Changeset 1903 in ProjectBuilder


Ignore:
Timestamp:
Oct 8, 2014, 1:35:10 PM (10 years ago)
Author:
Bruno Cornec
Message:
  • Add entries for osrepo for mageia/mandriva distros to allow setupve to work for these distributions without resorting to sbx2setupve
  • pbkeep now managed in pb_temp_init to avoid removing the temps dir when debugging
  • Add functions pb_ve_docker_get_image and pb_ve_get_type
  • lots of interfaces changes to allow support of docker, including script names. May break other non docker features
  • both newve and setupve work with docker now.
  • manages public keys for urpmi distriutions
  • Adds function pb_parallel_launchv. Now pb_launchv is single V
Location:
devel
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • devel/pb-modules/etc/pb.conf

    r1900 r1903  
    414414# When using docker some specific conf items
    415415dockerregistry default = http://localhost:5000/pb
     416#dockeropt default =
     417# Some options to pass to the docker command such as IP conf when firewalled --bip=172.17.42.1/16
     418# used in addition with shorewall with a masq file having "eth0 172.17.0.0/16"
    416419#dockerimage mageia-4-x86_64 = mageia-4-x86_64
    417420#dockertag pb = pb
     
    701704osrepo redhat-6.2 =
    702705osrepo rpm = ftp://ftp.project-builder.org/$pbos->{'name'}/$pbos->{'version'}/$pbos->{'arch'}/pb.repo
     706osrepo mageia = ftp://ftp.project-builder.org/$pbos->{'name'}/$pbos->{'version'}/$pbos->{'arch'}/pb.addmedia
     707osrepo mandriva = ftp://ftp.project-builder.org/$pbos->{'name'}/$pbos->{'version'}/$pbos->{'arch'}/pb.addmedia
    703708osrepo deb = ftp://ftp.project-builder.org/$pbos->{'name'}/$pbos->{'version'}/pb.sources.list
    704709
  • devel/pb-modules/etc/pb.conf.pod

    r1900 r1903  
    111111 Example: delivery mondorescue = prod
    112112
     113=item B<dockeropt>
     114
     115 Nature: Optional
     116 Key: project (as defined in the -p option or PBPROJ environment variable)
     117 Value: List of the options to call docker with
     118 Conffile: project
     119 Example: dockeropt default = --bip=172.16.42.1/16
     120
    113121=item B<dockerregistry>
    114122
  • devel/pb-modules/lib/ProjectBuilder/Base.pm

    r1896 r1903  
    425425
    426426sub pb_temp_init {
     427
     428my $pbkeep = shift || 0;    # Do not keep temp files by default
    427429
    428430if (not defined $ENV{'TMPDIR'}) {
     
    450452    pb_mkdir_p("$ENV{'TMPDIR'}/$template");
    451453} else {
    452     if ($pbdebug > 1) {
     454    if (($pbdebug > 1) || ($pbkeep == 1)) {
    453455        $ENV{'PBTMP'} = tempdir( "pb.XXXXXXXXXX", DIR => $ENV{'TMPDIR'});
    454456        pb_log(2,"DEBUG: Creating a non-volatile temporary directory ($ENV{'PBTMP'})\n");
  • devel/pb-modules/lib/ProjectBuilder/VE.pm

    r1902 r1903  
    1616use Carp 'confess';
    1717use English;
     18use File::Basename;
    1819use ProjectBuilder::Version;
    1920use ProjectBuilder::Base;
     
    3132 
    3233our @ISA = qw(Exporter);
    33 our @EXPORT = qw(pb_ve_launch pb_ve_snap);
     34our @EXPORT = qw(pb_ve_launch pb_ve_snap pb_ve_get_type pb_ve_docker_repo pb_ve_docker_get_image);
    3435
    3536($VERSION,$REVISION) = pb_version_init();
     
    6667sub pb_ve_launch {
    6768
    68 my $v = shift || undef;
    69 my $pbforce = shift || 0;       # By default do not rebuild VE
    70 my $locsnap = shift || 0;       # By default do not snap VE
    71 my $vetype = shift || undef;        # By default no image
    72 my $pbimage = shift || undef;       # By default no image
     69my $v = shift;
     70my $pbscript = shift;
     71my $pbforce = shift;   
     72my $pbstep= shift;          # Which step are we in (0: create, 1: setup, 2: build, 3: use)
     73my $locsnap = shift;
     74my $vetype = shift;
     75my $pbimage = shift;
    7376
    7477my $dockerregistry = undef;
    7578my $docrepo = undef;            # By default no repository for docker available
    7679
     80pb_log(2,"Entering pb_ve_launch at step $pbstep\n");
    7781# Get distro context
    7882my $pbos = pb_distro_get_context($v);
    7983
    80 # Get VE context
    81 if (not defined $vetype) {
    82     my ($ptr) = pb_conf_get("vetype");
    83     $vetype = $ptr->{$ENV{'PBPROJ'}};
    84 }
    85 confess "No vetype defined for $ENV{PBPROJ}" unless (defined $vetype);
    86 pb_log(1, "Using vetype $vetype for $ENV{PBPROJ}\n");
     84$vetype = pb_ve_get_type($vetype);
    8785my ($vepath) = pb_conf_get("vepath");
    8886
     
    114112    if ($EFFECTIVE_USER_ID != 0) {
    115113        $sudocmd ="sudo ";
    116     foreach my $proxy (qw/http_proxy ftp_proxy/) {
     114        foreach my $proxy (qw/http_proxy ftp_proxy/) {
    117115            if (defined $ENV{$proxy}) {
    118116                open(CMD,"sudo sh -c 'echo \$$proxy' |") or die "can't run sudo sh?: $!";
     
    132130    if (((((defined $verebuild) && ($verebuild->{$ENV{'PBPROJ'}} =~ /true/i)) || ($pbforce == 1)) && ($vetype ne "docker"))
    133131        # For docker we may have a reference image that we'll use
    134         || (((not defined $pbimage) || ($pbimage eq "")) && ($vetype eq "docker"))) {
     132        || (($vetype eq "docker") && ($pbstep == 0))) {
    135133
    136134        my ($verpmtype,$vedebtype) = pb_conf_get("verpmtype","vedebtype");
     
    275273        ((defined $vesnap->{$ENV{'PBPROJ'}}) && ($vesnap->{$ENV{'PBPROJ'}} =~ /true/i))) &&
    276274        ($locsnap eq 1) &&
     275        ($vetype ne "docker") &&
    277276        (! -d "$root/$pbos->{'name'}/$pbos->{'version'}/$pbos->{'arch'}")) {
    278277            my $cmd1 = pb_check_req("rm",0);
     
    281280            pb_system("$sudocmd $cmd1 -rf $root/$pbos->{'name'}/$pbos->{'version'}/$pbos->{'arch'} ; $sudocmd $cmd2 -p $root/$pbos->{'name'}/$pbos->{'version'}/$pbos->{'arch'} ; $sudocmd $cmd3 xz  -C $root/$pbos->{'name'}/$pbos->{'version'}/$pbos->{'arch'} -f $root/$pbos->{'name'}-$pbos->{'version'}-$pbos->{'arch'}.tar.gz","Extracting snapshot of $pbos->{'name'}-$pbos->{'version'}-$pbos->{'arch'}.tar.gz under $root/$pbos->{'name'}/$pbos->{'version'}/$pbos->{'arch'}");
    282281    }
    283    
    284     # Fix modes to allow access to the VE for pb user
    285     my $command = pb_check_req("chmod",0);
    286     pb_system("$sudocmd $command 755 $root/$pbos->{'name'} $root/$pbos->{'name'}/$pbos->{'version'} $root/$pbos->{'name'}/$pbos->{'version'}/$pbos->{'arch'}","Fixing permissions");
     282
     283    if ($vetype ne "docker") {
     284        # Fix modes to allow access to the VE for pb user
     285        my $command = pb_check_req("chmod",0);
     286        pb_system("$sudocmd $command 755 $root/$pbos->{'name'} $root/$pbos->{'name'}/$pbos->{'version'} $root/$pbos->{'name'}/$pbos->{'version'}/$pbos->{'arch'}","Fixing permissions");
     287    }
    287288
    288289    # If docker, create the image and remove the now temp dir except if we had one already
    289     if ($vetype eq "docker") {
    290         if ((not defined $pbimage) || ($pbimage eq "")) {
    291             # Snaphot the VE to serve as an input for docker
    292             pb_ve_snap($pbos,$root);
    293             # Create the docker image from the previous bootstrap
    294             my $cmd1 = pb_check_req("docker",0);
    295             $docrepo = pb_ve_docker_repo($dockerregistry->{$ENV{'PBPROJ'}});
    296             # Need sudo to be able to create all files correctly
    297             pb_system("$sudocmd $cmd1 import - $docrepo:$pbos->{'name'}-$pbos->{'version'}-$pbos->{'arch'} < $root/$pbos->{'name'}-$pbos->{'version'}-$pbos->{'arch'}.tar.gz");
    298             pb_system("$cmd1 push $docrepo:$pbos->{'name'}-$pbos->{'version'}-$pbos->{'arch'}");
    299         #} else {
    300         }
     290    if (($vetype eq "docker") && ($pbstep == 0)) {
     291        $docrepo = pb_ve_docker_repo($dockerregistry->{$ENV{'PBPROJ'}});
     292        my $cmd1 = pb_check_req("docker",0);
     293        # step 0 : nothing at creation -> tag n-v-a (made below)
     294
     295        # Snaphot the VE to serve as an input for docker
     296        pb_ve_snap($pbos,$root);
     297        # Create the docker image from the previous bootstrap
     298        # Need sudo to be able to create all files correctly
     299        # TODO: check before that the image doesn't already exist in the docker registry
     300        my $pbimage = "$docrepo:$pbos->{'name'}-$pbos->{'version'}-$pbos->{'arch'}";
     301        pb_system("$sudocmd $cmd1 import - $pbimage < $root/$pbos->{'name'}-$pbos->{'version'}-$pbos->{'arch'}.tar.gz");
     302        pb_system("$cmd1 push $pbimage");
    301303    }
    302304
     
    376378}
    377379
     380sub pb_ve_docker_get_image {
     381
     382my $pbimage = shift || undef;
     383my $found = 0;
     384
     385die "Unable to handle an undef docker image" if (not defined $pbimage);
     386
     387# Check that this docker image exists
     388my $cmd1 = pb_check_req("docker",0);
     389open(CMD, "$cmd1 images |") || die "Unable to get docker image list";
     390my ($repo, $tag, $id, $dummy);
     391while (<CMD>) {
     392    ($repo, $tag, $id, $dummy) = split(/\s+/,$_,4);
     393    $found = $id if ("$repo:$tag" eq $pbimage);
     394}
     395close(CMD);
     396return($found);
     397}
     398sub pb_ve_get_type {
     399
     400my $vetype = shift || undef;
     401
     402# Get VE context
     403if (not defined $vetype) {
     404    my ($ptr) = pb_conf_get("vetype");
     405    $vetype = $ptr->{$ENV{'PBPROJ'}};
     406}
     407confess "No vetype defined for $ENV{PBPROJ}" unless (defined $vetype);
     408pb_log(1, "Using vetype $vetype for $ENV{PBPROJ}\n");
     409return($vetype);
     410}
     411
     412
    378413=head1 WEB SITES
    379414
  • devel/pb/bin/pb

    r1900 r1903  
    4141
    4242# Global variables
    43 $Global::pb_stop_on_error = 0;  # False by default
    44 $Global::pb_show_sudo = 0;      # False by default
    45 
    46 my %opts;                   # CLI Options
    47 my $action;                 # action to realize
    48 my $test = "FALSE";         # Not used
    49 my $pbforce = 0;            # Force VE/VM rebuild
    50 my $pbsnap = 0;             # Do not use snapshot mode for VM/VE by default
    51 my $pbkeep = 0;             # keep temporary directory at the end
    52 my $option = "";            # Not used
    53 my @pkgs;                   # list of packages
    54 my $pbtag;                  # Global Tag variable
    55 my $pbver;                  # Global Version variable
    56 my $pbscript;               # Name of the script
    57 my %pbver;                  # per package
    58 my %pbtag;                  # per package
    59 my $pbrev;                  # Global REVISION variable
    60 my $pbaccount;              # Login to use to connect to the VM/RM
    61 my $pbtarget = undef;       # Target os-ver-arch you want to build for
    62 my $pbport;                 # Port to use to connect to the VM/RM
    63 my $newver;                 # New version to create
    64 my $pbimage = undef;            # ISO image for the VM to create or docker image for the VE
    65 my $vetype = undef;         # Type of Virtual environment to deal with
     43$Global::pb_stop_on_error = 0;      # False by default
     44$Global::pb_show_sudo = 0;          # False by default
     45
     46my %opts;                           # CLI Options
     47my $action;                         # action to realize
     48my $test = "FALSE";                 # Not used
     49my $pbforce = 0;                    # Force VM stop
     50my $pbsnap = 0;                     # Do not use snapshot mode for VM/VE by default
     51my $pbkeep = 0;                     # keep temporary directory at the end
     52my $option = "";                    # Not used
     53my @pkgs;                           # list of packages
     54my $pbtag;                          # Global Tag variable
     55my $pbver;                          # Global Version variable
     56my %pbscript;                       # Name of the scripts per V
     57my $pbrev;                          # Global REVISION variable
     58my $pbaccount;                      # Login to use to connect to the VM/RM
     59my $pbtarget = undef;               # Target os-ver-arch you want to build for
     60my $pbport;                         # Port to use to connect to the VM/RM
     61my $newver;                         # New version to create
     62my $pbimage = undef;                # ISO image for the VM to create or docker image for the VE
     63my $vetype = undef;                 # Type of Virtual environment to deal with
    6664
    6765my @date = pb_get_date();
     
    740738}
    741739if (defined $opts{'s'}) {
    742     $pbscript = $opts{'s'};
     740    $pbscript{'default'} = $opts{'s'};
    743741}
    744742if (defined $opts{'a'}) {
    745743    $pbaccount = $opts{'a'};
    746     die "option -a requires a -s script option" if (not defined $pbscript);
     744    die "option -a requires a -s script option" if (not defined $pbscript{'default'});
    747745}
    748746if (defined $opts{'P'}) {
     
    870868    pb_build2v("vm","build");
    871869} elsif ($action =~ /^launchvm$/) {
    872     my $pm;
    873     my $all_ok = 1;
    874     if (defined $pbparallel) {
    875         $pm = new Parallel::ForkManager($pbparallel);
    876    
    877         # Set which port the VM/RM will use to communicate
    878         $pm->run_on_start(\&pb_set_port);
    879         $pm->run_on_finish(sub { my ($pid, $code, $id, $signal, $dump) = @_;
    880                             $all_ok = 0 unless (($code == 0) && ($signal == 0) && ($dump == 0));
    881                         });
    882     }
    883 
    884     my $counter = 0;
    885     foreach my $v (split(/,/,$ENV{'PBV'})) {
    886         $counter++;
    887         # Modulo 2 * pbparallel (to avoid synchronization problems)
    888         $counter = 1 if ((defined $pbparallel) && ($counter > 2 * $pbparallel));
    889         $pm->start($counter) and next if (defined $pbparallel);
    890 
    891         pb_launchv("vm",$v,0);
    892     }
    893     $pm->wait_all_children if (defined $pbparallel);
    894     die "Aborting, one or more of the children failed." if ((not $all_ok) && ($Global::pb_stop_on_error));
     870    pb_parallel_launchv(undef,"vm",undef,3);
    895871} elsif ($action =~ /^launchve$/) {
    896     pb_launchv("ve",$ENV{'PBV'},0);
     872    pb_parallel_launchv(undef,"ve",undef,3);
    897873} elsif ($action =~ /^script2vm$/) {
    898     pb_script2v($pbscript,"vm");
     874    die "action script2vm requires a -s script option" if (not defined $pbscript{'default'});
     875    pb_script2v(\%pbscript,"vm");
    899876} elsif ($action =~ /^script2ve$/) {
    900     pb_script2v($pbscript,"ve");
     877    die "action script2ve requires a -s script option" if (not defined $pbscript{'default'});
     878    pb_script2v(\%pbscript,"ve");
    901879} elsif ($action =~ /^script2rm$/) {
    902     pb_script2v($pbscript,"rm");
     880    die "action script2rm requires a -s script option" if (not defined $pbscript{'default'});
     881    pb_script2v(\%pbscript,"rm");
    903882} elsif ($action =~ /^newver$/) {
    904883    pb_newver();
    905884} elsif ($action =~ /^newve$/) {
    906     pb_launchv("ve",$ENV{'PBV'},1);
     885    pb_parallel_launchv(undef,"ve",undef,0);
    907886} elsif ($action =~ /^newvm$/) {
    908     pb_launchv("vm",$ENV{'PBV'},1);
     887    pb_parallel_launchv(undef,"vm",undef,0);
    909888    pb_log(0, "Please ensure that sshd is running in your VM by default\n");
    910889    pb_log(0, "and that it allows remote root login (PermitRootLogin yes in /etc/ssh/sshd_config)\n");
     
    978957    my ($ptr2) = pb_conf_get("vmcmd");
    979958    my $vmcmd = $ptr2->{$ENV{'PBPROJ'}};
    980     my ($vmexist,$vmmport) = pb_check_ps($vmcmd,$ENV{'PBV'});
    981     if (! $vmexist) {
     959    my ($vexist,$vmmport) = pb_check_ps($vmcmd,$ENV{'PBV'});
     960    if (! $vexist) {
    982961        pb_log(0,"No VM found for $ENV{'PBV'}\n");
    983962    } else {
    984         pb_log(0,"Found an existing VM for $ENV{'PBV'} PID: $vmexist - SSH port: $vmmport\n");
     963        pb_log(0,"Found an existing VM for $ENV{'PBV'} PID: $vexist - SSH port: $vmmport\n");
    985964    }
    986965} elsif ($action =~ /^sbx2webpkg$/) {
     
    19301909
    19311910    my $cmt = shift;
     1911    my $pbscript = shift || undef;
    19321912    my $v = shift || undef;
    1933     my $vmexist = shift || 0;           # 0 is FALSE
    1934     my $vmpid = shift || 0;             # 0 is FALSE
     1913    my $vexist = shift || 0;            # 0 is FALSE
     1914    my $vpid = shift || 0;              # 0 is FALSE
    19351915    my $snapme = shift || 0;            # 0 is FALSE
    1936 
    1937     pb_log(2,"DEBUG: pb_send2target($cmt,".Dumper($v).",$vmexist,$vmpid)\n");
     1916    my $pbstep = shift || 3;            # 3 is usage of container
     1917
     1918    pb_log(2,"DEBUG: pb_send2target($cmt,".Dumper($v).",$vexist,$vpid)\n");
    19381919    my $host = "sshhost";
    19391920    my $login = "sshlogin";
     
    20392020
    20402021    if ($cmt =~ /(V[EM]|RM)build/) {
    2041         $src="$src $ENV{'PBROOTDIR'}/$ENV{'PBPROJ'}.pb $ENV{'PBDESTDIR'}/$ENV{'PBPROJVER'}-$ENV{'PBPROJTAG'}.pb $ENV{'PBETC'} $ENV{'PBDESTDIR'}/pbrc $ENV{'PBDESTDIR'}/pbscript.$$";
     2022        $src="$src $ENV{'PBROOTDIR'}/$ENV{'PBPROJ'}.pb $ENV{'PBDESTDIR'}/$ENV{'PBPROJVER'}-$ENV{'PBPROJTAG'}.pb $ENV{'PBETC'} $ENV{'PBDESTDIR'}/pbrc $pbscript{$v}";
    20422023    } elsif ($cmt =~ /(V[EM]|RM)Script/) {
    2043         $src="$src $ENV{'PBDESTDIR'}/pbscript.$$";
     2024        $src="$src $pbscript{$v}";
    20442025    } elsif ($cmt =~ /(V[EM]|RM)test/) {
    2045         $src="$src $ENV{'PBROOTDIR'}/$ENV{'PBPROJ'}.pb $ENV{'PBDESTDIR'}/$ENV{'PBPROJVER'}-$ENV{'PBPROJTAG'}.pb $ENV{'PBETC'} $ENV{'PBDESTDIR'}/pbrc $ENV{'PBDESTDIR'}/pbscript.$$ $ENV{'PBDESTDIR'}/pbtest";
     2026        $src="$src $ENV{'PBROOTDIR'}/$ENV{'PBPROJ'}.pb $ENV{'PBDESTDIR'}/$ENV{'PBPROJVER'}-$ENV{'PBPROJTAG'}.pb $ENV{'PBETC'} $ENV{'PBDESTDIR'}/pbrc $pbscript{$v} $ENV{'PBDESTDIR'}/pbtest";
    20462027    } elsif (($cmt eq "Announce") || ($cmt eq "Web") || ($cmt eq "CPAN")) {
    20472028        $src="$src $ENV{'PBTMP'}/pbscript";
     
    20522033        chomp($src);
    20532034        close(KEEP);
    2054         $src = "$src $ENV{'PBBUILDDIR'}/pbscript.$$";
     2035        $src = "$src $pbscript{$v}";
    20552036    }
    20562037    if (($cmt eq "Sources") || ($cmt eq "Packages") || ($cmt eq "CPAN")) {
     
    21892170# Prepare a script to ease urpmi setup
    21902171cat > $ENV{'PBPROJ'}$repotag.addmedia << EOT
     2172#rpm --import $pbrepo->{$ENV{'PBPROJ'}}/$repodir/$ENV{'PBPROJ'}.pubkey
    21912173urpmi.addmedia $ENV{'PBPROJ'}$repotag $pbrepo->{$ENV{'PBPROJ'}}/$repodir with media_info/hdlist.cz
    21922174EOT
     
    22002182    genhdlist .
    22012183fi
     2184(cd media_info ; ln -sf ../$ENV{'PBPROJ'}.pubkey pubkey)
    22022185EOF
    22032186            }
     
    23672350    my $tpdir;
    23682351    my $tp;
     2352    my $docrepo;
     2353    my $context = "$ENV{'PBTMP'}";
     2354    my %tag;
    23692355    if ($cmt =~ /^VE/) {
    23702356        $tp = pb_path_expand($vepath->{$ENV{'PBPROJ'}});
    23712357        $tpdir = pb_path_expand("$tp/$pbos->{'name'}/$pbos->{'version'}/$pbos->{'arch'}");
    2372         if (not defined $vetype) {
    2373             my ($ptr) = pb_conf_get("vetype");
    2374             $vetype = $ptr->{$ENV{'PBPROJ'}};
    2375         }
     2358        $vetype = pb_ve_get_type($vetype);
    23762359        my $arch = pb_get_arch();
    23772360        if ($vetype eq "chroot") {
     
    23822365        } elsif ($vetype eq "docker") {
    23832366            # docker manages the storage so rely on it
    2384             $shcmdroot = "sudo /usr/bin/docker $tpdir ";
    2385             $shcmd = "$shcmdroot /bin/su - $mac -c ";
     2367            $shcmdroot = "";
     2368            my ($dockerregistry) = pb_conf_get("dockerregistry");
     2369            $docrepo = pb_ve_docker_repo($dockerregistry->{$ENV{'PBPROJ'}});
     2370            my $cmd1 = pb_check_req("docker",0);
     2371            #my ($dockeropt) =  pb_conf_get_if("dockeropt");
     2372            # pbimage is used for docker and is setup to the right name depending on the step:
     2373            # TODO: This is not coded yet
     2374            my $pbimage = undef;
     2375            # step 0 : nothing at creation -> tag n-v-a (made below)
     2376            # step 1 : n-v-a + setup -> tag n-v-a-pb
     2377            # step 2 : n-v-a-pb + build -> tag n-v-a-pb-pbproj
     2378            # step 3 : n-v-a-pb-pbproj at use
     2379            if ((not defined $pbimage) || ($pbimage eq "")) {
     2380                # If no image name given, create a naming convention
     2381                $tag{1} = "$docrepo:$pbos->{'name'}-$pbos->{'version'}-$pbos->{'arch'}";
     2382                $tag{2} = "$tag{1}-pb";
     2383                $tag{3} = "$tag{2}-$ENV{'PBPROJ'}";
     2384            } else {
     2385                # If we were given an image name, just use it
     2386                $tag{1} = $pbimage;
     2387                $tag{2} = $pbimage;
     2388                $tag{3} = $pbimage;
     2389            }
     2390            # If we do not create the image, then use the one we should have
     2391            my $found = pb_ve_docker_get_image($tag{$pbstep});
     2392            # Now we use that image to do what is needed
     2393            # use a dockerfile to ease the creation of next images
     2394            # the context is in an empty temp dir
     2395            my $tmpd = "$context/Dockerfile";
     2396            open(DOCKER, "> $tmpd") || die "Unable to create the docker file $tmpd";
     2397            print DOCKER "FROM $tag{$pbstep}\n";
     2398            print DOCKER "MAINTAINER project-builder.org aka pb\n";
     2399            if ($pbstep == 1) {
     2400                # setup done as root
     2401                print DOCKER "USER root\n";
     2402            } else {
     2403                print DOCKER "USER pb\n";
     2404            }
     2405            $shcmd = "$cmd1 build -t $tag{$pbstep+1} $context";
     2406            #$shcmd = "$cmd1 build $dockeropt->{$ENV{'PBPROJ'}} -t $tag{$pbstep+1} $context";
    23862407        }
    23872408        $shcmd = "setarch i386 $shcmd" if (($pbos->{'arch'} =~ /i[3456]86/) && ($arch eq 'x86_64'));
    23882409        $cpcmd = "sudo /bin/cp -r ";
    2389         # We need to get the home dir of the target account to deliver in the right place
    2390         open(PASS,"$tpdir/etc/passwd") || die "Unable to open $tpdir/etc/passwd: $!";
    23912410        my $homedir = "";
    2392         while (<PASS>) {
    2393             my ($c1,$c2,$c3,$c4,$c5,$c6,$c7) = split(/:/);
    2394             $homedir = $c6 if ($c1 =~ /^$mac$/);
    2395             pb_log(3,"Homedir: $homedir - account: $c6\n");
    2396         }
    2397         close(PASS);
     2411        if (($cmt =~ /VE/) && ($vetype ne "docker")) {
     2412            # We need to get the home dir of the target account to deliver in the right place
     2413            open(PASS,"$tpdir/etc/passwd") || die "Unable to open $tpdir/etc/passwd: $!";
     2414            while (<PASS>) {
     2415                my ($c1,$c2,$c3,$c4,$c5,$c6,$c7) = split(/:/);
     2416                $homedir = $c6 if ($c1 =~ /^$mac$/);
     2417                pb_log(3,"Homedir: $homedir - account: $c6\n");
     2418            }
     2419            close(PASS);
     2420        }
    23982421        $cptarget = "$tpdir/$homedir/$tdir";
     2422        if ($vetype eq "docker") {
     2423            $cptarget = "docker container $shcmd";
     2424        }
    23992425        if ($cmt eq "VEbuild") {
    24002426            $cp2target = "$tpdir/$homedir/$bdir";
     
    24362462    # Do not touch when just announcing
    24372463    if (($cmt ne "Announce") && ($cmt ne "CPAN")) {
    2438         pb_system("$shcmd \"mkdir -p $tdir ; cd $tdir ; echo \'for i in $basesrc; do if [ -f \$i ]; then rm -f \$i; fi; done\ ; $cmd' | bash -e\"","Preparing $tdir on $cptarget");
     2464        # For docker everything is done in the Dockerfile
     2465        if (($cmt =~ /^VE/) && ($vetype eq "docker")) {
     2466            print DOCKER "RUN mkdir -p $tdir\n";
     2467            print DOCKER "RUN cd $tdir ; for i in $basesrc; do if [ -f \$i ]; then rm -f \$i; fi; done\n";
     2468            print DOCKER "RUN $cmd\n" if ((defined $cmd) && ($cmd ne ""));
     2469        } else {
     2470            pb_system("$shcmd \"mkdir -p $tdir ; cd $tdir ; echo \'for i in $basesrc; do if [ -f \$i ]; then rm -f \$i; fi; done\ ; $cmd' | bash -e\"","Preparing $tdir on $cptarget");
     2471        }
    24392472    } else {
    24402473        $logres = "> ";
    24412474    }
    2442     pb_system("cd $ENV{'PBBUILDDIR'} ; $cpcmd $src $cptarget 2> /dev/null","$cmt delivery in $cptarget");
     2475    # For docker everything is done in the Dockerfile
     2476    if (($cmt =~ /^VE/) && ($vetype eq "docker")) {
     2477        foreach my $f (split(/ +/,$src)) {
     2478            copy("$f","$context");
     2479            print DOCKER "ADD ".basename($f)." $tdir/\n";
     2480        }
     2481    } else {
     2482        pb_system("cd $ENV{'PBBUILDDIR'} ; $cpcmd $src $cptarget 2> /dev/null","$cmt delivery in $cptarget");
     2483    }
    24432484
    24442485    # For VE we need to change the owner manually
    24452486    if ($cmt =~ /^VE/) {
    2446         my $sudomode = pb_distro_get_param($pbos,pb_conf_get("ossudoersmode"));
    2447         my $res = pb_system("$shcmdroot sed -i '/requiretty/d' /etc/sudoers","Removing potential requiretty in sudoers","quiet");
    2448         pb_system("$shcmdroot sed '/requiretty/d' /etc/sudoers > /tmp/sudoers.new ; mv /tmp/sudoers.new $tpdir/tmp/sudoers.new ; $shcmdroot mv /tmp/sudoers.new /etc/sudoers ; $shcmdroot chown root:root /etc/sudoers ; $shcmdroot chmod $sudomode /etc/sudoers","Removing again potential requiretty in sudoers as sed -i failed") if (($res ne 0) && (-f "$tpdir/etc/sudoers"));
    2449         pb_system("$shcmd \"sudo chown -R $mac $tdir\"","Adapt owner in $tdir to $mac");
     2487        if ($vetype ne "docker") {
     2488            my $sudomode = pb_distro_get_param($pbos,pb_conf_get("ossudoersmode"));
     2489            my $res = pb_system("$shcmdroot sed -i '/requiretty/d' /etc/sudoers","Removing potential requiretty in sudoers","quiet");
     2490            pb_system("$shcmdroot sed '/requiretty/d' /etc/sudoers > /tmp/sudoers.new ; mv /tmp/sudoers.new $tpdir/tmp/sudoers.new ; $shcmdroot mv /tmp/sudoers.new /etc/sudoers ; $shcmdroot chown root:root /etc/sudoers ; $shcmdroot chmod $sudomode /etc/sudoers","Removing again potential requiretty in sudoers as sed -i failed") if (($res ne 0) && (-f "$tpdir/etc/sudoers"));
     2491            pb_system("$shcmd \"sudo chown -R $mac $tdir\"","Adapt owner in $tdir to $mac");
     2492        }
    24502493    }
    24512494
    24522495    # Use the right script name depending on context
    2453     my $pbscript;
     2496    my $pbscript1;
    24542497    if (($cmt =~ /^(V[EM]|RM)/) || ($cmt =~ /Packages/)){
    2455         $pbscript = "pbscript.$$";
     2498        $pbscript1 = "$pbscript{$v}";
    24562499    } else {
    2457         $pbscript = "pbscript";
     2500        $pbscript1 = "pbscript";
    24582501    }
    24592502
     
    24612504    my $shcmdbase = $shcmd;
    24622505    if ($cmt ne "CPAN") {
    2463         $shcmd .= " \"echo \'cd $tdir ; if [ -x $pbscript ]; then ./$pbscript; fi ; rm -f ./$pbscript\' | bash\"";
     2506        if (($cmt =~ /^VE/) && ($vetype eq "docker")) {
     2507            copy("$pbscript1","$context");
     2508            my $s = basename($pbscript1);
     2509            print DOCKER "ADD $s $tdir/\n";
     2510            # sleep to avoid text file busy errors
     2511            print DOCKER "RUN sleep 2\n";
     2512            print DOCKER "RUN cd $tdir ; chmod 755 $s ; ./$s ; if [ '$pbkeep' == '0' ]; then rm -f $s; fi\n";
     2513            close(DOCKER);
     2514        } else {
     2515            $shcmd .= " \"echo \'cd $tdir ; if [ -x $pbscript1 ]; then ./$pbscript1; fi ;  if [ '$pbkeep' == '0' ]; then rm -f ./$pbscript1\'; fi | bash\"";
     2516        }
    24642517    }
    24652518    my $cmdverb = "verbose";
     
    24692522        $cmdverb = "verbose_\[$v\] ";
    24702523    }
     2524    # this is where we lanch the execution
    24712525    pb_system("$shcmd","Executing pbscript on $cptarget if needed",$cmdverb);
     2526
    24722527    if ($cmt =~ /^(V[EM]|RM)build/) {
    24732528        # Get back info on pkg produced, compute their name and get them from the VM/RM
     
    25092564            # We want to send them to the ssh account so overwrite what has been done before
    25102565            undef $pbaccount;
    2511             pb_log(2,"Before sending pkgs, vmexist: $vmexist, vmpid: $vmpid\n");
    2512             pb_send2target("Packages",$pbos->{'name'}."-".$pbos->{'version'}."-".$pbos->{'arch'},$vmexist,$vmpid);
     2566            pb_log(2,"Before sending pkgs, vexist: $vexist, vpid: $vpid\n");
     2567            pb_send2target("Packages","$ENV{'PBDESTDIR'}/pbscript.$$",$pbos->{'name'}."-".$pbos->{'version'}."-".$pbos->{'arch'},$vexist,$vpid);
    25132568            pb_rm_rf("$ENV{'PBBUILDDIR'}/$pbos->{'name'}/$pbos->{'version'}/$pbos->{'arch'}");
    25142569        }
    25152570    }
    2516     unlink("$ENV{'PBDESTDIR'}/pbscript.$$") if ((($cmt =~ /^(V[ME]|RM)/) || ($cmt =~ /Packages/)) && ($pbkeep eq 0));
    2517 
    2518     pb_log(2,"Before halt, vmexist: $vmexist, vmpid: $vmpid\n");
    2519     if ((! $vmexist) && ($cmt =~ /^VM/)) {
     2571    unlink("$pbscript{$v}") if ((($cmt =~ /^(V[ME]|RM)/) || ($cmt =~ /Packages/)) && ($pbkeep eq 0));
     2572
     2573    pb_log(2,"Before halt, vexist: $vexist, vpid: $vpid\n");
     2574    if ((! $vexist) && ($cmt =~ /^VM/)) {
    25202575        # If in setupvm then takes a snapshot just before halting
    25212576        if ($snapme != 0) {
     
    25502605            $hoption = "" ;
    25512606        }
    2552         pb_system("$shcmdbase \"sudo $hpath $hoption \"; $tm ; echo \'if [ -d /proc/$vmpid ]; then kill -9 $vmpid; fi \' | bash ; sleep 10","VM $v halt (pid $vmpid)");
     2607        pb_system("$shcmdbase \"sudo $hpath $hoption \"; $tm ; echo \'if [ -d /proc/$vpid ]; then kill -9 $vpid; fi \' | bash ; sleep 10","VM $v halt (pid $vpid)");
    25532608    }
    25542609    if (($cmt =~ /^VE/) && ($snapme != 0)) {
    25552610        pb_ve_snap($pbos,$tp);
     2611        if ($vetype eq "docker") {
     2612            foreach my $f (split(/ +/,$src)) {
     2613                unlink("$context/$f");
     2614            }
     2615        }
    25562616    }
    25572617}
    25582618
    25592619sub pb_script2v {
    2560     my $pbscript=shift;
    2561     my $vtype=shift;
    2562     my $pbforce=shift || 0; # Force stop of VM. Default not.
    2563     my $vm1=shift || undef; # Only that VM/VE/RM to treat. Default all.
    2564     my $snapme=shift || 0;  # Do we have to create a snapshot. Default not.
    2565     my $vm;
    2566     my $all;
    2567 
    2568     pb_log(2,"DEBUG: pb_script2v($pbscript,$vtype,$pbforce,".Dumper($vm1).",$snapme)\n");
    2569     # Prepare the script to be executed on the VM/VE/RM
    2570     # in $ENV{'PBDESTDIR'}/pbscript.$$
    2571     if ((defined $pbscript ) && ($pbscript ne "$ENV{'PBDESTDIR'}/pbscript.$$")) {
    2572         copy($pbscript,"$ENV{'PBDESTDIR'}/pbscript.$$") || die "Unable to create $ENV{'PBDESTDIR'}/pbscript.$$ from $pbscript";
    2573         chmod 0755,"$ENV{'PBDESTDIR'}/pbscript.$$";
    2574     }
    2575 
    2576     if (not defined $vm1) {
    2577         ($vm,$all) = pb_get2v($vtype);
     2620
     2621my $pbscript=shift;
     2622my $vtype=shift;
     2623my $pbstep=shift;   # Which step are we in (0: create, 1: setup, 2: build, 3: use)
     2624my $pbforce=shift || 0; # Force stop of VM. Default not.
     2625my $snapme=shift || 0;  # Do we have to create a snapshot. Default not.
     2626my $pbimage=shift || undef; # Which image to use to start the VM/VE
     2627my $vm;
     2628my $all;
     2629
     2630pb_log(2,"DEBUG: pb_script2v($vtype,$pbstep,$pbforce,$snapme)\n");
     2631pb_log(2,"DEBUG: pb_script2v(pbscript: ".Dumper($pbscript)."\n");
     2632pb_parallel_launchv($pbscript,$vtype,uc($vtype)."Script",$pbstep,$pbforce,$snapme,$pbsnap,$pbimage);
     2633}
     2634
     2635sub pb_parallel_launchv {
     2636
     2637my $pbscript=shift;
     2638my $vtype = shift;
     2639my $action = shift;             # It an action is defined then use send2target
     2640my $pbstep=shift;           # Which step are we in (0: create, 1: setup, 2: build 3: use)
     2641my $pbforce=shift || 0;         # Force stop of VM. Default not.
     2642my $snapme = shift || 0;        # By default do not snap a VM/VE/RM
     2643my $usesnap = shift || 1;       # By default study the usage of the snapshot feature of VM/VE/RM   
     2644my $pbimage=shift || undef;     # Which image to use to start the VM/VE
     2645my $vm;
     2646my $all;
     2647
     2648# Adapt // mode to memory size
     2649$pbparallel = pb_set_parallel($vtype);
     2650
     2651pb_log(2,"DEBUG: pb_parallel_launch(vtype,step,force,snapme,usesnap: $vtype,$pbstep,$pbforce,$snapme,$usesnap)\n");
     2652pb_log(2,"DEBUG: pb_parallel_launch(pbscript: ".Dumper($pbscript)."\n");
     2653
     2654my ($vexist,$vpid) = (undef,undef);
     2655my $pm;
     2656my $all_ok = 1;
     2657if (defined $pbparallel) {
     2658    $pm = new Parallel::ForkManager($pbparallel);
     2659
     2660    # Set which port the VM/RM will use to communicate
     2661    $pm->run_on_start(\&pb_set_port);
     2662    $pm->run_on_finish(sub { my ($pid, $code, $id, $signal, $dump) = @_;
     2663                            unless ($code == 0 && $signal == 0 && $dump == 0) {
     2664                                $all_ok = 0;
     2665                                pb_log(0,"ERROR: pid $pid failed\n");
     2666                            }
     2667                        });
     2668}
     2669
     2670# Determine on which V to work
     2671if (defined $ENV{'PBV'}) {
     2672    @$vm = split(/,/,$ENV{'PBV'});
     2673} else {
     2674    ($vm,$all) = pb_get2v($vtype);
     2675}
     2676
     2677my $counter = 0;
     2678foreach my $v (@$vm) {
     2679    $counter++;
     2680    # Modulo 2 * pbparallel (to avoid synchronization problems)
     2681    $counter = 1 if ((defined $pbparallel) && ($counter > 2 * $pbparallel));
     2682    $pm->start($counter) and next if (defined $pbparallel);
     2683   
     2684    # Launch a single operation
     2685    my $pbscript = $pbscript->{$v} if (defined $pbscript->{$v});
     2686    $pbscript = $pbscript->{'default'} if (not defined $pbscript);
     2687    die "No script defined so unable to launch it in $v" if (not defined $pbscript);
     2688    ($vexist,$vpid) = pb_launchv($pbscript,$vtype,$v,$action,$pbstep,$pbforce,$snapme,$pbsnap,$pbimage);
     2689   
     2690    # Skip that V if something went wrong
     2691    if (($vpid == 0) && ($vexist == 0)) {
     2692        $pm->finish if (defined $pbparallel);
     2693        next;
     2694    }
     2695    $pm->finish if (defined $pbparallel);
     2696}
     2697$pm->wait_all_children if (defined $pbparallel);
     2698die "Aborting, one or more of the children failed." if ((not $all_ok) && ($Global::pb_stop_on_error));
     2699}
     2700
     2701sub pb_launchv {
     2702
     2703my $pbscript=shift;
     2704my $vtype = shift;
     2705my $v = shift;                  # Only 1 VM/VE/RM treated here
     2706my $action = shift;             # It an action is defined then use send2target
     2707my $pbstep=shift;           # Which step are we in (0: create, 1: setup, 2: build 3: use)
     2708my $pbforce=shift || 0;         # Force stop of VM. Default not.
     2709my $snapme = shift || 0;        # By default do not snap a VM/VE/RM
     2710my $usesnap = shift || 1;       # By default study the usage of the snapshot feature of VM/VE/RM   
     2711my $pbimage=shift || undef;     # Which image to use to start the VM/VE
     2712my $vexist = undef;
     2713my $vpid = undef;
     2714
     2715# Check that v exists
     2716die "No VM/VE/RM defined, unable to launch" if (not defined $v);
     2717
     2718# If creation or snapshot creation mode, no snapshot usable
     2719if (($pbstep == 0) || ($snapme == 1)) {
     2720    $usesnap = 0;
     2721}
     2722
     2723pb_log(2,"DEBUG: pb_launchv(script,vtype,v,step,force,snapme,usesnap: $pbscript,$vtype,$v,$pbstep,$pbforce,$snapme,$usesnap)\n");
     2724pb_log(2,"DEBUG: pb_launchv(pbimage: $pbimage)\n") if (defined $pbimage);
     2725pb_log(2,"DEBUG: pb_launchv(action: $action)\n") if (defined $action);
     2726# Keep only the first VM in case many were given
     2727if ($v =~ /,/) {
     2728    pb_log(0,"WARNING: pruning to just the first of several VM/VE/RMs listed ($v)\n");
     2729    $v =~ s/,.*//;
     2730}
     2731
     2732my $pbos = pb_distro_get_context($v);
     2733
     2734pb_apply_conf_proxy($pbos);
     2735
     2736# Launch the VMs/VEs
     2737if ($vtype eq "vm") {
     2738    die "-i image parameter needed" if (((not defined $pbimage) || ($pbimage eq "")) && ($pbstep == 0));
     2739
     2740    my ($ptr,$ptr2,$vmpath,$vmport,$vms) = pb_conf_get("vmtype","vmcmd","vmpath","vmport","vmsize");
     2741    my ($vmopt,$vmmm,$vmtmout,$vmsnap,$vmbuildtm,$vmmonport) = pb_conf_get_if("vmopt","vmmem","vmtmout","vmsnap","vmbuildtm","vmmonport");
     2742    my $vmsize = pb_distro_get_param($pbos,$vms);
     2743
     2744    my $vmtype = $ptr->{$ENV{'PBPROJ'}};
     2745    my $vmcmd = $ptr2->{$ENV{'PBPROJ'}};
     2746
     2747    if (defined $opts{'g'}) {
     2748        if (($vmtype eq "kvm") || ($vmtype eq "qemu")) {
     2749            $ENV{'PBVMOPT'} = "--nographic";
     2750        }
     2751    }
     2752    if (not defined $ENV{'PBVMOPT'}) {
     2753        $ENV{'PBVMOPT'} = "";
     2754    }
     2755    # Save the current status for later restoration
     2756    $ENV{'PBOLDVMOPT'} = $ENV{'PBVMOPT'};
     2757    # Set a default timeout of 2 minutes
     2758    if (not defined $ENV{'PBVMTMOUT'}) {
     2759        $ENV{'PBVMTMOUT'} = "120";
     2760    }
     2761    if (defined $vmopt->{$v}) {
     2762        $ENV{'PBVMOPT'} .= " $vmopt->{$v}" if ($ENV{'PBVMOPT'} !~ / $vmopt->{$v}/);
     2763    } elsif (defined $vmopt->{$ENV{'PBPROJ'}}) {
     2764        $ENV{'PBVMOPT'} .= " $vmopt->{$ENV{'PBPROJ'}}" if ($ENV{'PBVMOPT'} !~ / $vmopt->{$ENV{'PBPROJ'}}/);
     2765    }
     2766
     2767    # How much memory to allocate for VMs
     2768    if (defined $vmmm) {
     2769        my $vmmem = pb_distro_get_param($pbos,$vmmm);
     2770        if (defined $vmmem) {
     2771            $ENV{'PBVMOPT'} .= " -m $vmmem";
     2772        }
     2773    }
     2774
     2775    # Are we allowed to use snapshot feature
     2776    if ($usesnap == 1) {
     2777        if ((defined $vmsnap->{$v}) && ($vmsnap->{$v} =~ /true/i)) {
     2778            $ENV{'PBVMOPT'} .= " -snapshot";
     2779        } elsif ((defined $vmsnap->{$ENV{'PBPROJ'}}) && ($vmsnap->{$ENV{'PBPROJ'}} =~ /true/i)) {
     2780            $ENV{'PBVMOPT'} .= " -snapshot";
     2781        } elsif ($pbsnap eq 1) {
     2782            $ENV{'PBVMOPT'} .= " -snapshot";
     2783        }
     2784    }
     2785    if ($snapme != 0) {
     2786        if (($vmtype eq "kvm") || ($vmtype eq "qemu")) {
     2787            # Configure the monitoring to automate the creation of the 'pb' snapshot
     2788            $ENV{'PBVMOPT'} .= " -serial mon:telnet::$vmmonport->{$ENV{'PBPROJ'}},server,nowait" if ((defined $vmmonport) && (defined $vmmonport->{$ENV{'PBPROJ'}}));
     2789            # In that case no snapshot call needed
     2790            $ENV{'PBVMOPT'} =~ s/ -snapshot//;
     2791        }
     2792    }
     2793    if (defined $vmtmout->{$v}) {
     2794        $ENV{'PBVMTMOUT'} = $vmtmout->{$v};
     2795    } elsif (defined $vmtmout->{$ENV{'PBPROJ'}}) {
     2796        $ENV{'PBVMTMOUT'} = $vmtmout->{$ENV{'PBPROJ'}};
     2797    }
     2798    my $nport = pb_get_port($vmport,$pbos,$vtype);
     2799
     2800    my $cmd;
     2801    my $vmm;        # has to be used for pb_check_ps
     2802    if (($vmtype eq "qemu") || ($vmtype eq "kvm")) {
     2803        $vmm = "$vmpath->{$ENV{'PBPROJ'}}/$v.qemu";
     2804        if (($pbstep == 0) || (defined $pbimage)) {
     2805            $ENV{'PBVMOPT'} .= " -cdrom $pbimage -boot d";
     2806        }
     2807        # Always redirect the network and always try to use a 'pb' snapshot
     2808        #$cmd = "$vmcmd $ENV{'PBVMOPT'} -net nic,model=virtio -net user,hostfwd=tcp::$nport:22 -loadvm pb $vmm"
     2809        #$cmd = "$vmcmd $ENV{'PBVMOPT'} -redir tcp:$nport:10.0.2.15:22 $vmm"
     2810        $cmd = "$vmcmd $ENV{'PBVMOPT'} -net nic -net user,hostfwd=tcp:127.0.0.1:$nport-:22 $vmm"
     2811    } elsif ($vmtype eq "xen") {
     2812    } elsif ($vmtype eq "vmware") {
    25782813    } else {
    2579         @$vm = ($vm1);
    2580     }
    2581     my ($vmexist,$vmpid) = (undef,undef);
    2582 
    2583     foreach my $v (@$vm) {
    2584         # Launch VM/VE
    2585         ($vmexist,$vmpid) = pb_launchv($vtype,$v,0,$snapme,$pbsnap);
    2586 
    2587         if ($vtype eq "vm") {
    2588             pb_log(2,"DEBUG: After pb_launchv, vmexist: $vmexist, vmpid: $vmpid\n");
    2589 
    2590             # Skip that VM/RM if something went wrong
    2591             next if (($vmpid == 0) && ($vmexist == 0));
    2592 
    2593             # If force stopping the VM then reset vmexist
    2594             if ($pbforce == 1) {
    2595                 $vmpid = $vmexist;
    2596                 $vmexist = 0;
    2597             }
     2814        die "VM of type $vmtype not supported. Report to the dev team";
     2815    }
     2816    # Restore the ENV VAR Value
     2817    $ENV{'PBVMOPT'} = $ENV{'PBOLDVMOPT'};
     2818
     2819    my ($tmpcmd,$void) = split(/ +/,$cmd);
     2820    my $vmmport = undef;
     2821    ($vexist,$vmmport) = pb_check_ps($tmpcmd,$vmm);
     2822    my $vpid = 0;
     2823    if (! $vexist) {
     2824        if ($pbstep == 0) {
     2825            die("Found an existing Virtual machine $vmm. Won't overwrite") if (-r $vmm);
     2826            if (($vmtype eq "qemu") || ($vmtype eq "xen") || ($vmtype eq "kvm")) {
     2827                my $command = pb_check_req("qemu-img",0);
     2828                pb_system("$command create -f qcow2 $vmm $vmsize","Creating the QEMU VM");
     2829            } elsif ($vmtype eq "vmware") {
     2830            } else {
     2831            }
     2832        }
     2833        if (! -f "$vmm") {
     2834            pb_log(0,"Unable to find VM $vmm\n");
    25982835        } else {
    2599             #VE
    2600             $vmexist = 0;
    2601             $vmpid = 0;
    2602         }
    2603 
    2604         # Gather all required files to send them to the VM/VE/RM
    2605         # and launch the build through pbscript
    2606         pb_log(2,"DEBUG: Before send2target, vmexist: $vmexist, vmpid: $vmpid\n");
    2607         pb_send2target(uc($vtype)."Script","$v",$vmexist,$vmpid,$snapme);
    2608 
    2609     }
    2610 }
    2611 
    2612 sub pb_launchv {
    2613     my $vtype = shift;
    2614     my $v = shift;
    2615     my $create = shift || 0;        # By default do not create a VM/VE/RM
    2616     my $snapme = shift || 0;        # By default do not snap a VM/VE/RM
    2617     my $usesnap = shift || 1;       # By default study the usage of the snapshot feature of VM/VE/RM   
    2618 
    2619     # If creation or snapshot creation mode, no snapshot usable
    2620     if (($create == 1) || ($snapme == 1)) {
    2621         $usesnap = 0;
    2622     }
    2623 
    2624     pb_log(2,"DEBUG: pb_launchv($vtype,$v,$create,$snapme,$usesnap)\n");
    2625     die "No VM/VE/RM defined, unable to launch" if (not defined $v);
    2626     # Keep only the first VM in case many were given
    2627     if ($v =~ /,/) {
    2628         pb_log(0,"WARNING: pruning to just the first of several VM/VE/RMs listed ($v)\n");
    2629         $v =~ s/,.*//;
    2630     }
    2631 
     2836            # Is the SSH port free? if not kill the existing process using it after a build timeout period
     2837            my ($vmssh,$void) = pb_check_ps($tmpcmd,$vmm);
     2838            if ($vmssh) {
     2839                my $buildtm = $ENV{'PBVMTMOUT'};
     2840                if (defined $vmbuildtm->{$v}) {
     2841                    $buildtm = $vmbuildtm->{$v};
     2842                } elsif (defined $vmbuildtm->{$ENV{'PBPROJ'}}) {
     2843                    $buildtm = $vmbuildtm->{$ENV{'PBPROJ'}};
     2844                }
     2845
     2846                sleep $buildtm;
     2847                pb_log(0,"WARNING: Killing the process ($vmssh) using port $void (previous failed VM ?)\n");
     2848                kill 15,$vmssh;
     2849                # Let it time to exit
     2850                sleep 5;
     2851            }
     2852            pb_system("$cmd &","Launching the VM $vmm");
     2853            # Using system allows to kill it externaly if needed,sosupport that in the call
     2854            pb_system("sleep $ENV{'PBVMTMOUT'}","Waiting $ENV{'PBVMTMOUT'} s for VM $v to come up","mayfail");
     2855            ($vpid,$void) = pb_check_ps($tmpcmd,$vmm);
     2856            pb_log(0,"VM $vmm launched (pid $vpid)\n");
     2857        }
     2858    } else {
     2859        pb_log(0,"Found an existing VM $vmm (pid $vexist)\n");
     2860        # Set the correct port here based on what is done
     2861        $pbport = $vmmport;
     2862        $vpid = $vexist;
     2863    }
     2864    pb_log(2,"DEBUG: pb_launchv returns ($vexist,$vpid)\n");
     2865   
     2866    # Skip that VM/RM if something went wrong
     2867    return($vexist,$vpid) if (($vpid == 0) && ($vexist == 0));
     2868   
     2869    # If force stopping the VM then reset vexist
     2870    if ($pbforce == 1) {
     2871        $vpid = $vexist;
     2872        $vexist = 0;
     2873    }
     2874} elsif ($vtype eq "ve") {
     2875    # Force the creation of the VE and no snapshot usable
     2876    $vetype = pb_ve_get_type($vetype);
     2877    pb_ve_launch($v,$pbscript,$pbforce,$pbstep,$usesnap,$vetype,$pbimage);
     2878    $vexist = 0;
     2879    $vpid = 0;
     2880} else {
     2881    # RM here
     2882    # Get distro context
    26322883    my $pbos = pb_distro_get_context($v);
    2633    
    2634     pb_apply_conf_proxy($pbos);
    2635 
    2636     # Launch the VMs/VEs
    2637     if ($vtype eq "vm") {
    2638         die "-i image parameter needed" if (((not defined $pbimage) || ($pbimage eq "")) && ($create != 0));
    2639 
    2640         my ($ptr,$ptr2,$vmpath,$vmport,$vms) = pb_conf_get("vmtype","vmcmd","vmpath","vmport","vmsize");
    2641         my ($vmopt,$vmmm,$vmtmout,$vmsnap,$vmbuildtm,$vmmonport) = pb_conf_get_if("vmopt","vmmem","vmtmout","vmsnap","vmbuildtm","vmmonport");
    2642         my $vmsize = pb_distro_get_param($pbos,$vms);
    2643 
    2644         my $vmtype = $ptr->{$ENV{'PBPROJ'}};
    2645         my $vmcmd = $ptr2->{$ENV{'PBPROJ'}};
    2646 
    2647         if (defined $opts{'g'}) {
    2648             if (($vmtype eq "kvm") || ($vmtype eq "qemu")) {
    2649                 $ENV{'PBVMOPT'} = "--nographic";
    2650             }
    2651         }
    2652         if (not defined $ENV{'PBVMOPT'}) {
    2653             $ENV{'PBVMOPT'} = "";
    2654         }
    2655         # Save the current status for later restoration
    2656         $ENV{'PBOLDVMOPT'} = $ENV{'PBVMOPT'};
    2657         # Set a default timeout of 2 minutes
    2658         if (not defined $ENV{'PBVMTMOUT'}) {
    2659             $ENV{'PBVMTMOUT'} = "120";
    2660         }
    2661         if (defined $vmopt->{$v}) {
    2662             $ENV{'PBVMOPT'} .= " $vmopt->{$v}" if ($ENV{'PBVMOPT'} !~ / $vmopt->{$v}/);
    2663         } elsif (defined $vmopt->{$ENV{'PBPROJ'}}) {
    2664             $ENV{'PBVMOPT'} .= " $vmopt->{$ENV{'PBPROJ'}}" if ($ENV{'PBVMOPT'} !~ / $vmopt->{$ENV{'PBPROJ'}}/);
    2665         }
    2666 
    2667         # How much memory to allocate for VMs
    2668         if (defined $vmmm) {
    2669             my $vmmem = pb_distro_get_param($pbos,$vmmm);
    2670             if (defined $vmmem) {
    2671                 $ENV{'PBVMOPT'} .= " -m $vmmem";
    2672             }
    2673         }
    2674 
    2675         # Are we allowed to use snapshot feature
    2676         if ($usesnap == 1) {
    2677             if ((defined $vmsnap->{$v}) && ($vmsnap->{$v} =~ /true/i)) {
    2678                 $ENV{'PBVMOPT'} .= " -snapshot";
    2679             } elsif ((defined $vmsnap->{$ENV{'PBPROJ'}}) && ($vmsnap->{$ENV{'PBPROJ'}} =~ /true/i)) {
    2680                 $ENV{'PBVMOPT'} .= " -snapshot";
    2681             } elsif ($pbsnap eq 1) {
    2682                 $ENV{'PBVMOPT'} .= " -snapshot";
    2683             }
    2684         }
    2685         if ($snapme != 0) {
    2686             if (($vmtype eq "kvm") || ($vmtype eq "qemu")) {
    2687                 # Configure the monitoring to automate the creation of the 'pb' snapshot
    2688                 $ENV{'PBVMOPT'} .= " -serial mon:telnet::$vmmonport->{$ENV{'PBPROJ'}},server,nowait" if ((defined $vmmonport) && (defined $vmmonport->{$ENV{'PBPROJ'}}));
    2689                 # In that case no snapshot call needed
    2690                 $ENV{'PBVMOPT'} =~ s/ -snapshot//;
    2691             }
    2692         }
    2693         if (defined $vmtmout->{$v}) {
    2694             $ENV{'PBVMTMOUT'} = $vmtmout->{$v};
    2695         } elsif (defined $vmtmout->{$ENV{'PBPROJ'}}) {
    2696             $ENV{'PBVMTMOUT'} = $vmtmout->{$ENV{'PBPROJ'}};
    2697         }
    2698         my $nport = pb_get_port($vmport,$pbos,$vtype);
    2699    
    2700         my $cmd;
    2701         my $vmm;        # has to be used for pb_check_ps
    2702         if (($vmtype eq "qemu") || ($vmtype eq "kvm")) {
    2703             $vmm = "$vmpath->{$ENV{'PBPROJ'}}/$v.qemu";
    2704             if (($create != 0) || (defined $pbimage)) {
    2705                 $ENV{'PBVMOPT'} .= " -cdrom $pbimage -boot d";
    2706             }
    2707             # Always redirect the network and always try to use a 'pb' snapshot
    2708             #$cmd = "$vmcmd $ENV{'PBVMOPT'} -net nic,model=virtio -net user,hostfwd=tcp::$nport:22 -loadvm pb $vmm"
    2709             #$cmd = "$vmcmd $ENV{'PBVMOPT'} -redir tcp:$nport:10.0.2.15:22 $vmm"
    2710             $cmd = "$vmcmd $ENV{'PBVMOPT'} -net nic -net user,hostfwd=tcp:127.0.0.1:$nport-:22 $vmm"
    2711         } elsif ($vmtype eq "xen") {
    2712         } elsif ($vmtype eq "vmware") {
    2713         } else {
    2714             die "VM of type $vmtype not supported. Report to the dev team";
    2715         }
    2716         # Restore the ENV VAR Value
    2717         $ENV{'PBVMOPT'} = $ENV{'PBOLDVMOPT'};
    2718 
    2719         my ($tmpcmd,$void) = split(/ +/,$cmd);
    2720         my ($vmexist,$vmmport) = pb_check_ps($tmpcmd,$vmm);
    2721         my $vmpid = 0;
    2722         if (! $vmexist) {
    2723             if ($create != 0) {
    2724                 die("Found an existing Virtual machine $vmm. Won't overwrite") if (-r $vmm);
    2725                 if (($vmtype eq "qemu") || ($vmtype eq "xen") || ($vmtype eq "kvm")) {
    2726                     my $command = pb_check_req("qemu-img",0);
    2727                     pb_system("$command create -f qcow2 $vmm $vmsize","Creating the QEMU VM");
    2728                 } elsif ($vmtype eq "vmware") {
    2729                 } else {
    2730                 }
    2731             }
    2732             if (! -f "$vmm") {
    2733                 pb_log(0,"Unable to find VM $vmm\n");
    2734             } else {
    2735                 # Is the SSH port free? if not kill the existing process using it after a build timeout period
    2736                 my ($vmssh,$void) = pb_check_ps($tmpcmd,$vmm);
    2737                 if ($vmssh) {
    2738                     my $buildtm = $ENV{'PBVMTMOUT'};
    2739                     if (defined $vmbuildtm->{$v}) {
    2740                         $buildtm = $vmbuildtm->{$v};
    2741                     } elsif (defined $vmbuildtm->{$ENV{'PBPROJ'}}) {
    2742                         $buildtm = $vmbuildtm->{$ENV{'PBPROJ'}};
    2743                     }
    2744 
    2745                     sleep $buildtm;
    2746                     pb_log(0,"WARNING: Killing the process ($vmssh) using port $void (previous failed VM ?)\n");
    2747                     kill 15,$vmssh;
    2748                     # Let it time to exit
    2749                     sleep 5;
    2750                 }
    2751                 pb_system("$cmd &","Launching the VM $vmm");
    2752                 # Using system allows to kill it externaly if needed,sosupport that in the call
    2753                 pb_system("sleep $ENV{'PBVMTMOUT'}","Waiting $ENV{'PBVMTMOUT'} s for VM $v to come up","mayfail");
    2754                 ($vmpid,$void) = pb_check_ps($tmpcmd,$vmm);
    2755                 pb_log(0,"VM $vmm launched (pid $vmpid)\n");
    2756             }
    2757         } else {
    2758             pb_log(0,"Found an existing VM $vmm (pid $vmexist)\n");
    2759             # Set the correct port here based on what is done
    2760             $pbport = $vmmport;
    2761             $vmpid = $vmexist;
    2762         }
    2763         pb_log(2,"DEBUG: pb_launchv returns ($vmexist,$vmpid)\n");
    2764         return($vmexist,$vmpid);
    2765     } elsif ($vtype eq "ve") {
    2766         # Force the creation of the VE and no snapshot usable
    2767         pb_ve_launch($v,$create,$usesnap,$vetype,$pbimage);
    2768     } else {
    2769         # RM here
    2770         # Get distro context
    2771         my $pbos = pb_distro_get_context($v);
    2772 
    2773         # Get RM context
    2774         my ($ptr,$rmpath) = pb_conf_get("rmtype","rmpath");
    2775 
    2776         # Nothing more to do for RM. No real launch
    2777         # For the moment we support the RM is already running
    2778         # For ProLiant may be able to power them on if needed later on as an example.
    2779     }
     2884
     2885    # Get RM context
     2886    my ($ptr,$rmpath) = pb_conf_get("rmtype","rmpath");
     2887
     2888    # Nothing more to do for RM. No real launch
     2889    # For the moment we support the RM is already running
     2890    # For ProLiant may be able to power them on if needed later on as an example.
     2891    $vexist = 0;
     2892    $vpid = 0;
     2893}
     2894
     2895# Gather all required files to send them to the VM/VE/RM
     2896# and launch the build through pbscript
     2897if (defined $action) {
     2898    pb_log(2,"DEBUG: Before send2target, vexist: $vexist, vpid: $vpid, vtype: $vtype, v: $v, action: $action\n");
     2899    pb_send2target($action,$pbscript,$v,$vexist,$vpid,$snapme,$pbstep);
     2900}
     2901return($vexist,$vpid);
    27802902}
    27812903
     
    28212943my $vtype = shift;
    28222944my $action = shift || "build";
    2823 
     2945my $pbstep = 2;
     2946my $pbforce=shift || 0;         # Force stop of VM. Default not.
     2947
     2948# We prepare scripts for all V of this vtype to allow sharing that part
    28242949my ($v,$all) = pb_get2v($vtype);
    28252950
     
    28302955$pbparallel = pb_set_parallel($vtype);
    28312956
    2832 my ($vmexist,$vmpid) = (undef,undef);
     2957my ($vexist,$vpid) = (undef,undef);
    28332958my $pm;
    28342959my $all_ok = 1;
     
    28542979    # Prepare the script to be executed on the VM/VE/RM
    28552980    # in $ENV{'PBDESTDIR'}/pbscript.$$
    2856     open(SCRIPT,"> $ENV{'PBDESTDIR'}/pbscript.$$") || die "Unable to create $ENV{'PBDESTDIR'}/pbscript.$$";
     2981    #
     2982    $pbscript{$v} = "$ENV{'PBDESTDIR'}/pbscript.$$";
     2983    open(SCRIPT,"> $pbscript{$v}") || die "Unable to create $pbscript{$v}";
    28572984    print SCRIPT "#!/bin/bash\n";
    28582985
     
    28843011
    28853012    # VE needs a good /proc, tolerate one being potentially left around after a failure
    2886     if ($vtype eq "ve") {
     3013    if (($vtype eq "ve") && ($vetype ne "docker")) {
    28873014        print SCRIPT "[ -d /proc/1 ] || sudo /bin/mount -t proc /proc /proc\n";
    28883015    }
     
    29373064    print SCRIPT "pb $verbose -p $ENV{'PBPROJ'} $action"."2pkg $p\n";
    29383065
    2939     if ($vtype eq "ve") {
     3066    if (($vtype eq "ve") && ($vetype ne "docker")) {
    29403067        print SCRIPT "sudo /bin/umount /proc\n";
    29413068    }
     
    29543081    print SCRIPT "echo ==== End of script $$ for $vtype $v =====\n";
    29553082    close(SCRIPT);
    2956     chmod 0755,"$ENV{'PBDESTDIR'}/pbscript.$$";
     3083    chmod 0755,"$pbscript{$v}";
    29573084   
    2958     # Launch the VM/VE/RM
    2959     ($vmexist,$vmpid) = pb_launchv($vtype,$v,0);
    2960 
    2961 
    2962     if ($vtype eq "vm") {
    2963         # Skip that VM if something went wrong
    2964         if (($vmpid == 0) && ($vmexist == 0)) {
    2965             $pm->finish if (defined $pbparallel);
    2966             next;
    2967         }
    2968     } else {
    2969         # VE/RM
    2970         $vmexist = 0;
    2971         $vmpid = 0;
    2972     }
    2973     # Gather all required files to send them to the VM/VE
    2974     # and launch the build through pbscript
    2975     pb_log(2,"Calling send2target $vtype,$v,$vmexist,$vmpid\n");
    2976     pb_send2target(uc($vtype).$action,"$v",$vmexist,$vmpid);
    29773085    $pm->finish if (defined $pbparallel);
    29783086}
    29793087$pm->wait_all_children if (defined $pbparallel);
    29803088die "Aborting, one or more of the children failed." if ((not $all_ok) && ($Global::pb_stop_on_error));
     3089
     3090# Launch the VM/VE/RM
     3091# docker image to use if needed
     3092# Gather all required files to send them to the VM/VE
     3093# and launch the build through pbscript
     3094pb_parallel_launchv(\%pbscript,$vtype,uc($vtype).$action,$pbstep,$pbforce,0,1,$pbimage);
    29813095}
    29823096
     
    31453259my $vtype = shift;
    31463260my $sbx = shift || undef;
     3261my $pbstep = 1;
    31473262
    31483263my ($vm,$all) = pb_get2v($vtype);
    3149 
    3150 # Script generated
    3151 my $pbscript = "$ENV{'PBDESTDIR'}/setupv.$$";
    31523264
    31533265# Adapt // mode to memory size
     
    31683280my $counter = 0;
    31693281foreach my $v (@$vm) {
     3282    # Script generated - should be before the fork - after the %pbscript hash isn't updated correctly
     3283    $pbscript{$v} = "$ENV{'PBDESTDIR'}/setupv-$v";
     3284
    31703285    $counter++;
    31713286    # Modulo 2 * pbparallel (to avoid synchronization problems)
     
    31833298    my ($pbac) = pb_conf_get($vtype."login");
    31843299    my ($key,$zero0,$zero1,$zero2);
    3185     my ($vmexist,$vmpid);
     3300    my ($vexist,$vpid);
    31863301
    31873302    # Prepare the script to be executed on the VM/VE/RM
    31883303    # in $ENV{'PBDESTDIR'}/setupv
    3189     open(SCRIPT,"> $pbscript") || die "Unable to create $pbscript";
     3304
     3305    pb_log(2,"Preparing setup script for $v in $pbscript{$v}\n");
     3306    open(SCRIPT,"> $pbscript{$v}") || die "Unable to create $pbscript{$v}";
    31903307   
    31913308    print SCRIPT << 'EOF';
     
    32193336
    32203337EOF
    3221 
    3222     # Launch the VM/VE/RM - Usage of snapshot disabled
    3223     ($vmexist,$vmpid) = pb_launchv($vtype,$v,0,0,0);
    32243338
    32253339    my $keyfile;
     
    32363350   
    32373351        # Skip that VM/RM if something went wrong
    3238         next if (($vmpid == 0) && ($vmexist == 0));
     3352        next if (($vpid == 0) && ($vexist == 0));
    32393353   
    32403354        # Store the pub key part in a variable
     
    32493363        # once this is done, we can do what we need on the VM/RM remotely
    32503364    } elsif ($vtype eq "ve") {
    3251         print SCRIPT << "EOF";
     3365        if ($vetype ne "docker") {
     3366            print SCRIPT << "EOF";
    32523367# For VE we first need to mount some FS
    32533368pb_system("mount -t proc /proc /proc") unless (-d "/proc/$$");
    3254 
     3369EOF
     3370        }
     3371        print SCRIPT << "EOF";
    32553372# For VE we need a good null dev
    32563373# Except for RedHat 6.2 where it's good and doesnt like being recreated
     
    35653682pb_system("pbdistrocheck",undef,"verbose");
    35663683EOF
    3567     if ($vtype eq "ve") {
     3684    if ($vtype eq "ve"){
     3685        if ($vetype ne "docker") {
    35683686            print SCRIPT << 'EOF';
    35693687# For VE we need to umount some FS at the end
    35703688
    35713689pb_system("umount /proc","Unmounting /proc","mayfail");
    3572 
     3690EOF
     3691        }
     3692        print SCRIPT << 'EOF';
    35733693# Create a basic network file if not already there
    35743694
     
    36123732EOF
    36133733    close(SCRIPT);
    3614     chmod 0755,"$pbscript";
     3734    chmod 0755,"$pbscript{$v}";
    36153735
    36163736    # That build script needs to be run as root and force stop of VM at end
     
    36183738
    36193739    # Force shutdown of VM except if it was already launched
     3740    # TODO: FIX here as it doesn't work anymore
    36203741    my $pbforce = 0;
    3621     if ((! $vmexist) && ($vtype eq "vm")) {
     3742    if ((! $vexist) && ($vtype eq "vm")) {
    36223743        $pbforce = 1;
    36233744    }
    36243745   
    3625     pb_script2v($pbscript,$vtype,$pbforce,$v);
    36263746    $pm->finish if (defined $pbparallel);
    36273747}
    36283748$pm->wait_all_children if (defined $pbparallel);
    36293749die "Aborting, one or more of the children failed." if ((not $all_ok) && ($Global::pb_stop_on_error));
     3750
     3751# Launch the VM/VE/RM
     3752pb_log(2,"DEBUG: before parallel launch, pbscript hash is:".Dumper(%pbscript)."\n");
     3753pb_parallel_launchv(\%pbscript,$vtype,uc($vtype)."Script",$pbstep,$pbforce);
    36303754return;
    36313755}
     
    36353759
    36363760my $vtype = shift;
     3761my $pbstep = 3;
    36373762
    36383763my ($vm,$all) = pb_get2v($vtype);
     
    36673792    # Force shutdown of VM/VE
    36683793    # Force snapshot of VM/VE
    3669     pb_script2v($pbscript,$vtype,1,$v,1);
     3794    pb_script2v($pbscript,$vtype,$pbstep,1,$v,1);
    36703795}
    36713796return;
     
    36763801
    36773802my $vtype = shift;
     3803my $pbstep = 2;
    36783804
    36793805my ($vm,$all) = pb_get2v($vtype);
     
    36973823EOF
    36983824    # VE needs a good /proc
    3699     if ($vtype eq "ve") {
     3825    if (($vtype eq "ve") && ($vetype ne "docker")) {
    37003826        print SCRIPT "sudo /bin/mount -t proc /proc /proc\n";
    37013827    }
    37023828    print SCRIPT "$pbos->{'update'}\n";
    3703     if ($vtype eq "ve") {
     3829    if (($vtype eq "ve") && ($vetype ne "docker")) {
    37043830        print SCRIPT "sudo /bin/umount /proc\n";
    37053831    }
     
    37083834
    37093835    # Force shutdown of VM except
    3710     pb_script2v($pbscript,$vtype,1,$v);
     3836    pb_script2v($pbscript,$vtype,$pbstep,1,$v);
    37113837}
    37123838return;
     
    38543980    close(PBS);
    38553981    chmod 0755,"$ENV{'PBTMP'}/pbscript";
    3856     pb_send2target("Announce");
     3982    pb_send2target("Announce","$ENV{'PBTMP'}/pbscript");
    38573983
    38583984    my $sl = "Project $ENV{'PBPROJ'} version $ENV{'PBPROJVER'} is now available";
     
    42114337    my $vmcmd = shift;
    42124338    my $vmm = shift;
    4213     my $vmexist = 0;        # FALSE by default
     4339    my $vexist = 0;     # FALSE by default
    42144340    my $vmport = undef;         # NONE by default
    42154341
     
    42194345        next if (! /$vmm/);
    42204346        my $void1;
    4221         ($void1, $vmexist, $vmport) = split(/ +/,$_,3);
     4347        ($void1, $vexist, $vmport) = split(/ +/,$_,3);
    42224348        pb_log(2,"pb_check_ps $vmport\n");
    42234349        $vmport =~ s/.*=tcp:127\.0\.0\.1:([0-9]+)-:22.*/$1/;
    42244350        chomp($vmport);
    4225         pb_log(2,"pb_check_ps found a VM $vmexist using port $vmport\n");
     4351        pb_log(2,"pb_check_ps found a VM $vexist using port $vmport\n");
    42264352        last;
    42274353    }
    4228     return($vmexist,$vmport);
     4354    return($vexist,$vmport);
    42294355}
    42304356
Note: See TracChangeset for help on using the changeset viewer.