#!/usr/bin/env perl # # (c) Jan Gehring <jan.gehring@gmail.com> # use v5.12.5; use warnings; our $VERSION = '1.14.2.1'; # TRIAL VERSION BEGIN { # this is for new package format if ( -d '/usr/lib/rex/lib' ) { use lib '/usr/lib/rex/lib'; } } $|++; use LWP::UserAgent; use YAML; use Data::Dumper; use Rex::Config; use Rex::Logger; use Rex::Commands::Fs; use Rex::Commands::File; use JSON::MaybeXS; use Cwd qw(getcwd); use Carp; use URI; use URI::QueryParam; use File::Spec; use File::Basename; use Rex::Helper::Misc; use Rex::Helper::URI; use HTTP::Request; use HTTP::Request::Common; require Rex; $Rex::Logger::silent = 1; my ($major_minor) = ( $Rex::VERSION =~ m/^(\d*\.\d*)/ ); my $opts = {}; ###### # default server my $SEARCH_SERVER = "http://modules.rexify.org/api/$major_minor/get/recipes"; my $RECIPE_SERVER = "http://modules.rexify.org/api/$major_minor/get/mod/%s"; my $DEPEND_SERVER = "http://modules.rexify.org/api/$major_minor/get/dep/%s"; my $PERL_DEPEND_SERVER = "http://modules.rexify.org/api/$major_minor/get/perldep/%s"; my $AUTH_USER; my $AUTH_PASSWORD; my $AUTH_REALM; Rex::Config->register_config_handler( "module_server", sub { my ($param) = @_; if ( exists $param->{search} ) { $SEARCH_SERVER = $param->{search}; } if ( exists $param->{recipes} ) { $RECIPE_SERVER = $param->{recipes}; } if ( exists $param->{dependencies} ) { $DEPEND_SERVER = $param->{dependencies}; } if ( exists $param->{perl_dependencies} ) { $PERL_DEPEND_SERVER = $param->{perl_dependencies}; } if ( exists $param->{username} ) { $AUTH_USER = $param->{username}; } if ( exists $param->{password} ) { $AUTH_PASSWORD = $param->{password}; } if ( exists $param->{realm} ) { $AUTH_REALM = $param->{realm}; } } ); for ( my $i = 0 ; $i < @ARGV ; $i++ ) { if ( $ARGV[$i] =~ m/^\-\-([a-z0-9\-_]+)=/ ) { my $key = $1; my ( $c_key, $val ) = split( /=/, $ARGV[$i], 2 ); if ( exists $opts->{$key} ) { $opts->{$key} = [ $opts->{$key} ] if ( !ref $opts->{$key} ); push( @{ $opts->{$key} }, $val ); } else { $opts->{$key} = $val || 0; } } elsif ( $ARGV[$i] =~ m/^\-\-([a-z0-9\-_]+)/ ) { my $key = $1; if ( !$ARGV[ $i + 1 ] || $ARGV[ $i + 1 ] =~ m/^\-\-/ ) { $opts->{$key} = 1; } else { if ( exists $opts->{$key} ) { $opts->{$key} = [ $opts->{$key} ] if ( !ref $opts->{$key} ); push( @{ $opts->{$key} }, $ARGV[ ++$i ] ); } else { $opts->{$key} = $ARGV[ ++$i ]; } } } } if ( !$ARGV[0] || join( ",", @ARGV ) =~ m/\-h,|\-\-help,/ || $ARGV[0] eq "--help" ) { print STDERR "Usage: rexify [<project-name> [<directory>]] [<options>]\n"; print STDERR "\n"; print STDERR "Options:"; print STDERR "\n"; print STDERR "\t--search=value\t\t\tWill search community recipes\n"; print STDERR "\t--use=recipe\t\t\tWill download community recipe\n"; print STDERR "\t--init=git-url\t\t\tWill download and initialize the given repo\n"; print STDERR "\t--update-from-git\t\tUpdate the cloned repository.\n"; print STDERR "\t--template=template\t\tUse a custom template to create the Rexfile skeleton\n"; print STDERR "\t--create-module\t\t\tCreate a module skeleton.\n"; print STDERR "\t--create-module=Mod::Name\tCreate a module skeleton inside a Rex project.\n"; print STDERR "\t--sudo\t\t\t\tTo use sudo for Perl Module installation.\n"; print STDERR "\t--resolve-deps\t\t\tRead meta.yml and try to resolve project dependencies\n"; print STDERR "\t--no-install-perl-deps\t\tUse this if you don't want that rexify tries to install Perl Modules.\n"; print STDERR "\n"; print STDERR "Custom Templates:\n"; print STDERR " box - Template to use for Rex::Commands::Box projects.\n"; print STDERR "\n"; print STDERR "Application Bundle Commands:\n"; print STDERR "\n"; print STDERR "\t--bundle\tCreate an all-containing redistributable binary application.\n"; print STDERR "\t--task=taskname\tWhich task should be executed. default: setup\n"; print STDERR "\n"; print STDERR "Rex-JobControl Commands:\n"; print STDERR "\n"; print STDERR "\t--upload\tUpload Rexfile to JobControl server.\n"; print STDERR "\t\t--project=<project>\t\tProject where the Rexfile should be registered.\n"; print STDERR "\t\t--name=<name>\t\t\tThe name for the Rexfile.\n"; print STDERR "\t\t--description='<description>'\tA small description for the Rexfile.\n"; print STDERR "\n"; print STDERR "\t--execute\tExecute Job on JobControl server.\n"; print STDERR "\t\t--project=<project>\t\tProject where the Rexfile should be registered.\n"; print STDERR "\t\t--job=<job>\t\t\tThe name of the job to execute.\n"; print STDERR "\t\t--hosts='<server list>'\tA comma seperated list of servers.\n"; print STDERR "\n"; print STDERR "General options:"; print STDERR "\n"; print STDERR "\t--server=<server>\t\tThe URL to JobControl server.\n"; print STDERR "\t--username=<username>\t\tThe username to use for login to JobControl server.\n"; print STDERR "\t--password=<password>\t\tThe password for the user.\n"; print STDERR "\n"; exit 1; } sub print_found { my ( $name, $data ) = @_; $name =~ s/\//::/g; $name =~ s/\.pm$//; print "* $name\n"; print " Author : " . $data->{Author} . "\n"; print " Requires : " . join( ", ", @{ $data->{Requires} } ) . "\n" if ( $data->{Requires} ); print " License : " . $data->{License} . "\n" if ( $data->{License} ); print " Description: " . $data->{Description} . "\n"; } sub update_from_git { system "git pull origin"; resolve_deps("meta.yml"); } sub download_recipe_git { my ($url) = @_; $Rex::Logger::silent = 0; my $u = URI->new($url); my $branch = $u->query_param("branch"); my @splitted_path = split /\//, $u->path; $splitted_path[-1] =~ s/\.git$//; $branch ||= "master"; my $path = File::Spec->catdir( File::Spec->rel2abs( File::Spec->curdir() ), $splitted_path[-1] ); my $parent_path = dirname $path; mkdir $parent_path; my $clone_url = $u->scheme . '://' . $u->host . $u->path; Rex::Logger::info("Cloning $clone_url to $path. Using branch: $branch."); system "git", "clone", $url, "-b", $branch, $path; chdir $path; resolve_deps("meta.yml"); } sub download_recipe_local_tar_gz { my ($url) = @_; $Rex::Logger::silent = 0; system "tar", "xzf", $url; my $path = basename($url); $path =~ s/\.tar\.gz$//; chdir $path; resolve_deps("meta.yml"); } # upload rexfile to rex-jobcontrol (the complete directory) sub upload_rexfile { my (%option) = @_; my $tmp_dir = File::Spec->tmpdir; my $tmp_file = File::Spec->catfile( $tmp_dir, basename(getcwd) . ".tar.gz" ); my $login_url = $option{server} . "/login"; my $upload_url = $option{server} . "/project/" . Rex::Helper::URI::encode( $option{project} ) . "/rexfile/new"; Rex::Logger::info("Creating tar.gz file out of this directory."); my $dir = basename( getcwd() ); system "cd .. ; tar czf $tmp_file $dir"; # upload the file my $ua = LWP::UserAgent->new( cookie_jar => {} ); #my $request = HTTP::Request->new(POST 'http://example.com', Content_Type => 'multipart/form-data', Content => [file_0 => ['options2.txt']]); # first login my $res = $ua->post( $login_url, { username => $option{username}, password => $option{password} } ); if ( $res->code != 302 ) { print "Server not found or authentication wrong.\n"; exit 1; } my $up_request = POST( $upload_url, Content_Type => 'form-data', Content => [ rexfile_archive => [$tmp_file], rexfile_name => $option{name}, rexfile_description => $option{description} ] ); my $up_res = $ua->request($up_request); if ( $up_res->code != 302 ) { print "Upload of Rexfile failed.\n"; exit 1; } unlink $tmp_file; } # execute job on Job-Control server sub dispatch_execute_job { my (%option) = @_; my $login_url = $option{server} . "/login"; my $execute_url = $option{server} . "/project/" . Rex::Helper::URI::encode( $option{project} ) . "/job/" . Rex::Helper::URI::encode( $option{job} ) . "/execute"; # execute a job my $ua = LWP::UserAgent->new( cookie_jar => {} ); # first login my $res = $ua->post( $login_url, { username => $option{username}, password => $option{password} } ); if ( $res->code != 302 ) { print "Server not found or authentication wrong.\n"; exit 1; } # then send the execute command my $ex_request = POST( $execute_url, Content => [ sel_server => [ split( /[ ,]/, $option{hosts} ) ], ] ); my $ex_res = $ua->request($ex_request); if ( $ex_res->code != 302 ) { print "Execute of job failed.\n"; exit 1; } } sub download_recipe { my ($name) = @_; if ( $name =~ m/^(ssh|https|git):\/\// ) { # seems to be a git link download_recipe_git($name); return; } if ( !-f "Rexfile" ) { print STDERR "This is not a Rex project directory. There is no Rexfile.\n"; exit 1; } if ( !-d "lib" ) { mkdir "lib"; } print "Getting dependencies for $name...\n"; my $deps = decode_json( get( sprintf( $DEPEND_SERVER, $name ) ) ); if ( scalar( @{$deps} ) > 0 ) { print " Found: \n - " . join( "\n - ", @{$deps} ) . "\n"; for my $dep ( @{$deps} ) { download_recipe($dep); } } else { print " None found.\n"; } if ( !exists $opts->{"no-install-perl-deps"} ) { print "Getting perl dependencies for $name...\n"; my $perl_deps = decode_json( get( sprintf( $PERL_DEPEND_SERVER, $name ) ) ); if ( scalar( @{$perl_deps} ) > 0 ) { print " Found: \n - " . join( "\n - ", @{$perl_deps} ) . "\n"; for my $dep ( @{$perl_deps} ) { install_perl_module($dep); } } else { print " None found.\n"; } } print "Downloading $name... "; $name =~ s/::/\//g; my $content = get( sprintf( $RECIPE_SERVER, $name ) ); open( my $fh, ">", "tmp-mod.tar.gz" ) or die($!); binmode $fh; print $fh $content; close($fh); chdir("lib"); system "tar", "xzf", "../tmp-mod.tar.gz"; chdir(".."); unlink("tmp-mod.tar.gz"); print "done.\n"; } sub resolve_deps { my ($file) = @_; $Rex::Logger::silent = 0; $file ||= "meta.yml"; if ( !-f $file ) { return; } my $ref = YAML::LoadFile($file); my ( %deps, %perl_deps ); if ( exists $ref->{Require} ) { if ( ref $ref->{Require} eq "ARRAY" ) { do { ref $_ eq "HASH" ? $deps{ $_->{name} } = $_ : $deps{$_} = $_ } for @{ $ref->{Require} }; } else { %deps = %{ $ref->{Require} }; } } if ( exists $ref->{PerlRequire} ) { if ( ref $ref->{PerlRequire} eq "ARRAY" ) { do { ref $_ eq "HASH" ? $perl_deps{ $_->{name} } = $_ : $perl_deps{$_} = $_; } for @{ $ref->{PerlRequire} }; } else { %perl_deps = %{ $ref->{PerlRequire} }; } } Rex::Logger::debug("Found dependencies: "); Rex::Logger::debug( Dumper( \%deps ) ); for my $req ( keys %deps ) { if ( ref $deps{$req} ) { if ( exists $deps{$req}->{git} ) { # git dep my $branch = "master"; if ( exists $deps{$req}->{branch} ) { $branch = $deps{$req}->{branch}; } my @path_parts = split /::/, $req; my $path = File::Spec->catdir( File::Spec->rel2abs( File::Spec->curdir() ), "lib", @path_parts ); my $parent_path = dirname $path; if ( !-d $parent_path ) { mkdir $parent_path; } if ( -d $path && !-d "$path/.git" ) { Rex::Logger::info( "$req not under git control. Skipping.", "warn" ); next; } if ( -d "$path/.git" ) { rmdir $path; } Rex::Logger::info("Cloning $deps{$req}->{git}#$branch to $path"); system "git", "clone", $deps{$req}->{git}, "-b", $branch, $path; resolve_deps("$path/meta.yml"); } } else { download_recipe($req); } } Rex::Logger::debug("Found perl dependencies: "); Rex::Logger::debug( Dumper( \%perl_deps ) ); for my $req ( keys %perl_deps ) { if ( ref $perl_deps{$req} ) { if ( exists $perl_deps{$req}->{git} ) { # git dep my $branch = "master"; if ( exists $perl_deps{$req}->{branch} ) { $branch = $perl_deps{$req}->{branch}; } my $curdir = getcwd; my $path = File::Spec->catdir( File::Spec->tmpdir, "tmp-build-$$" ); my $lib_path = File::Spec->catdir( File::Spec->rel2abs( File::Spec->curdir() ), "lib", "perl" ); my $parent_path = dirname $lib_path; if ( !-d $parent_path ) { mkdir $parent_path; } Rex::Logger::info("Cloning $perl_deps{$req}->{git}#$branch to $path"); system "git", "clone", $perl_deps{$req}->{git}, "-b", $branch, $path; chdir $path; system "cpanm", "-l", $lib_path, "."; chdir $curdir; rmdir $path; } } else { # we need relative directories, because auf a cpanm bug on windows. my $lib_path = File::Spec->catdir( File::Spec->curdir(), "lib", "perl" ); eval "use $req"; if ($@) { system "cpanm", "-l", $lib_path, $req; } } } } sub install_perl_module { my ($mod) = @_; print "Checking $mod: "; eval "use $mod"; if ($@) { print "[failed]\n"; } else { print "[ok]\n"; return; } print "Trying to install $mod... "; my $cmd = "cpanm"; my $out = qx($cmd --help 2>&1); if ( $? != 0 ) { $cmd = "cpan"; $out = qx($cmd -h 2>&1); if ( $? != 0 ) { print "[failed]\n"; print "Can't find cpanm or cpan. Please install $mod manually.\n"; return; } } my $cpanm_opts = ""; if ( exists $opts->{sudo} ) { $cmd = "sudo $cmd"; } $out = qx($cmd $cpanm_opts $mod 2>&1); open( my $log, ">>", "rexify-install.log" ) or die($!); print $log $out; close($log); if ( $? != 0 ) { print "[failed]\n"; print "!! Please install $mod manually. See rexify-install.log for more details.\n"; } else { print "[ok]\n"; } } if ( exists $opts->{bundle} ) { # bundle everything to an application CORE::mkdir("rex.payload"); system "cp -vR * rex.payload/ 2>/dev/null"; system "rm -rf rex.payload/rex.payload"; my $rex = File::Spec->catfile( dirname($0), "rex" ); my $tmp_args_file = File::Spec->catfile( File::Spec->tmpdir(), "pp.args.$$.tmp" ); open( my $fh, ">", $tmp_args_file ) or die($!); print $fh template('@pp.args.tpl'); close($fh); # build the perl and application if ( -d '/usr/lib/rex/lib' ) { system "pp -I /usr/lib/rex/lib \@$tmp_args_file -o rex.payload/rex.bin $rex"; } else { system "pp -I /usr/lib/rex/lib \@$tmp_args_file -o rex.payload/rex.bin $rex"; } # find libssh2.so and libexpat.so my $arch = qx{uname -m}; chomp $arch; my $elf = "ELF32"; if ( $arch eq "x86_64" ) { $elf = "ELF64"; } CORE::mkdir( File::Spec->catdir( "rex.payload", "lib.bin" ) ); my @libexpat = qx{find /usr/lib /usr/lib32 /usr/lib64 /lib -name 'libexpat.so*' -type f 2>/dev/null}; my @libssh2 = qx{find /usr/lib /usr/lib32 /usr/lib64 /lib -name 'libssh2.so*' -type f 2>/dev/null}; chomp @libexpat; chomp @libssh2; for my $f ( @libexpat, @libssh2 ) { my $elf_class = qx{readelf -h $f | grep Class:}; # only copy architecture specific files # currently no multi arch support if ( $elf_class =~ m/$elf/ ) { system "cp -va $f " . File::Spec->catdir( "rex.payload", "lib.bin" ); } } system "cd rex.payload/lib.bin ; ln -s " . basename( $libssh2[0] ) . " libssh2.so.1"; system "cd rex.payload/lib.bin ; ln -s " . basename( $libexpat[0] ) . " libexpat.so.1"; # create wrapper script. open( my $fh2, ">", "rex.sh" ) or die($!); print $fh2 template( '@bundle.sh.tpl', task => 'setup' ); close($fh2); system "cd rex.payload ; tar czf ../rex.payload.tar.gz *"; system "cat rex.sh rex.payload.tar.gz >rex.selfextract.sbx ; chmod 755 rex.selfextract.sbx"; CORE::unlink($tmp_args_file); CORE::unlink("rex.sh"); system "rm -rf rex.payload"; CORE::unlink("rex.payload.tar.gz"); exit 0; } if ( exists $opts->{upload} && exists $opts->{server} && exists $opts->{username} && exists $opts->{password} && exists $opts->{project} && exists $opts->{name} ) { $opts->{description} ||= ""; upload_rexfile( %{$opts} ); exit 0; } if ( exists $opts->{execute} && exists $opts->{server} && exists $opts->{username} && exists $opts->{password} && exists $opts->{project} && exists $opts->{hosts} && exists $opts->{job} ) { dispatch_execute_job( %{$opts} ); exit 0; } if ( exists $opts->{search} ) { my $search_string = $opts->{search}; # only a search print "Downloading recipes.yml ... "; my $recipes = get($SEARCH_SERVER); print " done.\n"; print "Searching...\n\n"; my $data = Load($recipes); for my $mod ( keys %{$data} ) { if ( $mod =~ qr{$search_string}i ) { print_found( $mod, $data->{$mod} ); next; } if ( $data->{$mod}->{Description} =~ qr{$search_string}i ) { print_found( $mod, $data->{$mod} ); } } exit 0; } if ( exists $opts->{"update-from-git"} ) { update_from_git(); exit 0; } if ( exists $opts->{init} ) { if ( -f $opts->{init} ) { download_recipe_local_tar_gz( $opts->{init} ); } else { download_recipe_git( $opts->{init} ); } exit 0; } if ( exists $opts->{use} && $ARGV[0] =~ /^\-\-use/ ) { if ( $opts->{use} ) { if ( !ref $opts->{use} ) { $opts->{use} = [ $opts->{use} ]; } for my $use_mod ( @{ $opts->{use} } ) { download_recipe($use_mod); } } exit 0; } if ( exists $opts->{"resolve-deps"} && $opts->{"resolve-deps"} ) { resolve_deps("meta.yml"); exit 0; } if ( exists $opts->{"create-module"} ) { my $dir = $ARGV[0]; my $module_name = $dir; if ( $dir !~ m/^[a-zA-Z][a-zA-Z_:0-9]+$/ ) { print "USAGE: $0 Module::Name --create-module\n"; print " Allowed characters: a-z, A-Z, _, 0-9, '::'.\n"; exit 1; } if ( -f "Rexfile" && $opts->{"create-module"} ) { $dir = "lib/$dir"; } if ( $dir =~ m/\-\-create\-module/ ) { print "USAGE: $0 Module::Name --create-module\n"; print " Allowed characters: a-z, A-Z, _, 0-9, '::'.\n"; exit 1; } $dir =~ s/::/\//g; print "Creating module $module_name...\n"; print " mkdir $dir\n"; mkdir $dir, recursive => 1; chdir $dir; print " Creating template file: __module__.pm\n"; file "__module__.pm", content => template( '@module.pm.tpl', dir => $dir, module_name => $module_name ); print " Creating template file: meta.yml\n"; file "meta.yml", content => template( '@meta.yml.tpl', dir => $dir, module_name => $module_name ); print "\n"; print "Your module has been created in $dir.\n"; exit 0; } my $dir = $ARGV[0]; if ( defined $ARGV[1] && $ARGV[1] !~ m/^\-\-/ ) { $dir = $ARGV[1]; } if ( $dir !~ m/^[a-zA-Z][a-zA-Z_:0-9]+$/ ) { print "USAGE: $0 Project::Name\n"; print " Allowed characters: a-z, A-Z, 0-9, _, '::'.\n"; exit 1; } if ( exists $opts->{template} && -f $opts->{template} && $opts->{template} !~ m/^\// ) { my $cwd = getcwd; $opts->{template} = "$cwd/" . $opts->{template}; } elsif ( exists $opts->{template} && $opts->{template} !~ m/^\// ) { $opts->{template} = '@' . $opts->{template}; } unless ( -d $dir ) { print "Created $dir\n"; mkdir($dir); } print "chdir to $dir\n"; chdir($dir); unless ( -d 'lib' ) { mkdir('lib'); } unless ( -f 'lib' . $ARGV[0] . '.pm' ) { open( my $fh, ">", "lib/$ARGV[0].pm" ) or die($!); print $fh template( '@libfile', lib => $ARGV[0] ); close($fh); print STDERR "Created lib/Rex/$ARGV[0].pm\n"; if ( $opts->{template} ) { open( $fh, ">", "Rexfile" ) or die($!); print $fh template( $opts->{template}, lib => $ARGV[0] ); close($fh); } else { open( $fh, ">", "Rexfile" ) or die($!); print $fh template( '@rexfile', lib => $ARGV[0] ); close($fh); } if ( $opts->{use} ) { if ( !ref $opts->{use} ) { $opts->{use} = [ $opts->{use} ]; } for my $use_mod ( @{ $opts->{use} } ) { download_recipe($use_mod); } } print STDERR "Created Rexfile.\n"; print STDERR "Done.\n\nNow edit Rexfile to suit your needs.\n"; print STDERR "You can edit $dir/lib/$ARGV[0].pm to define tasks.\n"; print STDERR "\nIf you need assistance, get in touch via one of our support channels!\n"; } else { if ( $opts->{use} ) { if ( !ref $opts->{use} ) { $opts->{use} = [ $opts->{use} ]; } for my $use_mod ( @{ $opts->{use} } ) { download_recipe($use_mod); } } exit; } sub get { my ($url) = @_; my $ua = LWP::UserAgent->new; $ua->env_proxy; if ( $AUTH_USER && $AUTH_PASSWORD ) { my ($netloc) = ( $RECIPE_SERVER =~ m/^https?:\/\/([^\/]+)\// ); unless ( $netloc =~ m/\:\d+$/ ) { if ( $netloc =~ m/^https/ ) { $netloc .= ":443"; } else { $netloc .= ":80"; } } $ua->credentials( $netloc, $AUTH_REALM, $AUTH_USER, $AUTH_PASSWORD ); } my $resp = $ua->get($url); if ( $resp->is_success ) { return $resp->decoded_content; } } __DATA__ @rexfile # enable new Features use Rex -feature => 0.40; # set your username set user => "<user>"; # set your password set password => "<password>"; # enable password authentication set -passauth; # put your server in this group set group => "servers" => "server1", "server2"; # now load every module via ,,require'' require <%= $::lib %>; @end @libfile package <%= $::lib %>; use Rex -base; desc "Get uptime of server"; task "uptime", group => 'servers', sub { say run "uptime"; }; 1; @end @box use strict; use warnings; use Rex -feature => 0.40; use Rex::Commands::Box; set user => '<user>'; set password => '<password>'; set -passauth; # # CALL: # rex init --name=<%= $::lib %> --url=http://box.rexify.org/box/ubuntu-server-12.10-amd64.ova desc "Initialize and start the VM: rex init --name=vmname [--url=http://...]"; task "init", sub { my $param = shift; box { my ($box) = @_; $box->name($param->{name}); # where to download the base image $box->url($param->{url}); # default is nat #$box->network(1 => { # type => "bridged", # bridge => "eth0", #}); # only works with network type = nat # if a key ssh is present, rex will use this to log into the vm # you need this if you run VirtualBox not on your local host. $box->forward_port(ssh => [2222 => 22]); # share a folder from the host system #$box->share_folder("sharename" => "/path/on/host/system"); # define the authentication to the box # if you're downloading one from box.rexify.org this is the default. $box->auth( user => "root", password => "box", ); # if you want to provision the machine, # you can define the tasks to do that $box->setup(qw/install_webserver/); }; }; # # CALL: # rex down --name=<%= $::lib %> desc "Stop the VM: rex down --name=vmname"; task "down", sub { my $param = shift; my $box = Rex::Commands::Box->new(name => $param->{name}); $box->stop; }; task "install_webserver", sub { # update package db update_package_db; # install packages / customize the vm install "apache2"; }; require <%= $::lib %>; @end @module.pm.tpl package <%= $::module_name %>; use Rex -base; task example => sub { my $output = run "uptime"; say $output; }; 1; =pod =head1 NAME $::module_name - {{ SHORT DESCRIPTION }} =head1 DESCRIPTION {{ LONG DESCRIPTION }} =head1 USAGE {{ USAGE DESCRIPTION }} include qw/<%= $::module_name %>/; task yourtask => sub { <%= $::module_name %>::example(); }; =head1 TASKS =over 4 =item example This is an example Task. This task just output's the uptime of the system. =back =cut @end @meta.yml.tpl Name: <%= $::module_name %> Description: {{Â DESCRIPTION }} Author: {{ your name <your.name@email.com> }} License: {{Â THE LICENSE }} # Only if you have dependencies to other Rex Modules. Require: - Other::Rex::Module - 2nd::Rex::Module @end @pp.args.tpl -M Net::OpenSSH::ShellQuoter::POSIX -M Net::SFTP::Foreign::Backend::Unix -M Sys::Syslog -M Net::OpenSSH -M Net::SFTP::Foreign -M UNIVERSAL -M Rex::Virtualization -M Rex::User::Linux -M Rex::User::FreeBSD -M Rex::User::SunOS -M Rex::User::OpenWrt -M Rex::User::NetBSD -M Rex::User::OpenBSD -M Rex::Virtualization::VBox::delete -M Rex::Virtualization::VBox::list -M Rex::Virtualization::VBox::info -M Rex::Virtualization::VBox::shutdown -M Rex::Virtualization::VBox::reboot -M Rex::Virtualization::VBox::status -M Rex::Virtualization::VBox::import -M Rex::Virtualization::VBox::forward_port -M Rex::Virtualization::VBox::create -M Rex::Virtualization::VBox::bridge -M Rex::Virtualization::VBox::option -M Rex::Virtualization::VBox::start -M Rex::Virtualization::VBox::share_folder -M Rex::Virtualization::VBox::destroy -M Rex::Virtualization::VBox::guestinfo -M Rex::Virtualization::VBox -M Rex::Virtualization::LibVirt -M Rex::Virtualization::Docker -M Rex::Virtualization::LibVirt::delete -M Rex::Virtualization::LibVirt::blklist -M Rex::Virtualization::LibVirt::list -M Rex::Virtualization::LibVirt::info -M Rex::Virtualization::LibVirt::shutdown -M Rex::Virtualization::LibVirt::reboot -M Rex::Virtualization::LibVirt::dumpxml -M Rex::Virtualization::LibVirt::status -M Rex::Virtualization::LibVirt::import -M Rex::Virtualization::LibVirt::create -M Rex::Virtualization::LibVirt::option -M Rex::Virtualization::LibVirt::start -M Rex::Virtualization::LibVirt::vncdisplay -M Rex::Virtualization::LibVirt::clone -M Rex::Virtualization::LibVirt::iflist -M Rex::Virtualization::LibVirt::destroy -M Rex::Virtualization::LibVirt::hypervisor -M Rex::Virtualization::LibVirt::guestinfo -M Rex::Virtualization::Base -M Rex::Virtualization::Docker::delete -M Rex::Virtualization::Docker::list -M Rex::Virtualization::Docker::daemon -M Rex::Virtualization::Docker::info -M Rex::Virtualization::Docker::shutdown -M Rex::Virtualization::Docker::reboot -M Rex::Virtualization::Docker::create -M Rex::Virtualization::Docker::start -M Rex::Virtualization::Docker::destroy -M Rex::Shared::Var::Scalar -M Rex::Shared::Var::Array -M Rex::Shared::Var::Hash -M Rex::Shared::Var -M Rex::Hardware -M Rex::Args::Single -M Rex::Args::Integer -M Rex::Args::String -M Rex::Inventory::DMIDecode -M Rex::Inventory::Proc::Cpuinfo -M Rex::Inventory::SMBios::SystemInformation -M Rex::Inventory::SMBios::CPU -M Rex::Inventory::SMBios::Memory -M Rex::Inventory::SMBios::BaseBoard -M Rex::Inventory::SMBios::Section -M Rex::Inventory::SMBios::MemoryArray -M Rex::Inventory::SMBios::Bios -M Rex::Inventory::Hal -M Rex::Inventory::SMBios -M Rex::Inventory::Bios -M Rex::Inventory::HP::ACU -M Rex::Inventory::Hal::Object::Net -M Rex::Inventory::Hal::Object::Storage -M Rex::Inventory::Hal::Object::Volume -M Rex::Inventory::Hal::Object -M Rex::Inventory::DMIDecode::SystemInformation -M Rex::Inventory::DMIDecode::CPU -M Rex::Inventory::DMIDecode::Memory -M Rex::Inventory::DMIDecode::BaseBoard -M Rex::Inventory::DMIDecode::Section -M Rex::Inventory::DMIDecode::MemoryArray -M Rex::Inventory::DMIDecode::Bios -M Rex::Inventory::Proc -M Rex::File::Parser::Data -M Rex::File::Parser::Ini -M Rex::Notify -M Rex::Logger -M Rex::Commands -M Rex::Hook -M Rex::Exporter -M Rex::Box::VBox -M Rex::Box::Amazon -M Rex::Box::Base -M Rex::Box::KVM -M Rex::Output::JUnit -M Rex::Output::Base -M Rex::Report::YAML -M Rex::Report::Base -M Rex::Inventory -M Rex::FS::File -M Rex::Output -M Rex::Cron -M Rex::Batch -M Rex::CMDB -M Rex::TaskList -M Rex::Pkg::SuSE -M Rex::Pkg::Debian -M Rex::Pkg::Ubuntu -M Rex::Pkg::Gentoo -M Rex::Pkg::FreeBSD -M Rex::Pkg::SunOS -M Rex::Pkg::OpenWrt -M Rex::Pkg::Redhat -M Rex::Pkg::NetBSD -M Rex::Pkg::Base -M Rex::Pkg::ALT -M Rex::Pkg::Mageia -M Rex::Pkg::OpenBSD -M Rex::Pkg::SunOS::pkg -M Rex::Pkg::SunOS::OpenCSW -M Rex::Template -M Rex::Profiler -M Rex::User -M Rex::Box -M Rex::Resource::Common -M Rex::Cloud -M Rex::SCM::Subversion -M Rex::SCM::Git -M Rex::Report -M Rex::Value -M Rex::Group -M Rex::CMDB::YAML -M Rex::CMDB::Base -M Rex::Group::Entry::Server -M Rex::Group::Lookup::Command -M Rex::Group::Lookup::File -M Rex::Group::Lookup::DBI -M Rex::Group::Lookup::YAML -M Rex::Group::Lookup::XML -M Rex::Group::Lookup::INI -M Rex::Task -M Rex::Resource -M Rex::Interface::Fs::Sudo -M Rex::Interface::Fs::HTTP -M Rex::Interface::Fs::Local -M Rex::Interface::Fs::Base -M Rex::Interface::Fs::SSH -M Rex::Interface::Fs::OpenSSH -M Rex::Interface::File::Sudo -M Rex::Interface::File::HTTP -M Rex::Interface::File::Local -M Rex::Interface::File::Base -M Rex::Interface::File::SSH -M Rex::Interface::File::OpenSSH -M Rex::Interface::Cache::YAML -M Rex::Interface::Cache::Base -M Rex::Interface::Connection::Fake -M Rex::Interface::Connection::HTTP -M Rex::Interface::Connection::Local -M Rex::Interface::Connection::HTTPS -M Rex::Interface::Connection::Base -M Rex::Interface::Connection::SSH -M Rex::Interface::Connection::OpenSSH -M Rex::Interface::File -M Rex::Interface::Connection -M Rex::Interface::Executor -M Rex::Interface::Shell -M Rex::Interface::Exec::Sudo -M Rex::Interface::Exec::HTTP -M Rex::Interface::Exec::Local -M Rex::Interface::Exec::Base -M Rex::Interface::Exec::SSH -M Rex::Interface::Exec::OpenSSH -M Rex::Interface::Executor::Default -M Rex::Interface::Executor::Base -M Rex::Interface::Exec -M Rex::Interface::Fs -M Rex::Interface::Shell::Ksh -M Rex::Interface::Shell::Zsh -M Rex::Interface::Shell::Ash -M Rex::Interface::Shell::Default -M Rex::Interface::Shell::Sh -M Rex::Interface::Shell::Bash -M Rex::Interface::Shell::Tcsh -M Rex::Interface::Shell::Base -M Rex::Interface::Shell::Csh -M Rex::Interface::Cache -M Rex::Fork::Task -M Rex::Fork::Manager -M Rex::CLI -M Rex::Test::Base -M Rex::Test::Base::has_content -M Rex::Test::Base::has_file -M Rex::Test::Base::has_package -M Rex::Test::Base::has_service_stopped -M Rex::Test::Base::has_service_running -M Rex::Constants -M Rex::Hardware::Memory -M Rex::Hardware::Network -M Rex::Hardware::VirtInfo -M Rex::Hardware::Swap -M Rex::Hardware::Kernel -M Rex::Hardware::Host -M Rex::Hardware::Network::Linux -M Rex::Hardware::Network::FreeBSD -M Rex::Hardware::Network::Solaris -M Rex::Hardware::Network::NetBSD -M Rex::Hardware::Network::OpenBSD -M Rex::Hardware::Network::Darwin -M Rex::Cloud::Amazon -M Rex::Cloud::Base -M Rex::Cloud::Jiffybox -M Rex::Cloud::OpenStack -M Rex::Transaction -M Rex::Commands::LVM -M Rex::Commands::Virtualization -M Rex::Commands::Sysctl -M Rex::Commands::SimpleCheck -M Rex::Commands::Rsync -M Rex::Commands::Notify -M Rex::Commands::Tail -M Rex::Commands::Inventory -M Rex::Commands::Cron -M Rex::Commands::DB -M Rex::Commands::File -M Rex::Commands::Gather -M Rex::Commands::Network -M Rex::Commands::User -M Rex::Commands::Box -M Rex::Commands::Upload -M Rex::Commands::JobControl -M Rex::Commands::Cloud -M Rex::Commands::Run -M Rex::Commands::Download -M Rex::Commands::Process -M Rex::Commands::Sync -M Rex::Commands::Kernel -M Rex::Commands::MD5 -M Rex::Commands::Iptables -M Rex::Commands::Partition -M Rex::Commands::Service -M Rex::Commands::Pkg -M Rex::Commands::Fs -M Rex::Commands::Host -M Rex::Commands::SCM -M Rex::Commands::Mkfs -M Rex::Service -M Rex::Pkg -M Rex::Cron::Linux -M Rex::Cron::SunOS -M Rex::Cron::Base -M Rex::Config -M Rex::Helper::System -M Rex::Helper::Run -M Rex::Helper::SSH2 -M Rex::Helper::DBI -M Rex::Helper::Encode -M Rex::Helper::Array -M Rex::Helper::INI -M Rex::Helper::Misc -M Rex::Helper::SSH2::Expect -M Rex::Helper::UserAgent -M Rex::Helper::Hash -M Rex::Helper::URI -M Rex::Helper::Path -M Rex::TaskList::Parallel_ForkManager -M Rex::TaskList::Base -M Rex::Require -M Rex::Args -M Rex::Sudo::File -M Rex::Test -M Rex::Service::SuSE -M Rex::Service::Debian -M Rex::Service::ALT::systemd -M Rex::Service::Gentoo::systemd -M Rex::Service::Mageia::systemd -M Rex::Service::Ubuntu -M Rex::Service::Gentoo -M Rex::Service::FreeBSD -M Rex::Service::SunOS -M Rex::Service::Redhat::systemd -M Rex::Service::OpenWrt -M Rex::Service::Redhat -M Rex::Service::NetBSD -M Rex::Service::Base -M Rex::Service::ALT -M Rex::Service::Mageia -M Rex::Service::OpenBSD -M Rex::Service::SunOS::svcadm -M Rex::Service::SuSE::systemd -M Rex::Service::Arch::systemd -M Rex -M Net::SSH2 @end @bundle.sh.tpl #!/bin/bash echo "" echo "Self Extracting Rex Installer" echo "" export TMPDIR=`mktemp -d /tmp/rex.bundle.XXXXXX` ARCHIVE=`awk '/^__ARCHIVE_BELOW__/ {print NR + 1; exit 0; }' $0` echo -n "Extracting archive... " echo tail -n+$ARCHIVE $0 | tar xz -C $TMPDIR if [ "$?" != "0" ]; then echo "failed." exit 1 fi echo "done." CDIR=`pwd` cd $TMPDIR export LD_LIBRARY_PATH=`pwd`/lib.bin:$LD_LIBRARY_PATH ./rex.bin -f `pwd`/Rexfile $* cd $CDIR rm -rf $TMPDIR exit 0 __ARCHIVE_BELOW__ @end