t000755001751001751 013232707066 11153 5ustar00janjan000000000000Rex-1.6.001.t100644001751001751 61313232707066 11700 0ustar00janjan000000000000Rex-1.6.0/tuse strict; use warnings; use Test::More tests => 3; use Rex::Commands; desc("Test"); task( "test", sub { return "test"; } ); is( 1, Rex::TaskList->create()->is_task("test"), "is_task" ); is( "Test", Rex::TaskList->create()->get_desc("test"), "get test task description" ); is( "test", Rex::TaskList->create()->get_task("test")->run(""), "run test task" ); 1; fs.t100644001751001751 44213232707066 12070 0ustar00janjan000000000000Rex-1.6.0/tuse strict; use warnings; use Test::More tests => 1; use Rex::Commands::Fs; my $fake_file = "file_that_does_not_exist"; eval { Rex::Commands::Fs::stat($fake_file); }; my $err = $@; like( $err, qr/^Can't stat $fake_file/, "Trying to stat a non-existent file throws an exception" ); db.t100644001751001751 523713232707066 12074 0ustar00janjan000000000000Rex-1.6.0/tuse strict; use warnings; BEGIN { use Test::More; use Data::Dumper; eval "use DBI; 1" or plan skip_all => "Could not load DBI module"; eval "use Rex::Commands::DB; 1" or plan skip_all => "Could not load Rex::Commands::DB module: $@"; eval "use Test::mysqld; 1" or plan skip_all => "Could not load Test::mysqld module"; } my $dbh; my $mysqld = Test::mysqld->new( my_cnf => { 'skip-networking' => '', # no TCP socket } ) or do { no warnings 'once'; plan skip_all => $Test::mysqld::errstr; }; plan tests => 38; SKIP: { $dbh = DBI->connect( $mysqld->dsn( dbname => 'test' ), ); Rex::Commands::DB->import( { dsn => $mysqld->dsn( dbname => 'test' ) } ); _test_select(); _test_insert(); _test_delete(); _test_update(); _test_batch(); } sub _test_select { _initalize_db(); my @data = db( 'select' => { fields => '*', from => 'mytest', where => 'id=2' } ); is( $data[0]->{mykey}, "second", "correct value for id 2" ); @data = db( 'select' => { fields => '*', from => 'mytest', where => 'id=5' } ); is( $data[0], undef, "no data" ) or diag Dumper \@data; _cleanup_db(); } sub _test_insert { _initalize_db(); ok( db( insert => 'mytest', { id => 5, mykey => 'fifth' } ), "INSERT" ); my @data = db( 'select' => { fields => '*', from => 'mytest', where => 'id=5' } ); is( $data[0]->{mykey}, "fifth", "inserted fifths value" ); _cleanup_db(); } sub _test_delete { _initalize_db(); ok( db( delete => 'mytest', { where => 'id = 1' } ), "deleted" ); my @data = db( 'select' => { fields => '*', from => 'mytest', where => 'id=1' } ); is( $data[0], undef, "no data returned delete ok" ); _cleanup_db(); } sub _test_update { _initalize_db(); ok( db( 'update', 'mytest' => { set => { mykey => 'new value' }, where => "id=2" } ), "updated" ); my @data = db( 'select' => { fields => '*', from => 'mytest', where => 'id=2' } ); is( $data[0]->{mykey}, "new value", "correct updated value for id 2" ); _cleanup_db(); } sub _test_batch { TODO: { _initalize_db(); _cleanup_db(); } } sub _cleanup_db { ok( $dbh->do('DROP TABLE mytest'), "table mytest deleted" ); } sub _initalize_db { ok( $dbh->do('CREATE TABLE mytest (id INT primary key, mykey varchar(64))'), "table mytest created" ); ok( $dbh->do('INSERT INTO mytest VALUES(1, "first")'), "First INSERT" ); ok( $dbh->do('INSERT INTO mytest VALUES(2, "second")'), "Second INSERT" ); ok( $dbh->do('INSERT INTO mytest VALUES(3, "third")'), "Third INSERT" ); ok( $dbh->do('INSERT INTO mytest VALUES(4, "fourth")'), "Fourth INSERT" ); } df.t100644001751001751 555513232707066 12103 0ustar00janjan000000000000Rex-1.6.0/tuse strict; use warnings; use Test::More tests => 36; use Rex::Commands::Fs; my @lines = eval { local (@ARGV) = ("t/df.out2"); <>; }; my $df = Rex::Commands::Fs::_parse_df(@lines); ok( exists $df->{tmpfs}, "found tmpfs" ); is( $df->{tmpfs}->{used_perc}, '0%', "tmpfs percent usage" ); is( $df->{tmpfs}->{free}, 255160, "tmpfs free" ); is( $df->{tmpfs}->{mounted_on}, "/dev/shm", "tmpfs mounted_on" ); is( $df->{tmpfs}->{used}, 0, "tmpfs used" ); is( $df->{tmpfs}->{size}, 255160, "tmpfs size" ); ok( exists $df->{"/dev/sda1"}, "found /dev/sda1" ); is( $df->{"/dev/sda1"}->{used_perc}, '15%', "/dev/sda1 percent usage" ); is( $df->{"/dev/sda1"}->{free}, 402687, "/dev/sda1 free" ); is( $df->{"/dev/sda1"}->{mounted_on}, "/boot", "/dev/sda1 mounted_on" ); is( $df->{"/dev/sda1"}->{used}, 67557, "/dev/sda1 used" ); is( $df->{"/dev/sda1"}->{size}, 495844, "/dev/sda1 size" ); ok( exists $df->{"/dev/sda2"}, "found /dev/sda2" ); is( $df->{"/dev/sda2"}->{used_perc}, '10%', "/dev/sda2 percent usage" ); is( $df->{"/dev/sda2"}->{free}, 15489344, "/dev/sda2 free" ); is( $df->{"/dev/sda2"}->{mounted_on}, "/", "/dev/sda2 mounted_on" ); is( $df->{"/dev/sda2"}->{used}, 1693244, "/dev/sda2 used" ); is( $df->{"/dev/sda2"}->{size}, 18102140, "/dev/sda2 size" ); @lines = (); $df = {}; @lines = eval { local (@ARGV) = ("t/df.out1"); <>; }; $df = Rex::Commands::Fs::_parse_df(@lines); ok( exists $df->{tmpfs}, "found tmpfs" ); is( $df->{tmpfs}->{used_perc}, '0%', "tmpfs percent usage" ); is( $df->{tmpfs}->{free}, 255160, "tmpfs free" ); is( $df->{tmpfs}->{mounted_on}, "/dev/shm", "tmpfs mounted_on" ); is( $df->{tmpfs}->{used}, 0, "tmpfs used" ); is( $df->{tmpfs}->{size}, 255160, "tmpfs size" ); ok( exists $df->{"/dev/sda1"}, "found /dev/sda1" ); is( $df->{"/dev/sda1"}->{used_perc}, '15%', "/dev/sda1 percent usage" ); is( $df->{"/dev/sda1"}->{free}, 402687, "/dev/sda1 free" ); is( $df->{"/dev/sda1"}->{mounted_on}, "/boot", "/dev/sda1 mounted_on" ); is( $df->{"/dev/sda1"}->{used}, 67557, "/dev/sda1 used" ); is( $df->{"/dev/sda1"}->{size}, 495844, "/dev/sda1 size" ); ok( exists $df->{"/dev/mapper/vg_c6test0232-lv_root"}, "found /dev/mapper/vg_c6test0232-lv_root" ); is( $df->{"/dev/mapper/vg_c6test0232-lv_root"}->{used_perc}, '10%', "/dev/mapper/vg_c6test0232-lv_root percent usage" ); is( $df->{"/dev/mapper/vg_c6test0232-lv_root"}->{free}, 15489344, "/dev/mapper/vg_c6test0232-lv_root free" ); is( $df->{"/dev/mapper/vg_c6test0232-lv_root"}->{mounted_on}, "/", "/dev/mapper/vg_c6test0232-lv_root mounted_on" ); is( $df->{"/dev/mapper/vg_c6test0232-lv_root"}->{used}, 1693244, "/dev/mapper/vg_c6test0232-lv_root used" ); is( $df->{"/dev/mapper/vg_c6test0232-lv_root"}->{size}, 18102140, "/dev/mapper/vg_c6test0232-lv_root size" ); Rex-1.6.0000755001751001751 013232707066 10767 5ustar00janjan000000000000README100644001751001751 45013232707066 11707 0ustar00janjan000000000000Rex-1.6.0 This archive contains the distribution Rex, version 1.6.0: Remote Execution This software is Copyright (c) 2018 by Jan Gehring. This is free software, licensed under: The Apache License, Version 2.0, January 2004 This README file was generated by Dist::Zilla::Plugin::Readme v6.010. bin000755001751001751 013232707066 11460 5ustar00janjan000000000000Rex-1.6.0rex100755001751001751 764113232707066 12354 0ustar00janjan000000000000Rex-1.6.0/bin#!perl # # (c) Jan Gehring # # vim: set ts=3 sw=3 tw=0: # vim: set expandtab: use strict; use warnings; our $VERSION = '1.6.0'; # VERSION BEGIN { # this is for new package format if ( -d '/usr/lib/rex/lib' ) { use lib '/usr/lib/rex/lib'; } } use Rex::CLI; my $rex = Rex::CLI->new; $rex->__run__; __END__ =pod =head1 NAME (R)?ex - (Remote)? Execution =head1 DESCRIPTION Rex is a command line tool which executes commands on remote servers. Define tasks in Perl and execute them on remote servers or groups of servers. Rex can be used to: =over 4 =item - Deploy web applications to servers sequentially or in parallel. =item - Automate common tasks. =item - Provision servers using Rex's builtin tools. =back =head1 SYNOPSIS bash# rex -h # Show usage bash# rex -T # List tasks bash# rex uname # Run the 'uname' task bash# rex -H server[01..10] uname # Run the 'uname' task on all the specified hosts bash# rex -G production uname # Run 'uname' on hosts on the 'production' hostgroup bash# rex deploy --gracefully # Pass '--gracefully' to the 'deploy' task =head1 USAGE rex [] [-H ] [-G ] [] rex -T[m|y|v] [] -b Run batch -e Run the given code fragment -E Execute a task on the given environment -G|-g Execute a task on the given server groups -H Execute a task on the given hosts (space delimited) -z Execute a task on hosts from this command's output -K Public key file for the ssh connection -P Private key file for the ssh connection -p Password for the ssh connection -u Username for the ssh connection -d Show debug output -ddd Show more debug output (includes profiling output) -m Monochrome output: no colors -o Output format -q Quiet mode: no log output -qw Quiet mode: only output warnings and errors -Q Really quiet: output nothing -T List tasks -Tm List tasks in machine-readable format -Tv List tasks verbosely -Ty List tasks in YAML format -c Turn cache ON -C Turn cache OFF -f Use this file instead of Rexfile -F Force: disregard lock file -h Display this help message -M Load this module instead of Rexfile -O Pass additional options, like CMDB path -s Use sudo for every command -S Password for sudo -t Number of threads to use (aka 'parallelism' param) -v Display (R)?ex version =head1 Rexfile When you run C it reads the file C in the current working directory. A Rexfile consists of 2 major parts: Configuration and Task Definitions. =head2 Configuration =head3 Simple Authentication user "bruce"; password "batman"; pass_auth; =head3 Key Authentication private_key "/path/to/your/private/key.file"; public_key "/path/to/your/public/key.file"; =head3 Define Logging logging to_file => "rex.log"; logging to_syslog => "local0"; =head3 Group your servers Rex gives you the ability to define groups of servers. Groups can be defined the Rexfile: group "frontends" => "frontend01", "frontend02", "frontend03", "frontend04", "frontend[05..09]"; Groups can also be defined in a B file: [frontends] frontend[01..04] =head2 Other Configuration timeout 10; # ssh timeout parallelism 2; # execute tasks in parallel =head2 Defining tasks A basic task looks like this: # task description desc "This task tells you how long since the server was rebooted"; # task definition task "shortname", sub { say run "uptime"; }; You can also set a default server group: desc "This is a long description of a task"; task "shortname", group => "frontends", sub { say run "uptime"; }; =cut md5.t100644001751001751 34613232707066 12150 0ustar00janjan000000000000Rex-1.6.0/tuse strict; use warnings; use Test::More tests => 1; use Rex::Commands::MD5; my $test_file = Rex::Helper::File::Spec->catfile( 't', 'md5test.bin' ); is( md5($test_file), '93b885adfe0da089cdf634904fd59f71', 'MD5 checksum OK' ); xml.t100644001751001751 305213232707066 12300 0ustar00janjan000000000000Rex-1.6.0/tuse Test::More tests => 6; use FindBin qw($Bin); use Rex::Require; SKIP: { eval { XML::LibXML->require }; skip 'Missing XML::LibXML for XML file support.', 6 if $@; require Rex::Group::Lookup::XML; Rex::Group::Lookup::XML->import; use Rex::Group; use Rex::Commands; no warnings 'once'; $::QUIET = 1; groups_xml("$Bin/test.xml"); my %groups = Rex::Group->get_groups; # stringification needed for is_deeply string comparison my @application_server = map { "$_" } @{ $groups{application} }; is( scalar( @{ $groups{application} } ), 2, "2 application servers" ); is_deeply( [ sort @application_server ], [qw/machine01 machine02/], "got machine02,machine01" ); is( scalar( @{ $groups{profiler} } ), 2, "2 profiler servers 2" ); my ($server1) = grep { m/\bmachine07\b/ } @{ $groups{profiler} }; my ($server2) = grep { m/\bmachine01\b/ } @{ $groups{application} }; Rex::TaskList->create()->set_in_transaction(1); no_ssh( task( "xml_task1", $server1, sub { is( connection()->server->option("services"), "nginx,docker", "got services inside task" ); } ) ); Rex::Commands::do_task("xml_task1"); no_ssh( task( "xml_task2", $server2, sub { is( connection()->server->get_user(), 'root', "$server2 user is 'root'" ); is( connection()->server->get_password(), 'foob4r', "$server2 password is 'foob4r'" ); } ) ); Rex::Commands::do_task("xml_task2"); Rex::TaskList->create()->set_in_transaction(0); } ini.t100644001751001751 705213232707066 12263 0ustar00janjan000000000000Rex-1.6.0/tuse strict; use warnings; use Test::More tests => 34; SKIP: { eval { require String::Escape }; skip 'Missing String::Escape for INI file support.', 34 if $@; require Rex::Group::Lookup::INI; Rex::Group::Lookup::INI->import; use Rex::Group; use Rex::Commands; no warnings 'once'; $::QUIET = 1; groups_file("t/test.ini"); my %groups = Rex::Group->get_groups; is( scalar( @{ $groups{frontends} } ), 5, "frontends 5 servers" ); is( scalar( @{ $groups{backends} } ), 3, "backends 3 servers" ); ok( grep { $_ eq "fe01" } @{ $groups{frontends} }, "got fe01" ); ok( grep { $_ eq "fe02" } @{ $groups{frontends} }, "got fe02" ); ok( grep { $_ eq "fe03" } @{ $groups{frontends} }, "got fe03" ); ok( grep { $_ eq "fe04" } @{ $groups{frontends} }, "got fe04" ); ok( grep { $_ eq "fe05" } @{ $groups{frontends} }, "got fe05" ); ok( grep { $_ eq "be01" } @{ $groups{backends} }, "got be01" ); ok( grep { $_ eq "be02" } @{ $groups{backends} }, "got be02" ); ok( grep { $_ eq "be04" } @{ $groups{backends} }, "got be04" ); ok( grep { $_ eq "db[01..02]" } @{ $groups{db} }, "got db[01..02]" ); ok( grep { $_ eq "[01..02]-cassandra" } @{ $groups{cassandra} }, "got [01..02]-cassandra]" ); ok( grep { $_ eq "[111..133/11]-voldemort" } @{ $groups{voldemort} }, "got [111..133/11]-voldemort" ); ok( grep { $_ eq "[1,3,7,01]-kiokudb" } @{ $groups{kiokudb} }, "got [1,3,7,01]-kiokudb" ); ok( grep { $_ eq "[1..3,5,9..21/3]-riak" } @{ $groups{riak} }, "got [1..3,5,9..21/3]-riak" ); ok( grep { $_ eq "redis01" } @{ $groups{redis} }, "got redis01" ); ok( grep { $_ eq "redis02" } @{ $groups{redis} }, "got redis02" ); ok( grep { $_ eq "be01" } @{ $groups{redis} }, "got be01 in redis" ); ok( grep { $_ eq "be02" } @{ $groups{redis} }, "got be01 in redis" ); ok( grep { $_ eq "be04" } @{ $groups{redis} }, "got be01 in redis" ); ok( grep { $_ eq "redis01" } @{ $groups{memcache} }, "got redis01 in memcache" ); ok( grep { $_ eq "redis02" } @{ $groups{memcache} }, "got redis02 in memcache" ); ok( grep { $_ eq "be01" } @{ $groups{memcache} }, "got be01 in redis in memcache" ); ok( grep { $_ eq "be02" } @{ $groups{memcache} }, "got be01 in redis in memcache" ); ok( grep { $_ eq "be04" } @{ $groups{memcache} }, "got be01 in redis in memcache" ); ok( grep { $_ eq "memcache01" } @{ $groups{memcache} }, "got memcache01" ); ok( grep { $_ eq "memcache02" } @{ $groups{memcache} }, "got memcache02" ); delete $ENV{REX_USER}; user("krimdomu"); password("foo"); pass_auth(); my ($server) = grep { $_ eq "memcache02" } @{ $groups{memcache} }; no_ssh( task( "mytask", $server, sub { is( connection()->server->option("services"), "apache,memcache", "got services inside task" ); } ) ); my $task = Rex::TaskList->create()->get_task("mytask"); my $auth = $task->merge_auth($server); is( $auth->{user}, "krimdomu", "got krimdomu user for memcache02" ); is( $auth->{password}, "foo", "got foo password for memcache02" ); Rex::Config->set_use_server_auth(1); $auth = $task->merge_auth($server); is( $auth->{user}, "root", "got root user for memcache02" ); is( $auth->{password}, "foob4r", "got foob4r password for memcache02" ); ok( $auth->{sudo}, "got sudo for memcache02" ); is( $server->option("services"), "apache,memcache", "got services of server" ); # don't fork the task Rex::TaskList->create()->set_in_transaction(1); Rex::Commands::do_task("mytask"); Rex::TaskList->create()->set_in_transaction(0); } dmi.t100644001751001751 1052413232707066 12273 0ustar00janjan000000000000Rex-1.6.0/tuse Test::More tests => 30; use Rex::Inventory::DMIDecode; my @lines = eval { local (@ARGV) = ("t/dmi.linux.out"); <>; }; my $dmi = Rex::Inventory::DMIDecode->new( lines => \@lines ); isa_ok( $dmi, "Rex::Inventory::DMIDecode", "dmi object" ); my $bb = $dmi->get_base_board; my $bios = $dmi->get_bios; my @cpus = $dmi->get_cpus; my @mems = $dmi->get_memory_modules; my @mema = $dmi->get_memory_arrays; my $sysinfo = $dmi->get_system_information; is( $bb->get_product_name, "Parallels Virtual Platform", "get base board product name" ); is( $bios->get_vendor, "Parallels Software International Inc.", "get bios vendor" ); like( $bios->get_version, qr/\d\.0\./, "get bios version" ); is( $bios->get_release_date, "10/26/2007" ); ok( $cpus[0]->get_max_speed eq "2800 MHz" || $cpus[0]->get_max_speed eq "30000 MHz" || $cpus[0]->get_max_speed eq "2800MHz", "cpu get max speed" ); ok( $mems[0]->get_size eq "512 MB" || $mems[0]->get_size eq "1073741824 bytes", "memory size" ); ok( $mema[0]->get_maximum_capacity eq "8 GB" || $mema[0]->get_maximum_capacity eq "256 GB" || $mema[0]->get_maximum_capacity eq "8589934592 bytes", "memory array max capacity" ); is( $sysinfo->get_manufacturer, "Parallels Software International Inc.", "system information manucafturer" ); is( $sysinfo->get_product_name, "Parallels Virtual Platform", "system information product name" ); @lines = undef; $dmi = undef; $bb = undef; $bios = undef; @cpus = undef; @mems = undef; @mema = undef; $sysinfo = undef; @lines = eval { local (@ARGV) = ("t/dmi.obsd.out"); <>; }; $dmi = Rex::Inventory::DMIDecode->new( lines => \@lines ); isa_ok( $dmi, "Rex::Inventory::DMIDecode", "dmi object (obsd)" ); $bb = $dmi->get_base_board; $bios = $dmi->get_bios; @cpus = $dmi->get_cpus; @mems = $dmi->get_memory_modules; @mema = $dmi->get_memory_arrays; $sysinfo = $dmi->get_system_information; is( $bb->get_product_name, "Parallels Virtual Platform", "get base board product name" ); is( $bios->get_vendor, "Parallels Software International Inc.", "get bios vendor" ); like( $bios->get_version, qr/\d\.0\./, "get bios version" ); is( $bios->get_release_date, "10/26/2007" ); ok( $cpus[0]->get_max_speed eq "2800 MHz" || $cpus[0]->get_max_speed eq "30000 MHz" || $cpus[0]->get_max_speed eq "2800MHz", "cpu get max speed" ); ok( $mems[0]->get_size eq "512 MB" || $mems[0]->get_size eq "1073741824 bytes", "memory size" ); ok( $mema[0]->get_maximum_capacity eq "8 GB" || $mema[0]->get_maximum_capacity eq "256 GB" || $mema[0]->get_maximum_capacity eq "8589934592 bytes", "memory array max capacity" ); is( $sysinfo->get_manufacturer, "Parallels Software International Inc.", "system information manucafturer" ); is( $sysinfo->get_product_name, "Parallels Virtual Platform", "system information product name" ); @lines = undef; $dmi = undef; $bb = undef; $bios = undef; @cpus = undef; @mems = undef; @mema = undef; $sysinfo = undef; @lines = eval { local (@ARGV) = ("t/dmi.fbsd.out"); <>; }; $dmi = Rex::Inventory::DMIDecode->new( lines => \@lines ); isa_ok( $dmi, "Rex::Inventory::DMIDecode", "dmi object (fbsd)" ); $bb = $dmi->get_base_board; $bios = $dmi->get_bios; @cpus = $dmi->get_cpus; @mems = $dmi->get_memory_modules; @mema = $dmi->get_memory_arrays; $sysinfo = $dmi->get_system_information; is( $bb->get_product_name, "Parallels Virtual Platform", "get base board product name" ); is( $bios->get_vendor, "Parallels Software International Inc.", "get bios vendor" ); like( $bios->get_version, qr/\d\.0\./, "get bios version" ); is( $bios->get_release_date, "10/26/2007" ); ok( $cpus[0]->get_max_speed eq "2800 MHz" || $cpus[0]->get_max_speed eq "30000 MHz" || $cpus[0]->get_max_speed eq "2800MHz", "cpu get max speed" ); ok( $mems[0]->get_size eq "512 MB" || $mems[0]->get_size eq "1073741824 bytes", "memory size" ); ok( $mema[0]->get_maximum_capacity eq "8 GB" || $mema[0]->get_maximum_capacity eq "256 GB" || $mema[0]->get_maximum_capacity eq "8589934592 bytes", "memory array max capacity" ); is( $sysinfo->get_manufacturer, "Parallels Software International Inc.", "system information manucafturer" ); is( $sysinfo->get_product_name, "Parallels Virtual Platform", "system information product name" ); env.t100644001751001751 35713232707066 12255 0ustar00janjan000000000000Rex-1.6.0/tuse Test::More tests => 1; use Rex::Commands::Run; $::QUIET = 1; SKIP: { skip 'Do not run tests on Windows', 1 if $^O =~ m/^MSWin/; my $s = run( "printenv REX", env => { 'REX' => 'XER' } ); like( $s, qr/XER/, "run with env" ); } LICENSE100644001751001751 2635313232707066 12106 0ustar00janjan000000000000Rex-1.6.0This software is Copyright (c) 2018 by Jan Gehring. This is free software, licensed under: The Apache License, Version 2.0, January 2004 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. dist.ini100644001751001751 205013232707066 12511 0ustar00janjan000000000000Rex-1.6.0name = Rex version = 1.6.0 release_status = stable author = Jan Gehring license = Apache_2_0 copyright_holder = Jan Gehring [@Filter] -bundle = @Basic -remove = MakeMaker [AutoPrereqs] skip = __Rexfile__ [MakeMaker::Awesome] header = die 'OS unsupported' if ( $^O eq 'MSWin32' && scalar((Win32::GetOSVersion())[1]) < 6 ); [ManifestSkip] [MetaProvides::Package] [MetaResources] homepage = http://www.rexify.org bugtracker.web = https://github.com/RexOps/Rex/issues repository.url = https://github.com/RexOps/Rex.git repository.web = https://github.com/RexOps/Rex repository.type = git x_twitter = https://twitter.com/RexOps x_IRC = irc://irc.freenode.net/rex [MetaJSON] [OSPrereqs / !~MSWin] IO::Pty = 0 Net::OpenSSH = 0 Net::SFTP::Foreign = 0 [OSPrereqs / ~MSWin] Net::SSH2 = 0 [OurPkgVersion] ; [PodCoverageTests] [PodSyntaxTests] [Prereqs] perl = 5.008008 [Prereqs / BuildRequires] Test::Pod = 0 [Test::MinimumVersion] max_target_perl = 5.8.8 [Test::Perl::Critic] critic_config = ../../.perlcriticrc path.t100644001751001751 74013232707066 12415 0ustar00janjan000000000000Rex-1.6.0/tuse Test::More tests => 3; use Rex::Helper::Path; my $path = Rex::Helper::Path::resolv_path( "/home/foo/bar/baz", 1 ); is( $path, "/home/foo/bar/baz", "local test absolute path" ); SKIP: { skip 'No home directory tests for Windows.', 2 if $^O =~ m/^MSWin/; $path = Rex::Helper::Path::resolv_path( "~/bar/baz", 1 ); like( $path, qr{^/}, "expanded \$HOME" ); $path = Rex::Helper::Path::resolv_path("~/bar/baz"); like( $path, qr{^/}, "expanded \$HOME - no local" ); } file.t100644001751001751 2035013232707066 12437 0ustar00janjan000000000000Rex-1.6.0/tuse strict; use warnings; use Cwd 'getcwd'; my $cwd = getcwd; use Test::More tests => 55; use Rex::Commands::File; use Rex::Commands::Fs; use Rex::Commands::Gather; use Rex::Commands::Run; Rex::Config->set( foo => "bar" ); if ( $ENV{rex_LOCALTEST} ) { Rex::Config->set_executor_for( perl => "/Users/jan/perl5/perlbrew/perls/perl-5.14.2/bin/perl" ); } my $tmp_dir = "/tmp"; if ( $^O =~ m/^MSWin/ ) { $tmp_dir = $ENV{TMP}; } my $filename = "$cwd/test-$$.txt"; file( $filename, content => "blah blah\nfoo bar" ); my $c = cat($filename); ok( $c, "cat" ); like( $c, qr/blah/, "file with content (1)" ); like( $c, qr/bar/, "file with content (2)" ); Rex::Commands::Fs::unlink($filename); is( is_file($filename), undef, "file removed" ); file( $filename, content => "blah blah\nbaaazzzz", mode => 777 ); my %stats = Rex::Commands::Fs::stat($filename); if ( is_windows() && $^O ne "cygwin" ) { is( $stats{mode}, "0666", "windows without chmod" ); } else { is( $stats{mode}, "0777", "fs chmod ok" ); } my $changed = 0; my $content = cat($filename); unlike( $content, qr/change/ms, "found change" ); append_if_no_such_line( $filename, "change", qr{change}, on_change => sub { $changed = 1; } ); $content = cat($filename); like( $content, qr/change/ms, "found change" ); is( $changed, 1, "something was changed in the file" ); append_if_no_such_line( $filename, line => "dream-breaker", regexp => qr{^dream-breaker$} ); $content = cat($filename); like( $content, qr/dream\-breaker/ms, "found dream-breaker" ); append_if_no_such_line( $filename, line => "#include /etc/sudoers.d/*.conf", regexp => qr{^#include /etc/sudoers.d/\*.conf$}, ); $content = cat($filename); like( $content, qr{#include /etc/sudoers\.d/\*\.conf}ms, "found sudoers entry" ); append_if_no_such_line( $filename, line => 'silly with "quotes"' ); $content = cat($filename); like( $content, qr/silly with "quotes"/ms, "found entry with quotes" ); append_if_no_such_line( $filename, line => "#include /etc/sudoers.d/*.conf" ); my @content = split( /\n/, cat($filename) ); isnt( $content[-1], "#include /etc/sudoers.d/*.conf", "last entry is not #include ..." ); append_if_no_such_line( $filename, 'KEY="VAL"' ); $content = cat($filename); like( $content, qr/KEY="VAL"/ms, "found KEY=VAL" ); append_if_no_such_line( $filename, "change", qr{change}, on_change => sub { $changed = 0; } ); is( $changed, 1, "nothing was changed in the file" ); append_if_no_such_line( $filename, "change", on_change => sub { $changed = 0; } ); is( $changed, 1, "nothing was changed in the file without regexp" ); $content = cat($filename); unlike( $content, qr/foobar/ms, "not found foobar" ); append_if_no_such_line( $filename, line => "foobar", ); $content = cat($filename); like( $content, qr/foobar/ms, "found foobar" ); append_if_no_such_line( $filename, line => "bazzada", regexp => qr{^foobar}, ); $content = cat($filename); unlike( $content, qr/bazzada/ms, "not found bazzada" ); append_if_no_such_line( $filename, line => "tacktack", regexp => qr{blah blah}ms, ); $content = cat($filename); unlike( $content, qr/tacktack/ms, "not found tacktack" ); append_if_no_such_line( $filename, line => "nothing there", regexp => [ qr{blah blah}ms, qr{tzuhgjbn}ms ], ); $content = cat($filename); unlike( $content, qr/nothing there/ms, "not found nothing there" ); append_if_no_such_line( $filename, line => "this is there", regexp => [ qr{qaywsx}ms, qr{tzuhgjbn}ms ], ); $content = cat($filename); like( $content, qr/this is there/ms, "found this is there" ); append_if_no_such_line( $filename, line => "bazzada", regexp => qr{^bazzada}, ); $content = cat($filename); like( $content, qr/bazzada/ms, "found bazzada (2)" ); append_or_amend_line( $filename, line => 'silly more "quotes"', regexp => qr{^silly with "quotes"}, ); $content = cat($filename); like( $content, qr/silly more "quotes"/m, "found silly more quotes" ); unlike( $content, qr/silly with "quotes"/m, "silly with quotes no longer exists" ); append_or_amend_line( $filename, line => "dream-maker", regexp => qr{^dream\-}, ); $content = cat($filename); like( $content, qr/^dream\-maker$/m, "found dream-maker" ); unlike( $content, qr/^dream\-breaker$/m, "dream-breaker no longer exists" ); append_or_amend_line( $filename, line => "dream2-maker", regexp => qr{^dream2\-}, ); $content = cat($filename); like( $content, qr/^dream2\-maker$/m, "found dream2-maker" ); like( $content, qr/^dream\-maker$/m, "dream-maker still exists" ); append_or_amend_line( $filename, line => "dream3-maker", regexp => qr{^dream2\-}, ); $content = cat($filename); like( $content, qr/^dream3\-maker$/m, "found dream3-maker" ); unlike( $content, qr/^dream2\-maker$/m, "dream2-maker no longer exists" ); like( $content, qr/^dream\-maker$/m, "dream-maker still exists" ); unlike( $content, qr/^$/m, "no extra blank lines inserted" ); file "file with space-$$.txt", content => "file with space\n"; is( is_file("file with space-$$.txt"), 1, "file with space exists" ); $c = ""; $c = cat "file with space-$$.txt"; like( $c, qr/file with space/m, "found content of file with space" ); Rex::Commands::Fs::unlink("file with space-$$.txt"); is( is_file("file with space-$$.txt"), undef, "file with space removed" ); file "file_with_\@-$$.txt", content => "file with at sign\n"; is( is_file("file_with_\@-$$.txt"), 1, "file with at sign exists" ); $c = ""; $c = cat "file_with_\@-$$.txt"; like( $c, qr/file with at sign/m, "found content of file with at sign" ); Rex::Commands::Fs::unlink("file_with_\@-$$.txt"); is( is_file("file_with_\@-$$.txt"), undef, "file with at sign removed" ); Rex::Commands::Fs::unlink($filename); is( is_file($filename), undef, "test.txt removed" ); $filename = "$tmp_dir/test-sed-$$.txt"; file $filename, content => "this is a sed test file\nthese are just some lines\n0505\n0606\n0707\n'foo'\n/etc/passwd\n\"baz\"\n{klonk}\nfoo bar\n\\.-~'[a-z]\$ foo {1} /with/some/slashes \%\&()?\n|.-\\~'[a-z]\$ bar {2} /with/more/slashes \%\&()?\n"; sed qr/fo{2} bar/, "baz bar", $filename; $content = cat $filename; like( $content, qr/baz bar/, "sed replaced foo bar" ); sed qr/^\\\.\-\~'\[a\-z\]\$ foo \{1\} \/with\/some\/slashes/, "got replaced", $filename; $content = cat $filename; like( $content, qr/got replaced/, "sed replaced strange chars" ); sed qr/^\|\.\-\\\~'\[a\-z\]\$ BAR \{2\} \/with\/more\/slashes/i, "got another replace", $filename; $content = cat $filename; like( $content, qr/got another replace/, "sed replaced strange chars" ); my @lines = split( /\n/, $content ); like( $lines[-1], qr/^got another replace/, "last line was successfully replaced" ); like( $lines[-2], qr/^got replaced/, "second last line was successfully replaced" ); like( $lines[-4], qr/^\{klonk\}/, "fourth last line untouched" ); sed qr{0606}, "6666", $filename; $content = cat $filename; like( $content, qr/6666/, "sed replaced 0606" ); sed qr{'foo'}, "'bar'", $filename; $content = cat $filename; like( $content, qr/'bar'/, "sed replaced 'foo'" ); sed qr{/etc/passwd}, "/etc/shadow", $filename; $content = cat $filename; like( $content, qr/\/etc\/shadow/, "sed replaced /etc/passwd" ); sed qr{"baz"}, '"boooooz"', $filename; $content = cat $filename; like( $content, qr/"boooooz"/, "sed replaced baz" ); sed qr/{klonk}/, '{plonk}', $filename; $content = cat $filename; like( $content, qr/{plonk}/, "sed replaced {klonk}" ); sed qr/{klonk}/, '{plonk}', $filename; $content = cat $filename; like( $content, qr/{plonk}/, "sed replaced {klonk}" ); unlink $filename; file "$tmp_dir/multiline-$$.txt", content => "this is\na test.\n"; sed qr/is\sa test/msi, "no one\nknows!", "$tmp_dir/multiline-$$.txt", multiline => 1; $content = cat "$tmp_dir/multiline-$$.txt"; is( $content, "this no one\nknows!.\n", "multiline replacement" ); unlink "$tmp_dir/multiline-$$.txt"; file "$tmp_dir/test.d-$$", ensure => "directory"; ok( -d "$tmp_dir/test.d-$$", "created directory with file()" ); rmdir "$tmp_dir/test.d-$$"; $content = 'Hello this is <%= $::foo %>'; is( template( \$content, __no_sys_info__ => 1 ), "Hello this is bar", "get keys from Rex::Config" ); is( template( \$content, { foo => "baz", __no_sys_info__ => 1 } ), "Hello this is baz", "overwrite keys from Rex::Config" ); task.t100644001751001751 1043213232707066 12462 0ustar00janjan000000000000Rex-1.6.0/tuse strict; use warnings; use Test::More; my %have_mods = ( 'Net::SSH2' => 1, 'Net::OpenSSH' => 1, ); for my $m ( keys %have_mods ) { my $have_mod = 1; eval "use $m;"; if ($@) { $have_mods{$m} = 0; } } unless ( $have_mods{'Net::SSH2'} or $have_mods{'Net::OpenSSH'} ) { plan skip_all => 'SSH module not found. You need Net::SSH2 or Net::OpenSSH to connect to servers via SSH.'; } else { plan tests => 36; } use Rex::Task; use Rex::Commands; { no warnings 'once'; $::QUIET = 1; } my $t1 = Rex::Task->new( name => "foo" ); isa_ok( $t1, "Rex::Task", "create teask object" ); is( $t1->get_connection_type, "Local", "get connection type for local" ); is( $t1->is_local, 1, "is task local" ); is( $t1->is_remote, 0, "is task not remote" ); $t1->set_server("192.168.1.1"); is( $t1->server->[0], "192.168.1.1", "get/set server" ); is( $t1->is_local, 0, "is task not local" ); $t1->set_desc("Description"); is( $t1->desc, "Description", "get/set description" ); is( $t1->get_connection_type, ( $have_mods{"Net::OpenSSH"} && $^O !~ m/^MSWin/ ? "OpenSSH" : "SSH" ), "get connection type for ssh" ); is( $t1->want_connect, 1, "want a connection?" ); $t1->modify( "no_ssh", 1 ); is( $t1->want_connect, 0, "want no connection?" ); is( $t1->get_connection_type, "Fake", "get connection type for fake" ); $t1->modify( "no_ssh", 0 ); is( $t1->want_connect, 1, "want a connection?" ); is( $t1->get_connection_type, ( $have_mods{"Net::OpenSSH"} && $^O !~ m/^MSWin/ ? "OpenSSH" : "SSH" ), "get connection type for ssh" ); Rex::Config->set( "connection" => "SSH" ); is( $t1->get_connection_type, "SSH", "get connection type for ssh" ); Rex::Config->set( "connection" => "OpenSSH" ); is( $t1->get_connection_type, "OpenSSH", "get connection type for ssh" ); $t1->set_user("root"); is( $t1->user, "root", "get/set the user" ); $t1->set_password("f00b4r"); is( $t1->password, "f00b4r", "get/set the password" ); is( $t1->name, "foo", "get task name" ); $t1->set_auth( "user", "foo" ); is( $t1->user, "foo", "set auth user" ); $t1->set_auth( "password", "baz" ); is( $t1->password, "baz", "set auth password" ); my $test_var = 0; $t1->set_code( sub { $test_var = connection()->server; } ); Rex::Config->set( "connection" => $have_mods{"Net::OpenSSH"} && $^O !~ m/^MSWin/ ? "OpenSSH" : "SSH" ); ok( !$t1->connection->is_connected, "connection currently not established" ); $t1->modify( "no_ssh", 1 ); $t1->connect("localtest"); ok( $t1->connection->is_connected, "connection established" ); $t1->run("localtest"); is( $test_var, "localtest", "task run" ); $t1->disconnect(); my $before_hook = 0; $t1->delete_server; is( $t1->is_remote, 0, "task is no more remote" ); is( $t1->is_local, 1, "task is now local" ); $t1->modify( before => sub { my $server = shift; my $server_ref = shift; $before_hook = 1; $$server_ref = "local02"; } ); my $server = $t1->current_server; $t1->run_hook( \$server, "before" ); is( $before_hook, 1, "run before hook" ); is( $t1->is_remote, 1, "task is now remote" ); is( $t1->is_local, 0, "task is no more local" ); $t1->modify( before => sub { my $server = shift; my $server_ref = shift; $before_hook = 2; $$server_ref = ""; } ); $server = $t1->current_server; $t1->run_hook( \$server, "before" ); is( $before_hook, 2, "run before hook - right direction" ); is( $t1->is_remote, 0, "task is no not remote" ); is( $t1->is_local, 1, "task is now local" ); task( "ret_test1", sub { return "string"; } ); task( "ret_test2", sub { return ( "e1", "e2" ); } ); task( "param_test1", sub { my $param = shift; is_deeply( $param, { name => "foo" }, "First parameter to task is a hashRef" ); } ); task( "param_test2", sub { is_deeply( \@_, [ "city", "bar" ], "Parameters are a list (length 2)." ); } ); task( "param_test3", sub { is_deeply( \@_, [ "blah", "blub", "bumm" ], "Parameters are a list (length 3)." ); } ); my $s = ret_test1(); is( $s, "string", "task successfully returned a string" ); my @l = ret_test2(); is_deeply( \@l, [ "e1", "e2" ], "task successfully returned a list" ); param_test1( { name => "foo" } ); param_test2( city => "bar" ); param_test3( "blah", "blub", "bumm" ); 0.54.t100644001751001751 226713232707066 12075 0ustar00janjan000000000000Rex-1.6.0/tuse strict; use warnings; use Test::More tests => 13; use Rex -feature => '0.54'; set( "key1", "val1" ); is( get("key1"), "val1", "got value of key1" ); set( "key1", "val2" ); is( get("key1"), "val2", "got new value of key1" ); set( "key2", [qw/one two three/] ); is( get("key2")->[0], "one", "got value of first item in key2" ); is( get("key2")->[1], "two", "got value of 2nd item in key2" ); is( get("key2")->[2], "three", "got value of 3rd item in key2" ); set( "key2", [qw/four five/] ); is( get("key2")->[0], "four", "got value of NEW first item in key2" ); is( get("key2")->[1], "five", "got value of NEW 2nd item in key2" ); set( "key3", { name => 'foo', surname => 'bar' } ); is( get("key3")->{name}, "foo", "got value of name parameter in key3" ); is( get("key3")->{surname}, "bar", "got value of surname parameter in key3" ); set( "key3", { x1 => 'x', x2 => 'xx' } ); is( get("key3")->{x1}, "x", "got value of NEW name parameter x1 in key3" ); is( get("key3")->{x2}, "xx", "got value of NEW name parameter x2 in key3" ); ok( !exists get("key3")->{name}, "name parameter doesn't exists anymore" ); ok( !exists get("key3")->{surname}, "surname parameter doesn't exists anymore" ); auth.t100644001751001751 667613232707066 12460 0ustar00janjan000000000000Rex-1.6.0/tuse strict; use warnings; use Test::More tests => 48; use Rex::Commands; use Rex::Group; { no warnings 'once'; $::QUIET = 1; } group( "srvgr1", "srv1" ); group( "srvgr2", "srv2", "srv3" ); delete $ENV{REX_USER}; user("root1"); password("pass1"); private_key("priv.key1"); public_key("pub.key1"); task( "testa1", sub { } ); user("root2"); password("pass2"); private_key("priv.key2"); public_key("pub.key2"); auth( for => "srvgr1", user => "foouser", password => "foopass", private_key => "foo.priv", public_key => "foo.pub" ); task( "testb1", group => "srvgr1", sub { } ); task( "testb2", group => "srvgr2", sub { } ); task( "testb3", group => [ "srvgr1", "srvgr2" ], sub { } ); task( "testa2", sub { } ); user("root3"); password("pass3"); private_key("priv.key3"); public_key("pub.key3"); task( "testa3", sub { } ); my $auth = Rex::TaskList->create()->get_task("testa1")->{auth}; is( $auth->{user}, "root1" ); is( $auth->{password}, "pass1" ); is( $auth->{private_key}, "priv.key1" ); is( $auth->{public_key}, "pub.key1" ); $auth = Rex::TaskList->create()->get_task("testa2")->{auth}; is( $auth->{user}, "root2" ); is( $auth->{password}, "pass2" ); is( $auth->{private_key}, "priv.key2" ); is( $auth->{public_key}, "pub.key2" ); $auth = Rex::TaskList->create()->get_task("testa3")->{auth}; is( $auth->{user}, "root3" ); is( $auth->{password}, "pass3" ); is( $auth->{private_key}, "priv.key3" ); is( $auth->{public_key}, "pub.key3" ); my $task_b1 = Rex::TaskList->create()->get_task("testb1"); $auth = $task_b1->{auth}; is( $auth->{user}, "root2" ); is( $auth->{password}, "pass2" ); is( $auth->{private_key}, "priv.key2" ); is( $auth->{public_key}, "pub.key2" ); my $servers = $task_b1->server; for my $server ( @{$servers} ) { $auth = $task_b1->merge_auth($server); is( $auth->{user}, "root2" ); is( $auth->{password}, "pass2" ); is( $auth->{private_key}, "priv.key2" ); is( $auth->{public_key}, "pub.key2" ); } my $task_b2 = Rex::TaskList->create()->get_task("testb2"); $servers = $task_b2->server; for my $server ( @{$servers} ) { $auth = $task_b2->merge_auth($server); is( $auth->{user}, "root2" ); is( $auth->{password}, "pass2" ); is( $auth->{private_key}, "priv.key2" ); is( $auth->{public_key}, "pub.key2" ); } my $task_b3 = Rex::TaskList->create()->get_task("testb3"); $servers = $task_b3->server; for my $server ( @{$servers} ) { $auth = $task_b3->merge_auth($server); is( $auth->{user}, "root2" ); is( $auth->{password}, "pass2" ); is( $auth->{private_key}, "priv.key2" ); is( $auth->{public_key}, "pub.key2" ); } auth( for => "testa4", user => "baruser", password => "barpass", private_key => "testa4.priv", public_key => "testa4.pub" ); task( "testa4", sub { } ); $auth = Rex::TaskList->create()->get_task("testa4")->{auth}; is( $auth->{user}, "baruser" ); is( $auth->{password}, "barpass" ); is( $auth->{private_key}, "testa4.priv" ); is( $auth->{public_key}, "testa4.pub" ); $ENV{REX_USER} = "root5"; user("toor5"); password("pass5"); private_key("testa5.priv"); public_key("testa5.pub"); task( "testa5", sub { } ); $auth = Rex::TaskList->create()->get_task("testa5")->{auth}; is( $auth->{user}, "root5" ); is( $auth->{password}, "pass5" ); is( $auth->{private_key}, "testa5.priv" ); is( $auth->{public_key}, "testa5.pub" ); 0.31.t100644001751001751 1270513232707066 12106 0ustar00janjan000000000000Rex-1.6.0/tuse strict; use warnings; use Test::More tests => 139; use Rex -feature => '0.31'; delete $ENV{REX_USER}; user("root3"); password("pass3"); private_key("priv.key3"); public_key("pub.key3"); key_auth(); no warnings; is( Rex::TaskList->create()->is_default_auth(), 0, "default auth off" ); use warnings; group( "foo", "server1", "server2", "server3" ); group( "bar", "serv[01..10]" ); my @servers = Rex::Group->get_group("foo"); is( $servers[0], "server1", "get_group" ); is( $servers[2], "server3", "get_group" ); @servers = Rex::Group->get_group("bar"); @servers = $servers[0]->get_servers; is( $servers[0], "serv01", "get_group with evaluation" ); is( $servers[5], "serv06", "get_group with evaluation" ); task( "authtest1", group => "foo", sub { } ); task( "authtest2", group => "bar", sub { } ); task( "authtest3", "srv001", sub { } ); task( "authtest4", group => "latebar", sub { } ); group( "latebar", "server[01..03]" ); my $task = Rex::TaskList->create()->get_task("authtest3"); my @all_server = @{ $task->server }; for my $server (@all_server) { my $auth = $task->merge_auth($server); is( $auth->{user}, "root3", "merge_auth - user" ); is( $auth->{password}, "pass3", "merge_auth - pass" ); is( $auth->{public_key}, "pub.key3", "merge_auth - pub" ); is( $auth->{private_key}, "priv.key3", "merge_auth - priv" ); is( $auth->{auth_type}, "key", "merge_auth - auth" ); } pass_auth(); for my $server (@all_server) { my $auth = $task->merge_auth($server); is( $auth->{user}, "root3", "merge_auth - user" ); is( $auth->{password}, "pass3", "merge_auth - pass" ); is( $auth->{public_key}, "pub.key3", "merge_auth - pub" ); is( $auth->{private_key}, "priv.key3", "merge_auth - priv" ); is( $auth->{auth_type}, "pass", "merge_auth - auth" ); } auth( for => "bar", user => "jan", password => "foo" ); auth( for => "latebar", user => "jan", password => "foo" ); $task = Rex::TaskList->create()->get_task("authtest1"); @all_server = @{ $task->server }; for my $server (@all_server) { my $auth = $task->merge_auth($server); is( $auth->{user}, "root3", "merge_auth - user" ); is( $auth->{password}, "pass3", "merge_auth - pass" ); is( $auth->{public_key}, "pub.key3", "merge_auth - pub" ); is( $auth->{private_key}, "priv.key3", "merge_auth - priv" ); } $task = Rex::TaskList->create()->get_task("authtest2"); @all_server = @{ $task->server }; for my $server (@all_server) { my $auth = $task->merge_auth($server); is( $auth->{user}, "jan", "merge_auth - user" ); is( $auth->{password}, "foo", "merge_auth - pass" ); is( $auth->{public_key}, "pub.key3", "merge_auth - pub" ); is( $auth->{private_key}, "priv.key3", "merge_auth - priv" ); is( $auth->{auth_type}, "try", "merge_auth - auth_type" ); ok( !$auth->{sudo}, "merge_auth - sudo" ); } $task = Rex::TaskList->create()->get_task("authtest4"); @all_server = @{ $task->server }; for my $server (@all_server) { my $auth = $task->merge_auth($server); is( $auth->{user}, "jan", "merge_auth - user - lategroup" ); is( $auth->{password}, "foo", "merge_auth - pass - lategroup" ); is( $auth->{public_key}, "pub.key3", "merge_auth - pub - lategroup" ); is( $auth->{private_key}, "priv.key3", "merge_auth - priv - lategroup" ); is( $auth->{auth_type}, "try", "merge_auth - auth_type - lategroup" ); ok( !$auth->{sudo}, "merge_auth - sudo - lategroup" ); } auth( for => "authtest1", user => "deploy", password => "baz", private_key => FALSE(), public_key => FALSE(), sudo => TRUE() ); $task = Rex::TaskList->create()->get_task("authtest1"); @all_server = @{ $task->server }; for my $server (@all_server) { my $auth = $task->merge_auth($server); is( $auth->{user}, "deploy", "merge_auth - user" ); is( $auth->{password}, "baz", "merge_auth - pass" ); is( $auth->{public_key}, FALSE(), "merge_auth - pub" ); is( $auth->{private_key}, FALSE(), "merge_auth - priv" ); is( $auth->{auth_type}, "pass", "merge_auth - auth_type" ); is( $auth->{sudo}, TRUE(), "merge_auth - sudo" ); } set( "key1", "val1" ); is( get("key1"), "val1", "got value of key1" ); set( "key1", "val2" ); is( get("key1"), "val2", "got new value of key1" ); set( "key2", [qw/one two three/] ); is( get("key2")->[0], "one", "got value of first item in key2" ); is( get("key2")->[1], "two", "got value of 2nd item in key2" ); is( get("key2")->[2], "three", "got value of 3rd item in key2" ); set( "key2", [qw/four five/] ); is( get("key2")->[0], "one", "got value of first item in key2" ); is( get("key2")->[1], "two", "got value of 2nd item in key2" ); is( get("key2")->[2], "three", "got value of 3rd item in key2" ); is( get("key2")->[3], "four", "got value of NEW first item in key2" ); is( get("key2")->[4], "five", "got value of NEW 2nd item in key2" ); set( "key3", { name => 'foo', surname => 'bar' } ); is( get("key3")->{name}, "foo", "got value of name parameter in key3" ); is( get("key3")->{surname}, "bar", "got value of surname parameter in key3" ); set( "key3", { x1 => 'x', x2 => 'xx' } ); is( get("key3")->{name}, "foo", "got value of name parameter in key3" ); is( get("key3")->{surname}, "bar", "got value of surname parameter in key3" ); is( get("key3")->{x1}, "x", "got value of NEW name parameter x1 in key3" ); is( get("key3")->{x2}, "xx", "got value of NEW name parameter x2 in key3" ); host.t100644001751001751 174513232707066 12464 0ustar00janjan000000000000Rex-1.6.0/tuse strict; use warnings; use Test::More tests => 10; use Rex::Commands::Host; my @content = eval { local (@ARGV) = ("t/hosts.ex"); <>; }; my @ret = Rex::Commands::Host::_parse_hosts(@content); is( $ret[0]->{host}, "localhost", "got localhost" ); is( $ret[0]->{ip}, "127.0.0.1", "got 127.0.0.1" ); @ret = get_host( "mango", @content ); is( $ret[0]->{ip}, "192.168.2.23", "got 192.168.2.23 by alias" ); is( $ret[0]->{host}, "mango.rexify.org", "got mango.rexify.org by alias" ); @content = eval { local (@ARGV) = ("t/hosts.ex2"); <>; }; @ret = Rex::Commands::Host::_parse_hosts(@content); is( $ret[0]->{host}, "localhost", "got localhost" ); is( $ret[0]->{ip}, "127.0.0.1", "got 127.0.0.1" ); is( $ret[2]->{host}, "rexify.org", "got rexify.org" ); is( $ret[2]->{ip}, "1.2.3.4", "got 1.2.3.4" ); @ret = get_host( "rexify.org", @content ); is( $ret[0]->{ip}, "1.2.3.4", "got 1.2.3.4 from get_host" ); is( $ret[0]->{host}, "rexify.org", "got rexify.org from get_host" ); args.t100644001751001751 154713232707066 12443 0ustar00janjan000000000000Rex-1.6.0/tuse strict; use warnings; use Test::More; use Rex::Args; @ARGV = qw( -h -g test1 -g test2 -T -dv -u user -p pass -t 5 foo --name=thename --num=5 ); Rex::Args->parse_rex_opts; my %opts = Rex::Args->getopts; my $groups = $opts{g}; is_deeply( $groups, [qw/test1 test2/], "Got array for groups" ); ok( exists $opts{h} && $opts{h}, "single parameter" ); ok( exists $opts{T} && $opts{T}, "single parameter (2)" ); ok( exists $opts{d} && $opts{d}, "single parameter (3) (multiple)" ); ok( exists $opts{v} && $opts{v}, "single parameter (4) (multiple)" ); ok( exists $opts{u} && $opts{u} eq "user", "parameter with option (1) / string" ); ok( exists $opts{p} && $opts{p} eq "pass", "parameter with option (2) / string" ); ok( exists $opts{t} && $opts{t} == 5, "parameter with option (3) / integer" ); is( $ARGV[0], "foo", "got the taskname" ); done_testing; case.t100644001751001751 203013232707066 12406 0ustar00janjan000000000000Rex-1.6.0/tuse Test::More tests => 7; use Rex::Commands; my $test = "Debian"; my $var = case $test, { Debian => "foo", default => "bar", }; is( $var, "foo", "string equality" ); $var = case $test, { qr{debian}i => "baz", default => "this is bad", }; is( $var, "baz", "regexp match" ); $var = case $test, { debian => "some", default => "this is good", }; is( $var, "this is good", "return default" ); $var = case $test, { debian => "tata", }; ok( !$var, "var is undef" ); $var = case $test, { Debian => sub { return "this is debian"; }, default => sub { return "default"; } }; is( $var, "this is debian", "use a sub - string match" ); $var = undef; $var = case $test, { qr{debian}i => sub { return "this is debian"; }, default => sub { return "default"; } }; is( $var, "this is debian", "use a sub - regexp match" ); $var = undef; $var = case $test, { debian => sub { return "this is debian"; }, default => sub { return "default"; } }; is( $var, "default", "use a sub - return default" ); cmdb.t100644001751001751 472113232707066 12411 0ustar00janjan000000000000Rex-1.6.0/tuse strict; use warnings; use Test::More tests => 13; use Rex::CMDB; use Rex::Commands; use Rex::Commands::File; set( cmdb => { type => "YAML", path => "t/cmdb", } ); my $ntp = get( cmdb( "ntp", "foo" ) ); ok( $ntp->[0] eq "ntp1" && $ntp->[1] eq "ntp2", "got something from default.yml" ); my $name = get( cmdb( "name", "foo" ) ); is( $name, "foo", "got name from foo.yml" ); my $dns = get( cmdb( "dns", "foo" ) ); ok( $dns->[0] eq "1.1.1.1" && $dns->[1] eq "2.2.2.2", "got dns from env/default.yml" ); my $vhost = get( cmdb( "vhost", "foo" ) ); ok( $vhost->{name} eq "foohost" && $vhost->{doc_root} eq "/var/www", "got vhost from env/foo.yml" ); $ntp = undef; $ntp = get( cmdb("ntp") ); ok( $ntp->[0] eq "ntp1" && $ntp->[1] eq "ntp2", "got something from default.yml" ); $dns = undef; $dns = get( cmdb("dns") ); ok( $dns->[0] eq "1.1.1.1" && $dns->[1] eq "2.2.2.2", "got dns from env/default.yml" ); my $all = get( cmdb( undef, "foo" ) ); is( $all->{ntp}->[0], "ntp1", "got ntp1 from cmdb - all request" ); is( $all->{dns}->[1], "2.2.2.2", "got dns2 from cmdb - all request" ); is( $all->{vhost}->{name}, "foohost", "got vhost name from cmdb - all request" ); is( $all->{name}, "foo", "got name from cmdb - all request" ); Rex::Config->set_register_cmdb_template(1); my $content = 'Hello this is <%= $::name %>'; is( template( \$content, __no_sys_info__ => 1 ), "Hello this is defaultname", "get keys from CMDB" ); is( template( \$content, { name => "baz", __no_sys_info__ => 1 } ), "Hello this is baz", "overwrite keys from CMDB" ); set( cmdb => { type => "YAML", path => "t/cmdb", merge_behavior => 'LEFT_PRECEDENT', } ); my $foo_all = get( cmdb( undef, "foo" ) ); is_deeply( $foo_all, { 'ntp' => [ 'ntp1', 'ntp2' ], 'newntp' => [ 'ntpdefaultfoo01', 'ntpdefaultfoo02', 'ntp1', 'ntp2' ], 'dns' => [ '1.1.1.1', '2.2.2.2' ], 'MyTest::foo::mode' => '0666', 'vhost' => { 'name' => 'foohost', 'doc_root' => '/var/www' }, 'name' => 'foo', 'vhost2' => { 'name' => 'vhost2foo', 'doc_root' => '/var/www' }, 'users' => { 'root' => { 'password' => 'proot', 'id' => '0' }, 'user02' => { 'password' => 'puser02', 'id' => '600' }, 'user01' => { 'password' => 'puser01', 'id' => '500' } } }, "DeepMerge CMDB" ); base.t100644001751001751 121313232707066 12407 0ustar00janjan000000000000Rex-1.6.0/tuse strict; use warnings; use Test::More; use Test::UseAllModules; BEGIN { all_uses_ok except => qw( Rex::Cloud::Amazon Rex::Commands::DB Rex::Commands::Rsync Rex::Group::Lookup::DBI Rex::Group::Lookup::INI Rex::Group::Lookup::XML Rex::Helper::DBI Rex::Helper::INI Rex::Interface::Connection::OpenSSH Rex::Interface::Connection::SSH Rex::Interface::Exec::OpenSSH Rex::Interface::Exec::SSH Rex::Interface::File::OpenSSH Rex::Interface::File::SSH Rex::Interface::Fs::OpenSSH Rex::Interface::Fs::SSH Rex::Output Rex::Output::JUnit Rex::TaskList::Parallel_ForkManager ); } cron.t100644001751001751 5346513232707066 12476 0ustar00janjan000000000000Rex-1.6.0/tuse Test::More; $^O =~ m/^MSWin/ ? plan tests => 83 : plan tests => 286; use Rex::Cron::Base; my @lines = eval { local (@ARGV) = ("t/cron.ex"); <>; }; chomp @lines; my $c = Rex::Cron::Base->new; $c->parse_cron(@lines); my @cron = $c->list; is( $cron[0]->{type}, "comment", "first line is a comment" ); is( $cron[1]->{type}, "comment", "second line is comment" ); is( $cron[1]->{line}, "# Shell variable for cron", "got the line content #2" ); is( $cron[2]->{type}, "env", "3rd line is a env variable" ); is( $cron[2]->{name}, "SHELL", "name is SHELL" ); is( $cron[2]->{value}, "/bin/bash", "value is /bin/bash" ); is( $cron[3]->{type}, "comment", "another comment" ); is( $cron[4]->{type}, "env", "another env - path" ); is( $cron[4]->{name}, "PATH", "path env" ); is( $cron[4]->{value}, "/usr/local/bin:/usr/local/sbin:/sbin:/usr/sbin:/bin:/usr/bin:/usr/bin/X11", "path env value" ); is( $cron[5]->{type}, "env", "myvar env" ); is( $cron[5]->{name}, "MYVAR", "myvar env got name" ); is( $cron[5]->{value}, '"foo=bar"', "myvar env got value" ); is( $cron[6]->{type}, "comment", "yet another comment" ); is( $cron[7]->{type}, "comment", "yet another line comment" ); is( $cron[8]->{type}, "job", "the first job" ); is( $cron[8]->{cron}->{minute}, 5, "the first job / min" ); is( $cron[8]->{cron}->{hour}, "9-20", "the first job / hour" ); is( $cron[8]->{cron}->{day_of_month}, "*", "the first job / day" ); is( $cron[8]->{cron}->{month}, "*", "the first job / month" ); is( $cron[8]->{cron}->{day_of_week}, "*", "the first job / day_of_month of week" ); is( $cron[8]->{cron}->{command}, "/home/username/script/script1.sh > /dev/null", "the first job / cmd" ); is( $cron[9]->{type}, "job", "the 2nd job" ); is( $cron[9]->{cron}->{minute}, "*/10", "the 2nd job / min" ); is( $cron[9]->{cron}->{hour}, "*", "the 2nd job / hour" ); is( $cron[9]->{cron}->{day_of_month}, "*", "the 2nd job / day" ); is( $cron[9]->{cron}->{month}, "*", "the 2nd job / month" ); is( $cron[9]->{cron}->{day_of_week}, "*", "the 2nd job / day_of_month of week" ); is( $cron[9]->{cron}->{command}, "/usr/bin/script2.sh > /dev/null 2>&1", "the 2nd job / cmd" ); is( $cron[10]->{type}, "job", "the 3rd job" ); is( $cron[10]->{cron}->{minute}, "59", "the 3rd job / min" ); is( $cron[10]->{cron}->{hour}, "23", "the 3rd job / hour" ); is( $cron[10]->{cron}->{day_of_month}, "*", "the 3rd job / day" ); is( $cron[10]->{cron}->{month}, "*", "the 3rd job / month" ); is( $cron[10]->{cron}->{day_of_week}, "0,4", "the 3rd job / day_of_month of week" ); is( $cron[10]->{cron}->{command}, "cp /pfad/zu/datei /pfad/zur/kopie", "the 3rd job / cmd" ); is( $cron[11]->{type}, "job", "the 4th job" ); is( $cron[11]->{cron}->{minute}, "*", "the 4th job / min" ); is( $cron[11]->{cron}->{hour}, "*", "the 4th job / hour" ); is( $cron[11]->{cron}->{day_of_month}, "*", "the 4th job / day" ); is( $cron[11]->{cron}->{month}, "*", "the 4th job / month" ); is( $cron[11]->{cron}->{day_of_week}, "*", "the 4th job / day_of_month of week" ); like( $cron[11]->{cron}->{command}, qr/DISPLAY=:0 LANG=de_DE.UTF-8 zenity --info --text/i, "the 4th job / cmd" ); is( $cron[12]->{type}, "job", "the 5th job" ); is( $cron[12]->{cron}->{minute}, "0", "the 5th job / min" ); is( $cron[12]->{cron}->{hour}, "0", "the 5th job / hour" ); is( $cron[12]->{cron}->{day_of_month}, "*", "the 5th job / day" ); is( $cron[12]->{cron}->{month}, "*", "the 5th job / month" ); is( $cron[12]->{cron}->{day_of_week}, "*", "the 5th job / day_of_month of week" ); is( $cron[12]->{cron}->{command}, "backup", "the 5th job / cmd" ); is( $cron[13]->{type}, "comment", "last line is comment" ); $c->add( minute => "1", hour => "2", day_of_month => "3", month => "4", day_of_week => "5", command => "ls", ); $c->add( command => "foo", ); $c->add( hour => "5,6,7", month => "*/2", command => "bar", ); $c->add( minute => "0", hour => "0", day_of_week => "0", ); @cron = $c->list; is( $cron[14]->{type}, "job", "the 6th job" ); is( $cron[14]->{cron}->{minute}, "1", "the 6th job / min" ); is( $cron[14]->{cron}->{hour}, "2", "the 6th job / hour" ); is( $cron[14]->{cron}->{day_of_month}, "3", "the 6th job / day" ); is( $cron[14]->{cron}->{month}, "4", "the 6th job / month" ); is( $cron[14]->{cron}->{day_of_week}, "5", "the 6th job / day_of_month of week" ); is( $cron[14]->{cron}->{command}, "ls", "the 6th job / cmd" ); is( $cron[14]->{line}, "1 2 3 4 5 ls", "the 6th job / cron line" ); is( $cron[15]->{type}, "job", "the 7th job" ); is( $cron[15]->{cron}->{minute}, "*", "the 7th job / min" ); is( $cron[15]->{cron}->{hour}, "*", "the 7th job / hour" ); is( $cron[15]->{cron}->{day_of_month}, "*", "the 7th job / day" ); is( $cron[15]->{cron}->{month}, "*", "the 7th job / month" ); is( $cron[15]->{cron}->{day_of_week}, "*", "the 7th job / day_of_month of week" ); is( $cron[15]->{cron}->{command}, "foo", "the 7th job / cmd" ); is( $cron[15]->{line}, "* * * * * foo", "the 7th job / cron line" ); is( $cron[16]->{type}, "job", "the 8th job" ); is( $cron[16]->{cron}->{minute}, "*", "the 8th job / min" ); is( $cron[16]->{cron}->{hour}, "5,6,7", "the 8th job / hour" ); is( $cron[16]->{cron}->{day_of_month}, "*", "the 8th job / day" ); is( $cron[16]->{cron}->{month}, "*/2", "the 8th job / month" ); is( $cron[16]->{cron}->{day_of_week}, "*", "the 8th job / day_of_month of week" ); is( $cron[16]->{cron}->{command}, "bar", "the 8th job / cmd" ); is( $cron[16]->{line}, "* 5,6,7 * */2 * bar", "the 8th job / cron line" ); is( $cron[17]->{type}, "job", "the 9th job" ); is( $cron[17]->{cron}->{minute}, "0", "the 9th job / min" ); is( $cron[17]->{cron}->{hour}, "0", "the 9th job / hour" ); is( $cron[17]->{cron}->{day_of_month}, "*", "the 9th job / day" ); is( $cron[17]->{cron}->{month}, "*", "the 9th job / month" ); is( $cron[17]->{cron}->{day_of_week}, "0", "the 9th job / day_of_month of week" ); is( $cron[17]->{cron}->{command}, "false", "the 9th job / cmd" ); is( $cron[17]->{line}, "0 0 * * 0 false", "the 9th job / cron line" ); unless ( $^O =~ m/^MSWin/ ) { # # Write new entries and test again # my $file = $c->write_cron(); @lines = undef; @lines = eval { local (@ARGV) = ($file); <>; }; chomp @lines; unlink $file; $c = Rex::Cron::Base->new; $c->parse_cron(@lines); @cron = $c->list; is( $cron[0]->{type}, "comment", "first line is a comment" ); is( $cron[1]->{type}, "comment", "second line is comment" ); is( $cron[1]->{line}, "# Shell variable for cron", "got the line content #2" ); is( $cron[2]->{type}, "env", "3rd line is a env variable" ); is( $cron[2]->{name}, "SHELL", "name is SHELL" ); is( $cron[2]->{value}, "/bin/bash", "value is /bin/bash" ); is( $cron[3]->{type}, "comment", "another comment" ); is( $cron[4]->{type}, "env", "another env - path" ); is( $cron[4]->{name}, "PATH", "path env" ); is( $cron[4]->{value}, "/usr/local/bin:/usr/local/sbin:/sbin:/usr/sbin:/bin:/usr/bin:/usr/bin/X11", "path env value" ); is( $cron[5]->{type}, "env", "myvar env" ); is( $cron[5]->{name}, "MYVAR", "myvar env got name" ); is( $cron[5]->{value}, '"foo=bar"', "myvar env got value" ); is( $cron[6]->{type}, "comment", "yet another comment" ); is( $cron[7]->{type}, "comment", "yet another line comment" ); is( $cron[8]->{type}, "job", "the first job" ); is( $cron[8]->{cron}->{minute}, 5, "the first job / min" ); is( $cron[8]->{cron}->{hour}, "9-20", "the first job / hour" ); is( $cron[8]->{cron}->{day_of_month}, "*", "the first job / day" ); is( $cron[8]->{cron}->{month}, "*", "the first job / month" ); is( $cron[8]->{cron}->{day_of_week}, "*", "the first job / day_of_month of week" ); is( $cron[8]->{cron}->{command}, "/home/username/script/script1.sh > /dev/null", "the first job / cmd" ); is( $cron[9]->{type}, "job", "the 2nd job" ); is( $cron[9]->{cron}->{minute}, "*/10", "the 2nd job / min" ); is( $cron[9]->{cron}->{hour}, "*", "the 2nd job / hour" ); is( $cron[9]->{cron}->{day_of_month}, "*", "the 2nd job / day" ); is( $cron[9]->{cron}->{month}, "*", "the 2nd job / month" ); is( $cron[9]->{cron}->{day_of_week}, "*", "the 2nd job / day_of_month of week" ); is( $cron[9]->{cron}->{command}, "/usr/bin/script2.sh > /dev/null 2>&1", "the 2nd job / cmd" ); is( $cron[10]->{type}, "job", "the 3rd job" ); is( $cron[10]->{cron}->{minute}, "59", "the 3rd job / min" ); is( $cron[10]->{cron}->{hour}, "23", "the 3rd job / hour" ); is( $cron[10]->{cron}->{day_of_month}, "*", "the 3rd job / day" ); is( $cron[10]->{cron}->{month}, "*", "the 3rd job / month" ); is( $cron[10]->{cron}->{day_of_week}, "0,4", "the 3rd job / day_of_month of week" ); is( $cron[10]->{cron}->{command}, "cp /pfad/zu/datei /pfad/zur/kopie", "the 3rd job / cmd" ); is( $cron[11]->{type}, "job", "the 4th job" ); is( $cron[11]->{cron}->{minute}, "*", "the 4th job / min" ); is( $cron[11]->{cron}->{hour}, "*", "the 4th job / hour" ); is( $cron[11]->{cron}->{day_of_month}, "*", "the 4th job / day" ); is( $cron[11]->{cron}->{month}, "*", "the 4th job / month" ); is( $cron[11]->{cron}->{day_of_week}, "*", "the 4th job / day_of_month of week" ); is( $cron[11]->{cron}->{command}, 'DISPLAY=:0 LANG=de_DE.UTF-8 zenity --info --text "Beispiel für das Starten eines Programmes mit GUI"', "the 4th job / cmd" ); is( $cron[12]->{type}, "job", "the 5th job" ); is( $cron[12]->{cron}->{minute}, "0", "the 5th job / min" ); is( $cron[12]->{cron}->{hour}, "0", "the 5th job / hour" ); is( $cron[12]->{cron}->{day_of_month}, "*", "the 5th job / day" ); is( $cron[12]->{cron}->{month}, "*", "the 5th job / month" ); is( $cron[12]->{cron}->{day_of_week}, "*", "the 5th job / day_of_month of week" ); is( $cron[12]->{cron}->{command}, "backup", "the 5th job / cmd" ); is( $cron[13]->{type}, "comment", "last line is comment" ); is( $cron[14]->{type}, "job", "the 6th job" ); is( $cron[14]->{cron}->{minute}, "1", "the 6th job / min" ); is( $cron[14]->{cron}->{hour}, "2", "the 6th job / hour" ); is( $cron[14]->{cron}->{day_of_month}, "3", "the 6th job / day" ); is( $cron[14]->{cron}->{month}, "4", "the 6th job / month" ); is( $cron[14]->{cron}->{day_of_week}, "5", "the 6th job / day_of_month of week" ); is( $cron[14]->{cron}->{command}, "ls", "the 6th job / cmd" ); is( $cron[14]->{line}, "1 2 3 4 5 ls", "the 6th job / cron line" ); is( $cron[15]->{type}, "job", "the 7th job" ); is( $cron[15]->{cron}->{minute}, "*", "the 7th job / min" ); is( $cron[15]->{cron}->{hour}, "*", "the 7th job / hour" ); is( $cron[15]->{cron}->{day_of_month}, "*", "the 7th job / day" ); is( $cron[15]->{cron}->{month}, "*", "the 7th job / month" ); is( $cron[15]->{cron}->{day_of_week}, "*", "the 7th job / day_of_month of week" ); is( $cron[15]->{cron}->{command}, "foo", "the 7th job / cmd" ); is( $cron[15]->{line}, "* * * * * foo", "the 7th job / cron line" ); is( $cron[16]->{type}, "job", "the 8th job" ); is( $cron[16]->{cron}->{minute}, "*", "the 8th job / min" ); is( $cron[16]->{cron}->{hour}, "5,6,7", "the 8th job / hour" ); is( $cron[16]->{cron}->{day_of_month}, "*", "the 8th job / day" ); is( $cron[16]->{cron}->{month}, "*/2", "the 8th job / month" ); is( $cron[16]->{cron}->{day_of_week}, "*", "the 8th job / day_of_month of week" ); is( $cron[16]->{cron}->{command}, "bar", "the 8th job / cmd" ); is( $cron[16]->{line}, "* 5,6,7 * */2 * bar", "the 8th job / cron line" ); is( $cron[17]->{type}, "job", "the 9th job" ); is( $cron[17]->{cron}->{minute}, "0", "the 9th job / min" ); is( $cron[17]->{cron}->{hour}, "0", "the 9th job / hour" ); is( $cron[17]->{cron}->{day_of_month}, "*", "the 9th job / day" ); is( $cron[17]->{cron}->{month}, "*", "the 9th job / month" ); is( $cron[17]->{cron}->{day_of_week}, "0", "the 9th job / day_of_month of week" ); is( $cron[17]->{cron}->{command}, "false", "the 9th job / cmd" ); is( $cron[17]->{line}, "0 0 * * 0 false", "the 9th job / cron line" ); # # Delete 2 entries # $c->delete(14); $c->delete(9); @cron = $c->list; is( $cron[0]->{type}, "comment", "first line is a comment" ); is( $cron[1]->{type}, "comment", "second line is comment" ); is( $cron[1]->{line}, "# Shell variable for cron", "got the line content #2" ); is( $cron[2]->{type}, "env", "3rd line is a env variable" ); is( $cron[2]->{name}, "SHELL", "name is SHELL" ); is( $cron[2]->{value}, "/bin/bash", "value is /bin/bash" ); is( $cron[3]->{type}, "comment", "another comment" ); is( $cron[4]->{type}, "env", "another env - path" ); is( $cron[4]->{name}, "PATH", "path env" ); is( $cron[4]->{value}, "/usr/local/bin:/usr/local/sbin:/sbin:/usr/sbin:/bin:/usr/bin:/usr/bin/X11", "path env value" ); is( $cron[5]->{type}, "env", "myvar env" ); is( $cron[5]->{name}, "MYVAR", "myvar env got name" ); is( $cron[5]->{value}, '"foo=bar"', "myvar env got value" ); is( $cron[6]->{type}, "comment", "yet another comment" ); is( $cron[7]->{type}, "comment", "yet another line comment" ); is( $cron[8]->{type}, "job", "the first job" ); is( $cron[8]->{cron}->{minute}, 5, "the first job / min" ); is( $cron[8]->{cron}->{hour}, "9-20", "the first job / hour" ); is( $cron[8]->{cron}->{day_of_month}, "*", "the first job / day" ); is( $cron[8]->{cron}->{month}, "*", "the first job / month" ); is( $cron[8]->{cron}->{day_of_week}, "*", "the first job / day_of_month of week" ); is( $cron[8]->{cron}->{command}, "/home/username/script/script1.sh > /dev/null", "the first job / cmd" ); is( $cron[9]->{type}, "job", "the 3rd job" ); is( $cron[9]->{cron}->{minute}, "59", "the 3rd job / min" ); is( $cron[9]->{cron}->{hour}, "23", "the 3rd job / hour" ); is( $cron[9]->{cron}->{day_of_month}, "*", "the 3rd job / day" ); is( $cron[9]->{cron}->{month}, "*", "the 3rd job / month" ); is( $cron[9]->{cron}->{day_of_week}, "0,4", "the 3rd job / day_of_month of week" ); is( $cron[9]->{cron}->{command}, "cp /pfad/zu/datei /pfad/zur/kopie", "the 3rd job / cmd" ); is( $cron[10]->{type}, "job", "the 4th job" ); is( $cron[10]->{cron}->{minute}, "*", "the 4th job / min" ); is( $cron[10]->{cron}->{hour}, "*", "the 4th job / hour" ); is( $cron[10]->{cron}->{day_of_month}, "*", "the 4th job / day" ); is( $cron[10]->{cron}->{month}, "*", "the 4th job / month" ); is( $cron[10]->{cron}->{day_of_week}, "*", "the 4th job / day_of_month of week" ); is( $cron[10]->{cron}->{command}, 'DISPLAY=:0 LANG=de_DE.UTF-8 zenity --info --text "Beispiel für das Starten eines Programmes mit GUI"', "the 4th job / cmd" ); is( $cron[11]->{type}, "job", "the 5th job" ); is( $cron[11]->{cron}->{minute}, "0", "the 5th job / min" ); is( $cron[11]->{cron}->{hour}, "0", "the 5th job / hour" ); is( $cron[11]->{cron}->{day_of_month}, "*", "the 5th job / day" ); is( $cron[11]->{cron}->{month}, "*", "the 5th job / month" ); is( $cron[11]->{cron}->{day_of_week}, "*", "the 5th job / day_of_month of week" ); is( $cron[11]->{cron}->{command}, "backup", "the 5th job / cmd" ); is( $cron[12]->{type}, "comment", "last line is comment" ); is( $cron[13]->{type}, "job", "the 7th job" ); is( $cron[13]->{cron}->{minute}, "*", "the 7th job / min" ); is( $cron[13]->{cron}->{hour}, "*", "the 7th job / hour" ); is( $cron[13]->{cron}->{day_of_month}, "*", "the 7th job / day" ); is( $cron[13]->{cron}->{month}, "*", "the 7th job / month" ); is( $cron[13]->{cron}->{day_of_week}, "*", "the 7th job / day_of_month of week" ); is( $cron[13]->{cron}->{command}, "foo", "the 7th job / cmd" ); is( $cron[13]->{line}, "* * * * * foo", "the 7th job / cron line" ); is( $cron[14]->{type}, "job", "the 8th job" ); is( $cron[14]->{cron}->{minute}, "*", "the 8th job / min" ); is( $cron[14]->{cron}->{hour}, "5,6,7", "the 8th job / hour" ); is( $cron[14]->{cron}->{day_of_month}, "*", "the 8th job / day" ); is( $cron[14]->{cron}->{month}, "*/2", "the 8th job / month" ); is( $cron[14]->{cron}->{day_of_week}, "*", "the 8th job / day_of_month of week" ); is( $cron[14]->{cron}->{command}, "bar", "the 8th job / cmd" ); is( $cron[14]->{line}, "* 5,6,7 * */2 * bar", "the 8th job / cron line" ); is( $cron[15]->{type}, "job", "the 9th job" ); is( $cron[15]->{cron}->{minute}, "0", "the 9th job / min" ); is( $cron[15]->{cron}->{hour}, "0", "the 9th job / hour" ); is( $cron[15]->{cron}->{day_of_month}, "*", "the 9th job / day" ); is( $cron[15]->{cron}->{month}, "*", "the 9th job / month" ); is( $cron[15]->{cron}->{day_of_week}, "0", "the 9th job / day_of_month of week" ); is( $cron[15]->{cron}->{command}, "false", "the 9th job / cmd" ); is( $cron[15]->{line}, "0 0 * * 0 false", "the 9th job / cron line" ); $c->add_env( "FOOVAR" => "FOOVAL", ); $c->add_env( "BARVAR" => "BARVAL", ); @cron = $c->list; is( $cron[0]->{type}, "env", "1st line is now env" ); is( $cron[0]->{name}, "BARVAR", "1st line / name" ); is( $cron[0]->{value}, "BARVAL", "1st line / value" ); is( $cron[0]->{line}, 'BARVAR="BARVAL"', "1st line / value" ); is( $cron[1]->{type}, "env", "2nd line is now env" ); is( $cron[1]->{name}, "FOOVAR", "2nd line / name" ); is( $cron[1]->{value}, "FOOVAL", "2nd line / value" ); is( $cron[1]->{line}, 'FOOVAR="FOOVAL"', "2nd line / value" ); @cron = $c->list_jobs; is( $cron[0]->{minute}, 5, "the first job / min" ); is( $cron[0]->{hour}, "9-20", "the first job / hour" ); is( $cron[0]->{day_of_month}, "*", "the first job / day" ); is( $cron[0]->{month}, "*", "the first job / month" ); is( $cron[0]->{day_of_week}, "*", "the first job / day_of_month of week" ); is( $cron[0]->{command}, "/home/username/script/script1.sh > /dev/null", "the first job / cmd" ); is( $cron[1]->{minute}, "59", "the second job / min" ); is( $cron[1]->{hour}, "23", "the second job / hour" ); is( $cron[1]->{day_of_month}, "*", "the second job / day" ); is( $cron[1]->{month}, "*", "the second job / month" ); is( $cron[1]->{day_of_week}, "0,4", "the second job / day_of_month of week" ); is( $cron[1]->{command}, "cp /pfad/zu/datei /pfad/zur/kopie", "the second job / cmd" ); is( $cron[2]->{minute}, "*", "the third job / min" ); is( $cron[2]->{hour}, "*", "the third job / hour" ); is( $cron[2]->{day_of_month}, "*", "the third job / day" ); is( $cron[2]->{month}, "*", "the third job / month" ); is( $cron[2]->{day_of_week}, "*", "the third job / day_of_month of week" ); is( $cron[2]->{command}, 'DISPLAY=:0 LANG=de_DE.UTF-8 zenity --info --text "Beispiel für das Starten eines Programmes mit GUI"', "the third job / cmd" ); $c->delete_job(1); $c->delete_job(0); @cron = $c->list; is( $cron[0]->{type}, "env", "1st line is now env" ); is( $cron[0]->{name}, "BARVAR", "1st line / name" ); is( $cron[0]->{value}, "BARVAL", "1st line / value" ); is( $cron[0]->{line}, 'BARVAR="BARVAL"', "1st line / value" ); is( $cron[1]->{type}, "env", "2nd line is now env" ); is( $cron[1]->{name}, "FOOVAR", "2nd line / name" ); is( $cron[1]->{value}, "FOOVAL", "2nd line / value" ); is( $cron[1]->{line}, 'FOOVAR="FOOVAL"', "2nd line / value" ); @cron = $c->list_jobs; is( $cron[0]->{minute}, "*", "the third job / min" ); is( $cron[0]->{hour}, "*", "the third job / hour" ); is( $cron[0]->{day_of_month}, "*", "the third job / day" ); is( $cron[0]->{month}, "*", "the third job / month" ); is( $cron[0]->{day_of_week}, "*", "the third job / day_of_month of week" ); is( $cron[0]->{command}, 'DISPLAY=:0 LANG=de_DE.UTF-8 zenity --info --text "Beispiel für das Starten eines Programmes mit GUI"', "the third job / cmd" ); @cron = $c->list_envs; is( $cron[0]->{name}, "BARVAR", "1st env name" ); is( $cron[0]->{value}, "BARVAL", "1st env name" ); is( $cron[0]->{line}, 'BARVAR="BARVAL"', "1st env name" ); is( $cron[1]->{name}, "FOOVAR", "2nd env name" ); is( $cron[1]->{value}, "FOOVAL", "2nd env name" ); is( $cron[1]->{line}, 'FOOVAR="FOOVAL"', "2nd env name" ); is( $cron[2]->{name}, "SHELL", "3rd env name" ); is( $cron[2]->{value}, "/bin/bash", "3rd env name" ); is( $cron[2]->{line}, 'SHELL=/bin/bash', "3rd env name" ); $c->delete_env(1); $c->delete_env(0); @cron = $c->list_envs; is( $cron[0]->{name}, "SHELL", "3rd env name" ); is( $cron[0]->{value}, "/bin/bash", "3rd env name" ); is( $cron[0]->{line}, 'SHELL=/bin/bash', "3rd env name" ); } META.yml100644001751001751 7531613232707066 12355 0ustar00janjan000000000000Rex-1.6.0--- abstract: 'Remote Execution' author: - 'Jan Gehring ' build_requires: File::Temp: '0' Parallel::ForkManager: '0' String::Escape: '0' Test::Deep: '0' Test::More: '0' Test::Pod: '0' Test::UseAllModules: '0' configure_requires: ExtUtils::MakeMaker: '0' dynamic_config: 1 generated_by: 'Dist::Zilla version 6.010, CPAN::Meta::Converter version 2.150010' license: apache meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: Rex provides: Rex: file: lib/Rex.pm version: v1.6.0 Rex::Args: file: lib/Rex/Args.pm version: v1.6.0 Rex::Args::Integer: file: lib/Rex/Args/Integer.pm version: v1.6.0 Rex::Args::Single: file: lib/Rex/Args/Single.pm version: v1.6.0 Rex::Args::String: file: lib/Rex/Args/String.pm version: v1.6.0 Rex::Batch: file: lib/Rex/Batch.pm version: v1.6.0 Rex::Box: file: lib/Rex/Box.pm version: v1.6.0 Rex::Box::Amazon: file: lib/Rex/Box/Amazon.pm version: v1.6.0 Rex::Box::Base: file: lib/Rex/Box/Base.pm version: v1.6.0 Rex::Box::Docker: file: lib/Rex/Box/Docker.pm version: v1.6.0 Rex::Box::KVM: file: lib/Rex/Box/KVM.pm version: v1.6.0 Rex::Box::VBox: file: lib/Rex/Box/VBox.pm version: v1.6.0 Rex::CLI: file: lib/Rex/CLI.pm version: v1.6.0 Rex::CMDB: file: lib/Rex/CMDB.pm version: v1.6.0 Rex::CMDB::Base: file: lib/Rex/CMDB/Base.pm version: v1.6.0 Rex::CMDB::YAML: file: lib/Rex/CMDB/YAML.pm version: v1.6.0 Rex::Cloud: file: lib/Rex/Cloud.pm version: v1.6.0 Rex::Cloud::Amazon: file: lib/Rex/Cloud/Amazon.pm version: v1.6.0 Rex::Cloud::Base: file: lib/Rex/Cloud/Base.pm version: v1.6.0 Rex::Cloud::Jiffybox: file: lib/Rex/Cloud/Jiffybox.pm version: v1.6.0 Rex::Cloud::OpenStack: file: lib/Rex/Cloud/OpenStack.pm version: v1.6.0 Rex::Commands: file: lib/Rex/Commands.pm version: v1.6.0 Rex::Commands::Augeas: file: lib/Rex/Commands/Augeas.pm version: v1.6.0 Rex::Commands::Box: file: lib/Rex/Commands/Box.pm version: v1.6.0 Rex::Commands::Cloud: file: lib/Rex/Commands/Cloud.pm version: v1.6.0 Rex::Commands::Cron: file: lib/Rex/Commands/Cron.pm version: v1.6.0 Rex::Commands::DB: file: lib/Rex/Commands/DB.pm version: v1.6.0 Rex::Commands::Download: file: lib/Rex/Commands/Download.pm version: v1.6.0 Rex::Commands::File: file: lib/Rex/Commands/File.pm version: v1.6.0 Rex::Commands::Fs: file: lib/Rex/Commands/Fs.pm version: v1.6.0 Rex::Commands::Gather: file: lib/Rex/Commands/Gather.pm version: v1.6.0 Rex::Commands::Host: file: lib/Rex/Commands/Host.pm version: v1.6.0 Rex::Commands::Inventory: file: lib/Rex/Commands/Inventory.pm version: v1.6.0 Rex::Commands::Iptables: file: lib/Rex/Commands/Iptables.pm version: v1.6.0 Rex::Commands::JobControl: file: lib/Rex/Commands/JobControl.pm version: v1.6.0 Rex::Commands::Kernel: file: lib/Rex/Commands/Kernel.pm version: v1.6.0 Rex::Commands::LVM: file: lib/Rex/Commands/LVM.pm version: v1.6.0 Rex::Commands::MD5: file: lib/Rex/Commands/MD5.pm version: v1.6.0 Rex::Commands::Mkfs: file: lib/Rex/Commands/Mkfs.pm version: v1.6.0 Rex::Commands::Network: file: lib/Rex/Commands/Network.pm version: v1.6.0 Rex::Commands::Notify: file: lib/Rex/Commands/Notify.pm version: v1.6.0 Rex::Commands::Partition: file: lib/Rex/Commands/Partition.pm version: v1.6.0 Rex::Commands::Pkg: file: lib/Rex/Commands/Pkg.pm version: v1.6.0 Rex::Commands::PkgConf: file: lib/Rex/Commands/PkgConf.pm version: v1.6.0 Rex::Commands::Process: file: lib/Rex/Commands/Process.pm version: v1.6.0 Rex::Commands::Rsync: file: lib/Rex/Commands/Rsync.pm version: v1.6.0 Rex::Commands::Run: file: lib/Rex/Commands/Run.pm version: v1.6.0 Rex::Commands::SCM: file: lib/Rex/Commands/SCM.pm version: v1.6.0 Rex::Commands::Service: file: lib/Rex/Commands/Service.pm version: v1.6.0 Rex::Commands::SimpleCheck: file: lib/Rex/Commands/SimpleCheck.pm version: v1.6.0 Rex::Commands::Sync: file: lib/Rex/Commands/Sync.pm version: v1.6.0 Rex::Commands::Sysctl: file: lib/Rex/Commands/Sysctl.pm version: v1.6.0 Rex::Commands::Tail: file: lib/Rex/Commands/Tail.pm version: v1.6.0 Rex::Commands::Upload: file: lib/Rex/Commands/Upload.pm version: v1.6.0 Rex::Commands::User: file: lib/Rex/Commands/User.pm version: v1.6.0 Rex::Commands::Virtualization: file: lib/Rex/Commands/Virtualization.pm version: v1.6.0 Rex::Config: file: lib/Rex/Config.pm version: v1.6.0 Rex::Constants: file: lib/Rex/Constants.pm version: v1.6.0 Rex::Cron: file: lib/Rex/Cron.pm version: v1.6.0 Rex::Cron::Base: file: lib/Rex/Cron/Base.pm version: v1.6.0 Rex::Cron::FreeBSD: file: lib/Rex/Cron/FreeBSD.pm version: v1.6.0 Rex::Cron::Linux: file: lib/Rex/Cron/Linux.pm version: v1.6.0 Rex::Cron::SunOS: file: lib/Rex/Cron/SunOS.pm version: v1.6.0 Rex::Exporter: file: lib/Rex/Exporter.pm version: v1.6.0 Rex::FS::File: file: lib/Rex/FS/File.pm version: v1.6.0 Rex::File::Parser::Data: file: lib/Rex/File/Parser/Data.pm version: v1.6.0 Rex::File::Parser::Ini: file: lib/Rex/File/Parser/Ini.pm version: v1.6.0 Rex::Fork::Manager: file: lib/Rex/Fork/Manager.pm version: v1.6.0 Rex::Fork::Task: file: lib/Rex/Fork/Task.pm version: v1.6.0 Rex::Group: file: lib/Rex/Group.pm version: v1.6.0 Rex::Group::Entry::Server: file: lib/Rex/Group/Entry/Server.pm version: v1.6.0 Rex::Group::Lookup::Command: file: lib/Rex/Group/Lookup/Command.pm version: v1.6.0 Rex::Group::Lookup::DBI: file: lib/Rex/Group/Lookup/DBI.pm version: v1.6.0 Rex::Group::Lookup::File: file: lib/Rex/Group/Lookup/File.pm version: v1.6.0 Rex::Group::Lookup::INI: file: lib/Rex/Group/Lookup/INI.pm version: v1.6.0 Rex::Group::Lookup::XML: file: lib/Rex/Group/Lookup/XML.pm version: v1.6.0 Rex::Group::Lookup::YAML: file: lib/Rex/Group/Lookup/YAML.pm version: v1.6.0 Rex::Hardware: file: lib/Rex/Hardware.pm version: v1.6.0 Rex::Hardware::Host: file: lib/Rex/Hardware/Host.pm version: v1.6.0 Rex::Hardware::Kernel: file: lib/Rex/Hardware/Kernel.pm version: v1.6.0 Rex::Hardware::Memory: file: lib/Rex/Hardware/Memory.pm version: v1.6.0 Rex::Hardware::Network: file: lib/Rex/Hardware/Network.pm version: v1.6.0 Rex::Hardware::Network::Darwin: file: lib/Rex/Hardware/Network/Darwin.pm version: v1.6.0 Rex::Hardware::Network::FreeBSD: file: lib/Rex/Hardware/Network/FreeBSD.pm version: v1.6.0 Rex::Hardware::Network::Linux: file: lib/Rex/Hardware/Network/Linux.pm version: v1.6.0 Rex::Hardware::Network::NetBSD: file: lib/Rex/Hardware/Network/NetBSD.pm version: v1.6.0 Rex::Hardware::Network::OpenBSD: file: lib/Rex/Hardware/Network/OpenBSD.pm version: v1.6.0 Rex::Hardware::Network::Solaris: file: lib/Rex/Hardware/Network/Solaris.pm version: v1.6.0 Rex::Hardware::Swap: file: lib/Rex/Hardware/Swap.pm version: v1.6.0 Rex::Hardware::VirtInfo: file: lib/Rex/Hardware/VirtInfo.pm version: v1.6.0 Rex::Helper::Array: file: lib/Rex/Helper/Array.pm version: v1.6.0 Rex::Helper::DBI: file: lib/Rex/Helper/DBI.pm version: v1.6.0 Rex::Helper::Encode: file: lib/Rex/Helper/Encode.pm version: v1.6.0 Rex::Helper::File::Spec: file: lib/Rex/Helper/File/Spec.pm version: v1.6.0 Rex::Helper::File::Stat: file: lib/Rex/Helper/File/Stat.pm version: v1.6.0 Rex::Helper::File::Stat::Unix: file: lib/Rex/Helper/File/Stat/Unix.pm version: v1.6.0 Rex::Helper::File::Stat::Win32: file: lib/Rex/Helper/File/Stat/Win32.pm version: v1.6.0 Rex::Helper::Hash: file: lib/Rex/Helper/Hash.pm version: v1.6.0 Rex::Helper::INI: file: lib/Rex/Helper/INI.pm version: v1.6.0 Rex::Helper::IP: file: lib/Rex/Helper/IP.pm version: v1.6.0 Rex::Helper::Misc: file: lib/Rex/Helper/Misc.pm version: v1.6.0 Rex::Helper::Path: file: lib/Rex/Helper/Path.pm version: v1.6.0 Rex::Helper::Rexfile::ParamLookup: file: lib/Rex/Helper/Rexfile/ParamLookup.pm version: v1.6.0 Rex::Helper::Run: file: lib/Rex/Helper/Run.pm version: v1.6.0 Rex::Helper::SSH2: file: lib/Rex/Helper/SSH2.pm version: v1.6.0 Rex::Helper::SSH2::Expect: file: lib/Rex/Helper/SSH2/Expect.pm version: v1.6.0 Rex::Helper::System: file: lib/Rex/Helper/System.pm version: v1.6.0 Rex::Helper::URI: file: lib/Rex/Helper/URI.pm version: v1.6.0 Rex::Helper::UserAgent: file: lib/Rex/Helper/UserAgent.pm version: v1.6.0 Rex::Hook: file: lib/Rex/Hook.pm version: v1.6.0 Rex::Interface::Cache: file: lib/Rex/Interface/Cache.pm version: v1.6.0 Rex::Interface::Cache::Base: file: lib/Rex/Interface/Cache/Base.pm version: v1.6.0 Rex::Interface::Cache::YAML: file: lib/Rex/Interface/Cache/YAML.pm version: v1.6.0 Rex::Interface::Connection: file: lib/Rex/Interface/Connection.pm version: v1.6.0 Rex::Interface::Connection::Base: file: lib/Rex/Interface/Connection/Base.pm version: v1.6.0 Rex::Interface::Connection::Fake: file: lib/Rex/Interface/Connection/Fake.pm version: v1.6.0 Rex::Interface::Connection::HTTP: file: lib/Rex/Interface/Connection/HTTP.pm version: v1.6.0 Rex::Interface::Connection::HTTPS: file: lib/Rex/Interface/Connection/HTTPS.pm version: v1.6.0 Rex::Interface::Connection::Local: file: lib/Rex/Interface/Connection/Local.pm version: v1.6.0 Rex::Interface::Connection::OpenSSH: file: lib/Rex/Interface/Connection/OpenSSH.pm version: v1.6.0 Rex::Interface::Connection::SSH: file: lib/Rex/Interface/Connection/SSH.pm version: v1.6.0 Rex::Interface::Exec: file: lib/Rex/Interface/Exec.pm version: v1.6.0 Rex::Interface::Exec::Base: file: lib/Rex/Interface/Exec/Base.pm version: v1.6.0 Rex::Interface::Exec::HTTP: file: lib/Rex/Interface/Exec/HTTP.pm version: v1.6.0 Rex::Interface::Exec::IOReader: file: lib/Rex/Interface/Exec/IOReader.pm version: v1.6.0 Rex::Interface::Exec::Local: file: lib/Rex/Interface/Exec/Local.pm version: v1.6.0 Rex::Interface::Exec::OpenSSH: file: lib/Rex/Interface/Exec/OpenSSH.pm version: v1.6.0 Rex::Interface::Exec::SSH: file: lib/Rex/Interface/Exec/SSH.pm version: v1.6.0 Rex::Interface::Exec::Sudo: file: lib/Rex/Interface/Exec/Sudo.pm version: v1.6.0 Rex::Interface::Executor: file: lib/Rex/Interface/Executor.pm version: v1.6.0 Rex::Interface::Executor::Base: file: lib/Rex/Interface/Executor/Base.pm version: v1.6.0 Rex::Interface::Executor::Default: file: lib/Rex/Interface/Executor/Default.pm version: v1.6.0 Rex::Interface::File: file: lib/Rex/Interface/File.pm version: v1.6.0 Rex::Interface::File::Base: file: lib/Rex/Interface/File/Base.pm version: v1.6.0 Rex::Interface::File::HTTP: file: lib/Rex/Interface/File/HTTP.pm version: v1.6.0 Rex::Interface::File::Local: file: lib/Rex/Interface/File/Local.pm version: v1.6.0 Rex::Interface::File::OpenSSH: file: lib/Rex/Interface/File/OpenSSH.pm version: v1.6.0 Rex::Interface::File::SSH: file: lib/Rex/Interface/File/SSH.pm version: v1.6.0 Rex::Interface::File::Sudo: file: lib/Rex/Interface/File/Sudo.pm version: v1.6.0 Rex::Interface::Fs: file: lib/Rex/Interface/Fs.pm version: v1.6.0 Rex::Interface::Fs::Base: file: lib/Rex/Interface/Fs/Base.pm version: v1.6.0 Rex::Interface::Fs::HTTP: file: lib/Rex/Interface/Fs/HTTP.pm version: v1.6.0 Rex::Interface::Fs::Local: file: lib/Rex/Interface/Fs/Local.pm version: v1.6.0 Rex::Interface::Fs::OpenSSH: file: lib/Rex/Interface/Fs/OpenSSH.pm version: v1.6.0 Rex::Interface::Fs::SSH: file: lib/Rex/Interface/Fs/SSH.pm version: v1.6.0 Rex::Interface::Fs::Sudo: file: lib/Rex/Interface/Fs/Sudo.pm version: v1.6.0 Rex::Interface::Shell: file: lib/Rex/Interface/Shell.pm version: v1.6.0 Rex::Interface::Shell::Ash: file: lib/Rex/Interface/Shell/Ash.pm version: v1.6.0 Rex::Interface::Shell::Base: file: lib/Rex/Interface/Shell/Base.pm version: v1.6.0 Rex::Interface::Shell::Bash: file: lib/Rex/Interface/Shell/Bash.pm version: v1.6.0 Rex::Interface::Shell::Csh: file: lib/Rex/Interface/Shell/Csh.pm version: v1.6.0 Rex::Interface::Shell::Default: file: lib/Rex/Interface/Shell/Default.pm version: v1.6.0 Rex::Interface::Shell::Idrac: file: lib/Rex/Interface/Shell/Idrac.pm version: v1.6.0 Rex::Interface::Shell::Ksh: file: lib/Rex/Interface/Shell/Ksh.pm version: v1.6.0 Rex::Interface::Shell::Sh: file: lib/Rex/Interface/Shell/Sh.pm version: v1.6.0 Rex::Interface::Shell::Tcsh: file: lib/Rex/Interface/Shell/Tcsh.pm version: v1.6.0 Rex::Interface::Shell::Zsh: file: lib/Rex/Interface/Shell/Zsh.pm version: v1.6.0 Rex::Inventory: file: lib/Rex/Inventory.pm version: v1.6.0 Rex::Inventory::Bios: file: lib/Rex/Inventory/Bios.pm version: v1.6.0 Rex::Inventory::DMIDecode: file: lib/Rex/Inventory/DMIDecode.pm version: v1.6.0 Rex::Inventory::DMIDecode::BaseBoard: file: lib/Rex/Inventory/DMIDecode/BaseBoard.pm version: v1.6.0 Rex::Inventory::DMIDecode::Bios: file: lib/Rex/Inventory/DMIDecode/Bios.pm version: v1.6.0 Rex::Inventory::DMIDecode::CPU: file: lib/Rex/Inventory/DMIDecode/CPU.pm version: v1.6.0 Rex::Inventory::DMIDecode::Memory: file: lib/Rex/Inventory/DMIDecode/Memory.pm version: v1.6.0 Rex::Inventory::DMIDecode::MemoryArray: file: lib/Rex/Inventory/DMIDecode/MemoryArray.pm version: v1.6.0 Rex::Inventory::DMIDecode::Section: file: lib/Rex/Inventory/DMIDecode/Section.pm version: v1.6.0 Rex::Inventory::DMIDecode::SystemInformation: file: lib/Rex/Inventory/DMIDecode/SystemInformation.pm version: v1.6.0 Rex::Inventory::HP::ACU: file: lib/Rex/Inventory/HP/ACU.pm version: v1.6.0 Rex::Inventory::Hal: file: lib/Rex/Inventory/Hal.pm version: v1.6.0 Rex::Inventory::Hal::Object: file: lib/Rex/Inventory/Hal/Object.pm version: v1.6.0 Rex::Inventory::Hal::Object::Net: file: lib/Rex/Inventory/Hal/Object/Net.pm version: v1.6.0 Rex::Inventory::Hal::Object::Storage: file: lib/Rex/Inventory/Hal/Object/Storage.pm version: v1.6.0 Rex::Inventory::Hal::Object::Volume: file: lib/Rex/Inventory/Hal/Object/Volume.pm version: v1.6.0 Rex::Inventory::Proc: file: lib/Rex/Inventory/Proc.pm version: v1.6.0 Rex::Inventory::Proc::Cpuinfo: file: lib/Rex/Inventory/Proc/Cpuinfo.pm version: v1.6.0 Rex::Inventory::SMBios: file: lib/Rex/Inventory/SMBios.pm version: v1.6.0 Rex::Inventory::SMBios::BaseBoard: file: lib/Rex/Inventory/SMBios/BaseBoard.pm version: v1.6.0 Rex::Inventory::SMBios::Bios: file: lib/Rex/Inventory/SMBios/Bios.pm version: v1.6.0 Rex::Inventory::SMBios::CPU: file: lib/Rex/Inventory/SMBios/CPU.pm version: v1.6.0 Rex::Inventory::SMBios::Memory: file: lib/Rex/Inventory/SMBios/Memory.pm version: v1.6.0 Rex::Inventory::SMBios::MemoryArray: file: lib/Rex/Inventory/SMBios/MemoryArray.pm version: v1.6.0 Rex::Inventory::SMBios::Section: file: lib/Rex/Inventory/SMBios/Section.pm version: v1.6.0 Rex::Inventory::SMBios::SystemInformation: file: lib/Rex/Inventory/SMBios/SystemInformation.pm version: v1.6.0 Rex::Logger: file: lib/Rex/Logger.pm version: v1.6.0 Rex::Notify: file: lib/Rex/Notify.pm version: v1.6.0 Rex::Output: file: lib/Rex/Output.pm version: v1.6.0 Rex::Output::Base: file: lib/Rex/Output/Base.pm version: v1.6.0 Rex::Output::JUnit: file: lib/Rex/Output/JUnit.pm version: v1.6.0 Rex::Pkg: file: lib/Rex/Pkg.pm version: v1.6.0 Rex::Pkg::ALT: file: lib/Rex/Pkg/ALT.pm version: v1.6.0 Rex::Pkg::Arch: file: lib/Rex/Pkg/Arch.pm version: v1.6.0 Rex::Pkg::Base: file: lib/Rex/Pkg/Base.pm version: v1.6.0 Rex::Pkg::Debian: file: lib/Rex/Pkg/Debian.pm version: v1.6.0 Rex::Pkg::FreeBSD: file: lib/Rex/Pkg/FreeBSD.pm version: v1.6.0 Rex::Pkg::Gentoo: file: lib/Rex/Pkg/Gentoo.pm version: v1.6.0 Rex::Pkg::Mageia: file: lib/Rex/Pkg/Mageia.pm version: v1.6.0 Rex::Pkg::NetBSD: file: lib/Rex/Pkg/NetBSD.pm version: v1.6.0 Rex::Pkg::OpenBSD: file: lib/Rex/Pkg/OpenBSD.pm version: v1.6.0 Rex::Pkg::OpenWrt: file: lib/Rex/Pkg/OpenWrt.pm version: v1.6.0 Rex::Pkg::Redhat: file: lib/Rex/Pkg/Redhat.pm version: v1.6.0 Rex::Pkg::SuSE: file: lib/Rex/Pkg/SuSE.pm version: v1.6.0 Rex::Pkg::SunOS: file: lib/Rex/Pkg/SunOS.pm version: v1.6.0 Rex::Pkg::SunOS::OpenCSW: file: lib/Rex/Pkg/SunOS/OpenCSW.pm version: v1.6.0 Rex::Pkg::SunOS::pkg: file: lib/Rex/Pkg/SunOS/pkg.pm version: v1.6.0 Rex::Pkg::Ubuntu: file: lib/Rex/Pkg/Ubuntu.pm version: v1.6.0 Rex::PkgConf: file: lib/Rex/PkgConf.pm version: v1.6.0 Rex::PkgConf::Base: file: lib/Rex/PkgConf/Base.pm version: v1.6.0 Rex::PkgConf::Debian: file: lib/Rex/PkgConf/Debian.pm version: v1.6.0 Rex::Profiler: file: lib/Rex/Profiler.pm version: v1.6.0 Rex::Report: file: lib/Rex/Report.pm version: v1.6.0 Rex::Report::Base: file: lib/Rex/Report/Base.pm version: v1.6.0 Rex::Report::YAML: file: lib/Rex/Report/YAML.pm version: v1.6.0 Rex::Require: file: lib/Rex/Require.pm version: v1.6.0 Rex::Resource: file: lib/Rex/Resource.pm version: v1.6.0 Rex::Resource::Common: file: lib/Rex/Resource/Common.pm version: v1.6.0 Rex::Resource::firewall: file: lib/Rex/Resource/firewall.pm version: v1.6.0 Rex::Resource::firewall::Provider::base: file: lib/Rex/Resource/firewall/Provider/base.pm version: v1.6.0 Rex::Resource::firewall::Provider::iptables: file: lib/Rex/Resource/firewall/Provider/iptables.pm version: v1.6.0 Rex::Resource::firewall::Provider::ufw: file: lib/Rex/Resource/firewall/Provider/ufw.pm version: v1.6.0 Rex::RunList: file: lib/Rex/RunList.pm version: v1.6.0 Rex::SCM::Git: file: lib/Rex/SCM/Git.pm version: v1.6.0 Rex::SCM::Subversion: file: lib/Rex/SCM/Subversion.pm version: v1.6.0 Rex::Service: file: lib/Rex/Service.pm version: v1.6.0 Rex::Service::ALT: file: lib/Rex/Service/ALT.pm version: v1.6.0 Rex::Service::ALT::systemd: file: lib/Rex/Service/ALT/systemd.pm version: v1.6.0 Rex::Service::Arch::systemd: file: lib/Rex/Service/Arch/systemd.pm version: v1.6.0 Rex::Service::Base: file: lib/Rex/Service/Base.pm version: v1.6.0 Rex::Service::Debian: file: lib/Rex/Service/Debian.pm version: v1.6.0 Rex::Service::Debian::systemd: file: lib/Rex/Service/Debian/systemd.pm version: v1.6.0 Rex::Service::FreeBSD: file: lib/Rex/Service/FreeBSD.pm version: v1.6.0 Rex::Service::Gentoo: file: lib/Rex/Service/Gentoo.pm version: v1.6.0 Rex::Service::Gentoo::systemd: file: lib/Rex/Service/Gentoo/systemd.pm version: v1.6.0 Rex::Service::Mageia: file: lib/Rex/Service/Mageia.pm version: v1.6.0 Rex::Service::Mageia::systemd: file: lib/Rex/Service/Mageia/systemd.pm version: v1.6.0 Rex::Service::NetBSD: file: lib/Rex/Service/NetBSD.pm version: v1.6.0 Rex::Service::OpenBSD: file: lib/Rex/Service/OpenBSD.pm version: v1.6.0 Rex::Service::OpenWrt: file: lib/Rex/Service/OpenWrt.pm version: v1.6.0 Rex::Service::Redhat: file: lib/Rex/Service/Redhat.pm version: v1.6.0 Rex::Service::Redhat::systemd: file: lib/Rex/Service/Redhat/systemd.pm version: v1.6.0 Rex::Service::SuSE: file: lib/Rex/Service/SuSE.pm version: v1.6.0 Rex::Service::SuSE::systemd: file: lib/Rex/Service/SuSE/systemd.pm version: v1.6.0 Rex::Service::SunOS: file: lib/Rex/Service/SunOS.pm version: v1.6.0 Rex::Service::SunOS::svcadm: file: lib/Rex/Service/SunOS/svcadm.pm version: v1.6.0 Rex::Service::Ubuntu: file: lib/Rex/Service/Ubuntu.pm version: v1.6.0 Rex::Shared::Var: file: lib/Rex/Shared/Var.pm version: v1.6.0 Rex::Shared::Var::Array: file: lib/Rex/Shared/Var/Array.pm version: v1.6.0 Rex::Shared::Var::Common: file: lib/Rex/Shared/Var/Common.pm version: v1.6.0 Rex::Shared::Var::Hash: file: lib/Rex/Shared/Var/Hash.pm version: v1.6.0 Rex::Shared::Var::Scalar: file: lib/Rex/Shared/Var/Scalar.pm version: v1.6.0 Rex::Sudo::File: file: lib/Rex/Sudo/File.pm version: v1.6.0 Rex::Task: file: lib/Rex/Task.pm version: v1.6.0 Rex::TaskList: file: lib/Rex/TaskList.pm version: v1.6.0 Rex::TaskList::Base: file: lib/Rex/TaskList/Base.pm version: v1.6.0 Rex::TaskList::Parallel_ForkManager: file: lib/Rex/TaskList/Parallel_ForkManager.pm version: v1.6.0 Rex::Template: file: lib/Rex/Template.pm version: v1.6.0 Rex::Template::NG: file: lib/Rex/Template/NG.pm version: v1.6.0 Rex::Test: file: lib/Rex/Test.pm version: v1.6.0 Rex::Test::Base: file: lib/Rex/Test/Base.pm version: v1.6.0 Rex::Test::Base::has_content: file: lib/Rex/Test/Base/has_content.pm version: v1.6.0 Rex::Test::Base::has_cron: file: lib/Rex/Test/Base/has_cron.pm version: v1.6.0 Rex::Test::Base::has_cron_env: file: lib/Rex/Test/Base/has_cron_env.pm version: v1.6.0 Rex::Test::Base::has_dir: file: lib/Rex/Test/Base/has_dir.pm version: v1.6.0 Rex::Test::Base::has_file: file: lib/Rex/Test/Base/has_file.pm version: v1.6.0 Rex::Test::Base::has_file_content: file: lib/Rex/Test/Base/has_file_content.pm version: v1.6.0 Rex::Test::Base::has_output: file: lib/Rex/Test/Base/has_output.pm version: v1.6.0 Rex::Test::Base::has_output_matching: file: lib/Rex/Test/Base/has_output_matching.pm version: v1.6.0 Rex::Test::Base::has_package: file: lib/Rex/Test/Base/has_package.pm version: v1.6.0 Rex::Test::Base::has_service_running: file: lib/Rex/Test/Base/has_service_running.pm version: v1.6.0 Rex::Test::Base::has_service_stopped: file: lib/Rex/Test/Base/has_service_stopped.pm version: v1.6.0 Rex::Test::Base::has_stat: file: lib/Rex/Test/Base/has_stat.pm version: v1.6.0 Rex::Transaction: file: lib/Rex/Transaction.pm version: v1.6.0 Rex::User: file: lib/Rex/User.pm version: v1.6.0 Rex::User::Base: file: lib/Rex/User/Base.pm version: v1.6.0 Rex::User::FreeBSD: file: lib/Rex/User/FreeBSD.pm version: v1.6.0 Rex::User::Linux: file: lib/Rex/User/Linux.pm version: v1.6.0 Rex::User::NetBSD: file: lib/Rex/User/NetBSD.pm version: v1.6.0 Rex::User::OpenBSD: file: lib/Rex/User/OpenBSD.pm version: v1.6.0 Rex::User::OpenWrt: file: lib/Rex/User/OpenWrt.pm version: v1.6.0 Rex::User::SunOS: file: lib/Rex/User/SunOS.pm version: v1.6.0 Rex::Value: file: lib/Rex/Value.pm version: v1.6.0 Rex::Virtualization: file: lib/Rex/Virtualization.pm version: v1.6.0 Rex::Virtualization::Base: file: lib/Rex/Virtualization/Base.pm version: v1.6.0 Rex::Virtualization::Docker: file: lib/Rex/Virtualization/Docker.pm version: v1.6.0 Rex::Virtualization::Docker::create: file: lib/Rex/Virtualization/Docker/create.pm version: v1.6.0 Rex::Virtualization::Docker::daemon: file: lib/Rex/Virtualization/Docker/daemon.pm version: v1.6.0 Rex::Virtualization::Docker::delete: file: lib/Rex/Virtualization/Docker/delete.pm version: v1.6.0 Rex::Virtualization::Docker::destroy: file: lib/Rex/Virtualization/Docker/destroy.pm version: v1.6.0 Rex::Virtualization::Docker::guestinfo: file: lib/Rex/Virtualization/Docker/guestinfo.pm version: v1.6.0 Rex::Virtualization::Docker::images: file: lib/Rex/Virtualization/Docker/images.pm version: v1.6.0 Rex::Virtualization::Docker::import: file: lib/Rex/Virtualization/Docker/import.pm version: v1.6.0 Rex::Virtualization::Docker::info: file: lib/Rex/Virtualization/Docker/info.pm version: v1.6.0 Rex::Virtualization::Docker::list: file: lib/Rex/Virtualization/Docker/list.pm version: v1.6.0 Rex::Virtualization::Docker::reboot: file: lib/Rex/Virtualization/Docker/reboot.pm version: v1.6.0 Rex::Virtualization::Docker::shutdown: file: lib/Rex/Virtualization/Docker/shutdown.pm version: v1.6.0 Rex::Virtualization::Docker::start: file: lib/Rex/Virtualization/Docker/start.pm version: v1.6.0 Rex::Virtualization::Docker::status: file: lib/Rex/Virtualization/Docker/status.pm version: v1.6.0 Rex::Virtualization::LibVirt: file: lib/Rex/Virtualization/LibVirt.pm version: v1.6.0 Rex::Virtualization::LibVirt::blklist: file: lib/Rex/Virtualization/LibVirt/blklist.pm version: v1.6.0 Rex::Virtualization::LibVirt::clone: file: lib/Rex/Virtualization/LibVirt/clone.pm version: v1.6.0 Rex::Virtualization::LibVirt::create: file: lib/Rex/Virtualization/LibVirt/create.pm version: v1.6.0 Rex::Virtualization::LibVirt::delete: file: lib/Rex/Virtualization/LibVirt/delete.pm version: v1.6.0 Rex::Virtualization::LibVirt::destroy: file: lib/Rex/Virtualization/LibVirt/destroy.pm version: v1.6.0 Rex::Virtualization::LibVirt::dumpxml: file: lib/Rex/Virtualization/LibVirt/dumpxml.pm version: v1.6.0 Rex::Virtualization::LibVirt::guestinfo: file: lib/Rex/Virtualization/LibVirt/guestinfo.pm version: v1.6.0 Rex::Virtualization::LibVirt::hypervisor: file: lib/Rex/Virtualization/LibVirt/hypervisor.pm version: v1.6.0 Rex::Virtualization::LibVirt::iflist: file: lib/Rex/Virtualization/LibVirt/iflist.pm version: v1.6.0 Rex::Virtualization::LibVirt::import: file: lib/Rex/Virtualization/LibVirt/import.pm version: v1.6.0 Rex::Virtualization::LibVirt::info: file: lib/Rex/Virtualization/LibVirt/info.pm version: v1.6.0 Rex::Virtualization::LibVirt::list: file: lib/Rex/Virtualization/LibVirt/list.pm version: v1.6.0 Rex::Virtualization::LibVirt::option: file: lib/Rex/Virtualization/LibVirt/option.pm version: v1.6.0 Rex::Virtualization::LibVirt::reboot: file: lib/Rex/Virtualization/LibVirt/reboot.pm version: v1.6.0 Rex::Virtualization::LibVirt::shutdown: file: lib/Rex/Virtualization/LibVirt/shutdown.pm version: v1.6.0 Rex::Virtualization::LibVirt::start: file: lib/Rex/Virtualization/LibVirt/start.pm version: v1.6.0 Rex::Virtualization::LibVirt::status: file: lib/Rex/Virtualization/LibVirt/status.pm version: v1.6.0 Rex::Virtualization::LibVirt::vncdisplay: file: lib/Rex/Virtualization/LibVirt/vncdisplay.pm version: v1.6.0 Rex::Virtualization::Lxc: file: lib/Rex/Virtualization/Lxc.pm version: v1.6.0 Rex::Virtualization::Lxc::attach: file: lib/Rex/Virtualization/Lxc/attach.pm version: v1.6.0 Rex::Virtualization::Lxc::copy: file: lib/Rex/Virtualization/Lxc/copy.pm version: v1.6.0 Rex::Virtualization::Lxc::create: file: lib/Rex/Virtualization/Lxc/create.pm version: v1.6.0 Rex::Virtualization::Lxc::destroy: file: lib/Rex/Virtualization/Lxc/destroy.pm version: v1.6.0 Rex::Virtualization::Lxc::info: file: lib/Rex/Virtualization/Lxc/info.pm version: v1.6.0 Rex::Virtualization::Lxc::list: file: lib/Rex/Virtualization/Lxc/list.pm version: v1.6.0 Rex::Virtualization::Lxc::start: file: lib/Rex/Virtualization/Lxc/start.pm version: v1.6.0 Rex::Virtualization::Lxc::stop: file: lib/Rex/Virtualization/Lxc/stop.pm version: v1.6.0 Rex::Virtualization::VBox: file: lib/Rex/Virtualization/VBox.pm version: v1.6.0 Rex::Virtualization::VBox::bridge: file: lib/Rex/Virtualization/VBox/bridge.pm version: v1.6.0 Rex::Virtualization::VBox::create: file: lib/Rex/Virtualization/VBox/create.pm version: v1.6.0 Rex::Virtualization::VBox::delete: file: lib/Rex/Virtualization/VBox/delete.pm version: v1.6.0 Rex::Virtualization::VBox::destroy: file: lib/Rex/Virtualization/VBox/destroy.pm version: v1.6.0 Rex::Virtualization::VBox::forward_port: file: lib/Rex/Virtualization/VBox/forward_port.pm version: v1.6.0 Rex::Virtualization::VBox::guestinfo: file: lib/Rex/Virtualization/VBox/guestinfo.pm version: v1.6.0 Rex::Virtualization::VBox::import: file: lib/Rex/Virtualization/VBox/import.pm version: v1.6.0 Rex::Virtualization::VBox::info: file: lib/Rex/Virtualization/VBox/info.pm version: v1.6.0 Rex::Virtualization::VBox::list: file: lib/Rex/Virtualization/VBox/list.pm version: v1.6.0 Rex::Virtualization::VBox::option: file: lib/Rex/Virtualization/VBox/option.pm version: v1.6.0 Rex::Virtualization::VBox::reboot: file: lib/Rex/Virtualization/VBox/reboot.pm version: v1.6.0 Rex::Virtualization::VBox::share_folder: file: lib/Rex/Virtualization/VBox/share_folder.pm version: v1.6.0 Rex::Virtualization::VBox::shutdown: file: lib/Rex/Virtualization/VBox/shutdown.pm version: v1.6.0 Rex::Virtualization::VBox::start: file: lib/Rex/Virtualization/VBox/start.pm version: v1.6.0 Rex::Virtualization::VBox::status: file: lib/Rex/Virtualization/VBox/status.pm version: v1.6.0 requires: AWS::Signature4: '0' Carp: '0' Cwd: '0' Data::Dumper: '0' Data::Validate::IP: '0' Devel::Caller: '0' Digest::HMAC_SHA1: '0' Digest::MD5: '0' Exporter: '0' Fcntl: '0' File::Basename: '0' File::Spec: '0' File::Spec::Unix: '0' File::Spec::Win32: '0' FindBin: '0' HTTP::Request: '0' HTTP::Request::Common: '0' Hash::Merge: '0' IO::File: '0' IO::Select: '0' IO::Socket: '0' IO::String: '0' IPC::Open3: '0' JSON::MaybeXS: '0' LWP::UserAgent: '0' List::MoreUtils: '0' List::Util: '0' MIME::Base64: '0' Net::OpenSSH::ShellQuoter: '0' POSIX: '0' Scalar::Util: '0' Sort::Naturally: '0' Storable: '0' Symbol: '0' Term::ReadKey: '0' Test::Builder::Module: '0' Text::Glob: '0' Text::Wrap: '0' Time::HiRes: '0' UNIVERSAL: '0' URI: '0' URI::QueryParam: '0' XML::Simple: '0' YAML: '0' attributes: '0' base: '0' constant: '0' lib: '0' overload: '0' perl: '5.008008' strict: '0' vars: '0' version: '0' warnings: '0' resources: IRC: irc://irc.freenode.net/rex Twitter: https://twitter.com/RexOps bugtracker: https://github.com/RexOps/Rex/issues homepage: http://www.rexify.org repository: https://github.com/RexOps/Rex.git version: 1.6.0 x_serialization_backend: 'YAML::Tiny version 1.70' MANIFEST100644001751001751 2701713232707066 12230 0ustar00janjan000000000000Rex-1.6.0# This file was automatically generated by Dist::Zilla::Plugin::Manifest v6.010. CONTRIBUTORS ChangeLog LICENSE MANIFEST META.json META.yml Makefile.PL README bin/rex bin/rexify dist.ini lib/Rex.pm lib/Rex/Args.pm lib/Rex/Args/Integer.pm lib/Rex/Args/Single.pm lib/Rex/Args/String.pm lib/Rex/Batch.pm lib/Rex/Box.pm lib/Rex/Box/Amazon.pm lib/Rex/Box/Base.pm lib/Rex/Box/Docker.pm lib/Rex/Box/KVM.pm lib/Rex/Box/VBox.pm lib/Rex/CLI.pm lib/Rex/CMDB.pm lib/Rex/CMDB/Base.pm lib/Rex/CMDB/YAML.pm lib/Rex/Cloud.pm lib/Rex/Cloud/Amazon.pm lib/Rex/Cloud/Base.pm lib/Rex/Cloud/Jiffybox.pm lib/Rex/Cloud/OpenStack.pm lib/Rex/Commands.pm lib/Rex/Commands/Augeas.pm lib/Rex/Commands/Box.pm lib/Rex/Commands/Cloud.pm lib/Rex/Commands/Cron.pm lib/Rex/Commands/DB.pm lib/Rex/Commands/Download.pm lib/Rex/Commands/File.pm lib/Rex/Commands/Fs.pm lib/Rex/Commands/Gather.pm lib/Rex/Commands/Host.pm lib/Rex/Commands/Inventory.pm lib/Rex/Commands/Iptables.pm lib/Rex/Commands/JobControl.pm lib/Rex/Commands/Kernel.pm lib/Rex/Commands/LVM.pm lib/Rex/Commands/MD5.pm lib/Rex/Commands/Mkfs.pm lib/Rex/Commands/Network.pm lib/Rex/Commands/Notify.pm lib/Rex/Commands/Partition.pm lib/Rex/Commands/Pkg.pm lib/Rex/Commands/PkgConf.pm lib/Rex/Commands/Process.pm lib/Rex/Commands/Rsync.pm lib/Rex/Commands/Run.pm lib/Rex/Commands/SCM.pm lib/Rex/Commands/Service.pm lib/Rex/Commands/SimpleCheck.pm lib/Rex/Commands/Sync.pm lib/Rex/Commands/Sysctl.pm lib/Rex/Commands/Tail.pm lib/Rex/Commands/Upload.pm lib/Rex/Commands/User.pm lib/Rex/Commands/Virtualization.pm lib/Rex/Commands/templates/append_if_no_such_line.tpl.pl lib/Rex/Config.pm lib/Rex/Constants.pm lib/Rex/Cron.pm lib/Rex/Cron/Base.pm lib/Rex/Cron/FreeBSD.pm lib/Rex/Cron/Linux.pm lib/Rex/Cron/SunOS.pm lib/Rex/Exporter.pm lib/Rex/FS/File.pm lib/Rex/File/Parser/Data.pm lib/Rex/File/Parser/Ini.pm lib/Rex/Fork/Manager.pm lib/Rex/Fork/Task.pm lib/Rex/Group.pm lib/Rex/Group/Entry/Server.pm lib/Rex/Group/Lookup/Command.pm lib/Rex/Group/Lookup/DBI.pm lib/Rex/Group/Lookup/File.pm lib/Rex/Group/Lookup/INI.pm lib/Rex/Group/Lookup/XML.pm lib/Rex/Group/Lookup/YAML.pm lib/Rex/Hardware.pm lib/Rex/Hardware/Host.pm lib/Rex/Hardware/Kernel.pm lib/Rex/Hardware/Memory.pm lib/Rex/Hardware/Network.pm lib/Rex/Hardware/Network/Darwin.pm lib/Rex/Hardware/Network/FreeBSD.pm lib/Rex/Hardware/Network/Linux.pm lib/Rex/Hardware/Network/NetBSD.pm lib/Rex/Hardware/Network/OpenBSD.pm lib/Rex/Hardware/Network/Solaris.pm lib/Rex/Hardware/Swap.pm lib/Rex/Hardware/VirtInfo.pm lib/Rex/Helper/Array.pm lib/Rex/Helper/DBI.pm lib/Rex/Helper/Encode.pm lib/Rex/Helper/File/Spec.pm lib/Rex/Helper/File/Stat.pm lib/Rex/Helper/File/Stat/Unix.pm lib/Rex/Helper/File/Stat/Win32.pm lib/Rex/Helper/Hash.pm lib/Rex/Helper/INI.pm lib/Rex/Helper/IP.pm lib/Rex/Helper/Misc.pm lib/Rex/Helper/Path.pm lib/Rex/Helper/Rexfile/ParamLookup.pm lib/Rex/Helper/Run.pm lib/Rex/Helper/SSH2.pm lib/Rex/Helper/SSH2/Expect.pm lib/Rex/Helper/System.pm lib/Rex/Helper/URI.pm lib/Rex/Helper/UserAgent.pm lib/Rex/Hook.pm lib/Rex/Interface/Cache.pm lib/Rex/Interface/Cache/Base.pm lib/Rex/Interface/Cache/YAML.pm lib/Rex/Interface/Connection.pm lib/Rex/Interface/Connection/Base.pm lib/Rex/Interface/Connection/Fake.pm lib/Rex/Interface/Connection/HTTP.pm lib/Rex/Interface/Connection/HTTPS.pm lib/Rex/Interface/Connection/Local.pm lib/Rex/Interface/Connection/OpenSSH.pm lib/Rex/Interface/Connection/SSH.pm lib/Rex/Interface/Exec.pm lib/Rex/Interface/Exec/Base.pm lib/Rex/Interface/Exec/HTTP.pm lib/Rex/Interface/Exec/IOReader.pm lib/Rex/Interface/Exec/Local.pm lib/Rex/Interface/Exec/OpenSSH.pm lib/Rex/Interface/Exec/SSH.pm lib/Rex/Interface/Exec/Sudo.pm lib/Rex/Interface/Executor.pm lib/Rex/Interface/Executor/Base.pm lib/Rex/Interface/Executor/Default.pm lib/Rex/Interface/File.pm lib/Rex/Interface/File/Base.pm lib/Rex/Interface/File/HTTP.pm lib/Rex/Interface/File/Local.pm lib/Rex/Interface/File/OpenSSH.pm lib/Rex/Interface/File/SSH.pm lib/Rex/Interface/File/Sudo.pm lib/Rex/Interface/Fs.pm lib/Rex/Interface/Fs/Base.pm lib/Rex/Interface/Fs/HTTP.pm lib/Rex/Interface/Fs/Local.pm lib/Rex/Interface/Fs/OpenSSH.pm lib/Rex/Interface/Fs/SSH.pm lib/Rex/Interface/Fs/Sudo.pm lib/Rex/Interface/Shell.pm lib/Rex/Interface/Shell/Ash.pm lib/Rex/Interface/Shell/Base.pm lib/Rex/Interface/Shell/Bash.pm lib/Rex/Interface/Shell/Csh.pm lib/Rex/Interface/Shell/Default.pm lib/Rex/Interface/Shell/Idrac.pm lib/Rex/Interface/Shell/Ksh.pm lib/Rex/Interface/Shell/Sh.pm lib/Rex/Interface/Shell/Tcsh.pm lib/Rex/Interface/Shell/Zsh.pm lib/Rex/Inventory.pm lib/Rex/Inventory/Bios.pm lib/Rex/Inventory/DMIDecode.pm lib/Rex/Inventory/DMIDecode/BaseBoard.pm lib/Rex/Inventory/DMIDecode/Bios.pm lib/Rex/Inventory/DMIDecode/CPU.pm lib/Rex/Inventory/DMIDecode/Memory.pm lib/Rex/Inventory/DMIDecode/MemoryArray.pm lib/Rex/Inventory/DMIDecode/Section.pm lib/Rex/Inventory/DMIDecode/SystemInformation.pm lib/Rex/Inventory/HP/ACU.pm lib/Rex/Inventory/Hal.pm lib/Rex/Inventory/Hal/Object.pm lib/Rex/Inventory/Hal/Object/Net.pm lib/Rex/Inventory/Hal/Object/Storage.pm lib/Rex/Inventory/Hal/Object/Volume.pm lib/Rex/Inventory/Proc.pm lib/Rex/Inventory/Proc/Cpuinfo.pm lib/Rex/Inventory/SMBios.pm lib/Rex/Inventory/SMBios/BaseBoard.pm lib/Rex/Inventory/SMBios/Bios.pm lib/Rex/Inventory/SMBios/CPU.pm lib/Rex/Inventory/SMBios/Memory.pm lib/Rex/Inventory/SMBios/MemoryArray.pm lib/Rex/Inventory/SMBios/Section.pm lib/Rex/Inventory/SMBios/SystemInformation.pm lib/Rex/Logger.pm lib/Rex/Notify.pm lib/Rex/Output.pm lib/Rex/Output/Base.pm lib/Rex/Output/JUnit.pm lib/Rex/Pkg.pm lib/Rex/Pkg/ALT.pm lib/Rex/Pkg/Arch.pm lib/Rex/Pkg/Base.pm lib/Rex/Pkg/Debian.pm lib/Rex/Pkg/FreeBSD.pm lib/Rex/Pkg/Gentoo.pm lib/Rex/Pkg/Mageia.pm lib/Rex/Pkg/NetBSD.pm lib/Rex/Pkg/OpenBSD.pm lib/Rex/Pkg/OpenWrt.pm lib/Rex/Pkg/Redhat.pm lib/Rex/Pkg/SuSE.pm lib/Rex/Pkg/SunOS.pm lib/Rex/Pkg/SunOS/OpenCSW.pm lib/Rex/Pkg/SunOS/pkg.pm lib/Rex/Pkg/Ubuntu.pm lib/Rex/PkgConf.pm lib/Rex/PkgConf/Base.pm lib/Rex/PkgConf/Debian.pm lib/Rex/Profiler.pm lib/Rex/Report.pm lib/Rex/Report/Base.pm lib/Rex/Report/YAML.pm lib/Rex/Require.pm lib/Rex/Resource.pm lib/Rex/Resource/Common.pm lib/Rex/Resource/firewall.pm lib/Rex/Resource/firewall/Provider/base.pm lib/Rex/Resource/firewall/Provider/iptables.pm lib/Rex/Resource/firewall/Provider/ufw.pm lib/Rex/RunList.pm lib/Rex/SCM/Git.pm lib/Rex/SCM/Subversion.pm lib/Rex/Service.pm lib/Rex/Service/ALT.pm lib/Rex/Service/ALT/systemd.pm lib/Rex/Service/Arch/systemd.pm lib/Rex/Service/Base.pm lib/Rex/Service/Debian.pm lib/Rex/Service/Debian/systemd.pm lib/Rex/Service/FreeBSD.pm lib/Rex/Service/Gentoo.pm lib/Rex/Service/Gentoo/systemd.pm lib/Rex/Service/Mageia.pm lib/Rex/Service/Mageia/systemd.pm lib/Rex/Service/NetBSD.pm lib/Rex/Service/OpenBSD.pm lib/Rex/Service/OpenWrt.pm lib/Rex/Service/Redhat.pm lib/Rex/Service/Redhat/systemd.pm lib/Rex/Service/SuSE.pm lib/Rex/Service/SuSE/systemd.pm lib/Rex/Service/SunOS.pm lib/Rex/Service/SunOS/svcadm.pm lib/Rex/Service/Ubuntu.pm lib/Rex/Shared/Var.pm lib/Rex/Shared/Var/Array.pm lib/Rex/Shared/Var/Common.pm lib/Rex/Shared/Var/Hash.pm lib/Rex/Shared/Var/Scalar.pm lib/Rex/Sudo/File.pm lib/Rex/Task.pm lib/Rex/TaskList.pm lib/Rex/TaskList/Base.pm lib/Rex/TaskList/Parallel_ForkManager.pm lib/Rex/Template.pm lib/Rex/Template/NG.pm lib/Rex/Test.pm lib/Rex/Test/Base.pm lib/Rex/Test/Base/has_content.pm lib/Rex/Test/Base/has_cron.pm lib/Rex/Test/Base/has_cron_env.pm lib/Rex/Test/Base/has_dir.pm lib/Rex/Test/Base/has_file.pm lib/Rex/Test/Base/has_file_content.pm lib/Rex/Test/Base/has_output.pm lib/Rex/Test/Base/has_output_matching.pm lib/Rex/Test/Base/has_package.pm lib/Rex/Test/Base/has_service_running.pm lib/Rex/Test/Base/has_service_stopped.pm lib/Rex/Test/Base/has_stat.pm lib/Rex/Transaction.pm lib/Rex/User.pm lib/Rex/User/Base.pm lib/Rex/User/FreeBSD.pm lib/Rex/User/Linux.pm lib/Rex/User/NetBSD.pm lib/Rex/User/OpenBSD.pm lib/Rex/User/OpenWrt.pm lib/Rex/User/SunOS.pm lib/Rex/Value.pm lib/Rex/Virtualization.pm lib/Rex/Virtualization/Base.pm lib/Rex/Virtualization/Docker.pm lib/Rex/Virtualization/Docker/create.pm lib/Rex/Virtualization/Docker/daemon.pm lib/Rex/Virtualization/Docker/delete.pm lib/Rex/Virtualization/Docker/destroy.pm lib/Rex/Virtualization/Docker/guestinfo.pm lib/Rex/Virtualization/Docker/images.pm lib/Rex/Virtualization/Docker/import.pm lib/Rex/Virtualization/Docker/info.pm lib/Rex/Virtualization/Docker/list.pm lib/Rex/Virtualization/Docker/reboot.pm lib/Rex/Virtualization/Docker/shutdown.pm lib/Rex/Virtualization/Docker/start.pm lib/Rex/Virtualization/Docker/status.pm lib/Rex/Virtualization/LibVirt.pm lib/Rex/Virtualization/LibVirt/blklist.pm lib/Rex/Virtualization/LibVirt/clone.pm lib/Rex/Virtualization/LibVirt/create.pm lib/Rex/Virtualization/LibVirt/delete.pm lib/Rex/Virtualization/LibVirt/destroy.pm lib/Rex/Virtualization/LibVirt/dumpxml.pm lib/Rex/Virtualization/LibVirt/guestinfo.pm lib/Rex/Virtualization/LibVirt/hypervisor.pm lib/Rex/Virtualization/LibVirt/iflist.pm lib/Rex/Virtualization/LibVirt/import.pm lib/Rex/Virtualization/LibVirt/info.pm lib/Rex/Virtualization/LibVirt/list.pm lib/Rex/Virtualization/LibVirt/option.pm lib/Rex/Virtualization/LibVirt/reboot.pm lib/Rex/Virtualization/LibVirt/shutdown.pm lib/Rex/Virtualization/LibVirt/start.pm lib/Rex/Virtualization/LibVirt/status.pm lib/Rex/Virtualization/LibVirt/vncdisplay.pm lib/Rex/Virtualization/Lxc.pm lib/Rex/Virtualization/Lxc/attach.pm lib/Rex/Virtualization/Lxc/copy.pm lib/Rex/Virtualization/Lxc/create.pm lib/Rex/Virtualization/Lxc/destroy.pm lib/Rex/Virtualization/Lxc/info.pm lib/Rex/Virtualization/Lxc/list.pm lib/Rex/Virtualization/Lxc/start.pm lib/Rex/Virtualization/Lxc/stop.pm lib/Rex/Virtualization/VBox.pm lib/Rex/Virtualization/VBox/bridge.pm lib/Rex/Virtualization/VBox/create.pm lib/Rex/Virtualization/VBox/delete.pm lib/Rex/Virtualization/VBox/destroy.pm lib/Rex/Virtualization/VBox/forward_port.pm lib/Rex/Virtualization/VBox/guestinfo.pm lib/Rex/Virtualization/VBox/import.pm lib/Rex/Virtualization/VBox/info.pm lib/Rex/Virtualization/VBox/list.pm lib/Rex/Virtualization/VBox/option.pm lib/Rex/Virtualization/VBox/reboot.pm lib/Rex/Virtualization/VBox/share_folder.pm lib/Rex/Virtualization/VBox/shutdown.pm lib/Rex/Virtualization/VBox/start.pm lib/Rex/Virtualization/VBox/status.pm t/0.31.t t/0.54.t t/01.t t/args.t t/auth.t t/author-critic.t t/author-pod-syntax.t t/base.t t/base_virt.t t/batch.t t/before_all_tasks.t t/can_run.t t/case.t t/cmdb.t t/cmdb/default.yml t/cmdb/default/default.yml t/cmdb/default/foo.yml t/cmdb/foo.yml t/commands.t t/commands/evaluate_hostnames.t t/commands/file/test.tpl t/commands/iptables.t t/commands_file_template.t t/config-ssh.t t/config.t t/cron.ex t/cron.t t/db.t t/df.out1 t/df.out2 t/df.t t/dmi.fbsd.out t/dmi.linux.out t/dmi.obsd.out t/dmi.t t/do_task.t t/env.t t/file.t t/fs.t t/fs_files.t t/group.t t/helper_hash.t t/helper_path.t t/hooks.t t/host.t t/hosts.ex t/hosts.ex2 t/ifconfig.out1 t/ifconfig.out2 t/ifconfig.out3 t/ifconfig.out4 t/ifconfig.out5 t/ifconfig.out6 t/ifconfig.out7 t/include.t t/ini.t t/interface_fs_local.t t/ip.out1 t/ip.out2 t/ip.out3 t/ip.out_centos7 t/ip.out_centos7_alias t/ip.out_issue_539 t/issue/1008.t t/issue/513.t t/issue/513_t1.rex t/issue/513_t2.rex t/issue/860.t t/issue/934.t t/issue/948.t t/issue/948/capabilities.xml t/issue/949.t t/issue_539.t t/last_command_output.t t/lib/t/tasks/alien.pm t/lib/t/tasks/cowbefore.pm t/lib/t/tasks/cowboy.pm t/logger.t t/md5.t t/md5test.bin t/needs.t t/net_interface_centos7.t t/network_linux.t t/no_tty.t t/package.t t/param_lookup.t t/param_lookup_cmdb.t t/param_lookup_resource.t t/path.t t/read_buffers.t t/release-minimum-version.t t/report.t t/resource.t t/runlist.t t/shared.t t/ssh_config.1 t/summary.t t/task.t t/task_hosts.t t/template.t t/template_ng.t t/test.ini t/test.xml t/url_encode.t t/virtualization.t t/xml.t ChangeLog100644001751001751 22154113232707066 12667 0ustar00janjan000000000000Rex-1.6.02017-12-03 Jan Gehring (1.6.0) * Check brctl command is available (resolves #1115) - perlancar * fixed path bug in git scm checkout - Joachim Bargsten * add Rexfile path to @INC, fixes bug with perl >=5.26 fixes #1170 - Jan Gehring * Added support for Devuan as clone of Debian. - Christophe Wolfhugel * fix bug with before => ALL in modules - Joachim Bargsten * fixed syntax bug and crash on windows - Jan Gehring * catch exception if version command failed. detect() method must return true/false not throw exception. - fix #1152 - Jan Gehring * replace JSON::XS with JSON::MaybeXS - fix #1153 - Jan Gehring * added initial docker box support - Jan Gehring * indent code to be properly formatted - Yanick Champoux * Allow connection config to be altered in before hook - Andy Beverley * fix #1134 - redirect stderr to dev/null or to stdout - Jan Gehring * fix #1066 - use Task::run() method to call the task code. - Jan Gehring * fix #1117 - run after hook after connection poped from connection stack - Jan Gehring * restore old current task object. - fix #1150 - Jan Gehring * mkfs for lvm volumes is optional - Samuele Tognini * Get PkgConf working with Debian clones like Ubuntu - James D Bearden * Use fail_ok in cron - Daniel Dico * VirtInfo: check dmi sys_vendor for qemu - Ali Polatel * Gentoo/layman: check repo exists for add_repo & add readd option - Ali Polatel * Libvirt: support to select cpu mode for KVM guests - Ali Polatel * Libvirt: vm destroy must not die() for not-running VMs - Ali Polatel * set_auth used before creating the task object - NDZ Prajith * fix exit code on failed logins. we need to bitshift the return value of a fork - Jan Gehring * clearpart: fix partition regex for nvme - Ali Polatel 2016-10-09 Jan Gehring (1.5.0) * Rex::Pkg::Gentoo: Add support for slotted package specs - Ali Polatel * only use srand() once. this caused a bug with sudo file writing. sometimes it generated two identical files - Jan Gehring * fix problem with locks when Rexfile is on a nfs share #1130 - Jan Gehring * fix running sudo on local connections - Jan Gehring * don't format a partition which is part of vg - Jan Gehring * Fix Rsync.pm paths and ports. - necrophcodr * Fix uninitialized value warnings when using Augeas module - Andy Beverley * fixed #1116 - detect suse systems by regex - Jan Gehring * load project libraries before system libraries. fix RexOps/Rex#1108 - Jan Gehring * fixed systemctl detection #1026 - Jan Gehring * Service.pm: Fix service class for Gentoo & non-systemd - Ali Polatel * Rex::Pkg::Gentoo: Smarter installed check to reinstall on USE updates - Ali Polatel * Handle different md5 styles on BSDs (fix #1099) - Ferenc Erki * Catch exceptions when trying to run dmidecode - Ferenc Erki * don't use is_ip function, so that it also works with older Data::Validate::IP module - Jan Gehring * Added some more tests to Rex::Box::Test - Jan Gehring * Refactored Rex::Box to be able to programmatically load box definitions. - Jan Gehring * Die if undefined line is found during _append_or_update (fix #1044) - Ferenc Erki * fix a problem with creating boxes. catch errors - Jan Gehring * Fix for #1051 - Logger.pm - uninitialized value - Tamas Molnar * reload sysctl if file changed - Jan Gehring * use ensure or persistent for persistance - Jan Gehring * fix an extra define on a hash - Solène Rapenne * Detect Manjaro correctly (fix #993) - Ferenc Erki * Don't warn on missing Rexfile when -e is used (fix #1032, ref #1010) - Ferenc Erki * Specify XenServer as RedHat (CentOS) clone - Walery Wysotsky * added i_exec and i_exec_nohup function for better parameter quoting - #872 - Jan Gehring * refactored calls to run() function to use i_run() - Jan Gehring * test for ipv4 - Jan Gehring * don't set_connection if current task is the only one - Jan Gehring * load Helper::Run module - Jan Gehring * Give the kernel time to get informed of partition changes - Robert Abraham * Trying to fix #791 Now get_ssh_config_hostname sub returns a good hosntame. - Paco Esteban * also exclude directories - fix RexOps/Rex#904 - Jan Gehring * push connection information to the new task - fix #1091 - Jan Gehring * Add checking for ensure parameter and an error if ensure parameter isn't present/absent - solene rapenne * sysctl supports remove and keyword changed to ensure - solene rapenne * Create Rexfile-example2 - alx542 * callback and options can both be used - Andrew Solomon * Add an option to save a sysctl key/value in /etc/sysctl.conf - Solène Rapenne * Using OpenBSD rcctl tool for managing services - Solène Rapenne * Add OpenBSD compatibility to test - solene rapenne * Implemented LXC container support - Oleg Hardt * try to add same update_system behaviour for as many systems as possible. Fix for RexOps/Rex#546 - Jan Gehring * backported has_feature_version functions - Jan Gehring * Escape backquotes when run command via sudo in the remote host - Nikolay A. Fetisov 2016-07-16 Jan Gehring (1.4.1) * bug with get_cloud_instances_as_group - Pierrick DINTRAT * Adds 64bit module path to @INC for linux - Mitch Broadhead * set ls quoting for fedora 24 which magically quotes filenames - Jan Gehring * added cache for can_run - Jan Gehring * removed space from line ending. fixed test on centos 6 - Jan Gehring * Don't add duplicates env variable in crontab - Rapenne Solène * quote source and destination, so spaces in directory names are possible - Jan Gehring * fixed summary tests for netbsd - Jan Gehring * run hooks before push_connection - Jan Gehring * Don't hardcode LibXML dependency - Jan Gehring * workaround for wrong behaviour with -f and windows symlinks - Jan Gehring * it seems that the channel sometimes doesn't get closed in time. so we need an option to force close it after command run. - Jan Gehring * fixed reporting for named resource - Jan Gehring * pass cli parameters to tasks for pre 1.4 feature flags. fix #1039 - Jan Gehring * fixed old style Rex::Args->get when called from outside a task - Jan Gehring * Fix for #1054 - get_private_key() to check the private key file - Tamas Molnar * Fix for #1052 - mask sudo_password in debug log - Tamas Molnar * exit rex with an exitcode of 1 if some tests failed. fix #989 - Jan Gehring * show how to assign a user with a non-default shell - Andrew Solomon * Fix example code (fix #1034) - Ferenc Erki * Update example tasknames - Ferenc Erki * Add possible root cause to authentication error message (fix #671) - Ferenc Erki * Correct message for authentication problems - Ferenc Erki * Avoid hardcoded path - Ferenc Erki * Refactor calls to reporting - Ferenc Erki * Refactor reporting message handling - Ferenc Erki * Simplify detection of pkg change type - Ferenc Erki * Refactor pkg on_change handling - Ferenc Erki * Report each pkg modification (fix #838) - Ferenc Erki * Revert "report all packages" - Ferenc Erki * Timeout value should be passed in milliseconds - Ferenc Erki * Fix compatibility with recent Net::SSH2 (fix #1023) - Ferenc Erki * Only log masked password if one is being used - Ferenc Erki * Avoid logging undefined host during connection - Ferenc Erki * Refactor do_task - Ferenc Erki * Backport task existence check for do_task from #1010 (fix #992) - Ferenc Erki * Initial tests for do_task - Ferenc Erki * fix hardcoded git tag command - Kirill Babikhin * Document perlcritic violations - Ferenc Erki * Integer with leading zeros - Ferenc Erki * Add S_IMODE - Ferenc Erki * Variable declared in conditional statement - Ferenc Erki * "require" statement with library name as string - Ferenc Erki * "select" used to emulate "sleep" - Ferenc Erki * Don't modify $_ in list functions - Ferenc Erki * removed hard coded qemu-img create format and added use of driver_type option. fix #948 - Jan Gehring * added understandable error message when no virtualization module is given. fix #949 - Jan Gehring * print right error message if run_task is calling a task that is not available. fix #970 - Jan Gehring * on windows test for SSH connection type. fix #965 - Jan Gehring * Add more tests for shared arrays - Ferenc Erki * Add unshift for shared arrays - Ferenc Erki * Add tests for unshift to shared arrays - Ferenc Erki * Add pop for shared arrays - Ferenc Erki * Add tests for pop from shared arrays - Ferenc Erki * Add shift for shared arrays (fix #921) - Ferenc Erki * Add tests for shift from shared arrays (ref #921) - Ferenc Erki * Fix #967 - Harm Müller * Add META.json to CPAN release - Kent Fredric 2016-03-06 Ferenc Erki (1.4.0) * Normalize EOL characters earlier (fix #957) - Ferenc Erki * fixed variable usage - Jan * remove chr(13) everywhere - fix #957 - Jan * cleanup newline(s) at the end. fix for #957 - Jan * Increase timeout for read_buffer test (fix #954) - Ferenc Erki * Test with a bit less output - Ferenc Erki * Update read_buffer test for Windows - Ferenc Erki * removed read_buffer reduction - Jan Gehring * Handle partial last chunks from STDOUT and STDERR - Ferenc Erki * Fix draining IO - Ferenc Erki * Add initial tests for draining IO - Ferenc Erki * fix continuous read bug - RexOps/#951 - Jan * Reopen STDERR after loading Rexfile - Dmitry Kopytov * Let service tests be silent - Ferenc Erki * Don't confess upon failure of a non-resource (fix #936) - Ferenc Erki * iptables_clear should clear only loaded tables - Dmitry Kopytov * Fix copy-pasta POD errors - Ferenc Erki * Make CPAN Testers happy for unsupported OSes - Ferenc Erki * fixed issue of calling Rex::Args->get inside before_task_start - RexOps/Rex#934 - krimdomu * can't connect message must be ::info logging - krimdomu * check if task is no object, and get the task object then - Jan Gehring * removed connecting to mesages (change to debug level) - Jan Gehring * added parameters to do_task and fixed LOCAL block - Jan Gehring * fixed calling of exit hooks after summary - Jan Gehring * Update docs for Rex::Task - Ferenc Erki * Add docs for Rex::Shared::Var (fix #920) - Ferenc Erki * Update sudo docs (fix #918) - Ferenc Erki * Fix summary tests for Mac OS X - Ferenc Erki * Add tests for task hooks - Ferenc Erki * don't clone task object. When cloned before_task_start/after_task_finished hooks doesn't work - fix #919 - krimdomu * debug output now gets displayed again. - fix RexOps/Rex#910 - krimdomu * fix loading of Rexfile on perl 5.8.9 - RexOps/Rex#922 - krimdomu * fixed loading of resource module in perl 5.8 - RexOps/Rex#917 - krimdomu * try to fix failing tests on freebsd - #901 - krimdomu * Fix POD - Ferenc Erki * Unbundle AWS::Signature4 - Ferenc Erki * Fix rendering of false values in templates - Ferenc Erki * Test rendering of false values in templates - Ferenc Erki * Avoid __Rexfile__ as dependency - Ferenc Erki * Enable more feature flags for development releases - Ferenc Erki * Use development versioning - Ferenc Erki * Pass options to mkfs (#fix 905) - Ferenc Erki * Don't fail mkfs if label is undefined - Ferenc Erki * Skip creating filesystems for some reserved words - Ferenc Erki * Check for fstype existence at the start - Ferenc Erki * Update docs for partition command - Ferenc Erki * Remove purge package action on arch (fix #867) - Ferenc Erki * Use sysread for local exec interface too (fix #887) - Ferenc Erki * Avoid bitshifting error code twice (fix #901) - Ferenc Erki * add default values for cpu and memory - krimdomu * some windows fixes for rexify command - krimdomu * run shell detection a second time if first time failed - krimdomu * remove unnecessary new lines - krimdomu * moved detect method to base, because it is everywhere the same, created direct_exec method for all interfaces - krimdomu * remove error message, we don't need it for the tests - krimdomu * fixed inheritance problem, moved shell() method to base class. - krimdomu * return always shell object - krimdomu * load right cron module if os is *bsd and shell is csh - #883 - krimdomu * use sysread to read from filehandle. fixes #887 - krimdomu * Read STDOUT and STDERR in parallel (fix #756) - Ferenc Erki * fix error code in $? after a run command which was introduced by 6c8441 - krimdomu * print the error message in the summary when a task die()d - krimdomu * readd close of stderr - krimdomu * only update chained resource when the upper resource wasn't changed before. Also removed 'inside resource check' because this is not needed anymore due to stacked resources - krimdomu * fix loading of libraries in lib directory when use -f path/to/Rexfile - krimdomu * fixed loading of rexfiles with -f - krimdomu * load a rexfile also if it doesn't return a true value. This fixes a regression - RexOps/Rex#513 - krimdomu * fixed reporting of nested resources (changed resource) - krimdomu * fixed firewall resource to work with ipv6 patch - krimdomu * added Rex::Args->get method again. fixes #860 - krimdomu * Refactor Rex::TaskList. No functional change. - Eric Johnson * fixed tests for windows - krimdomu * added tmp to gitignore - krimdomu * Add Rex::Commands::Mkfs to rexify and pp.args - Ferenc Erki * The after hook now runs even if the ssh connection fails. - Eric Johnson * added archlinux support - Harm Müller * support key_url and key_file also on redhat systems - krimdomu * also lookup task name in cmdb - krimdomu * Fix regression in deprecated static call to Rex::Task->run() - Eric Johnson * Fix links in README (fix #853) - Ferenc Erki * Output full task names (fix #825, close #842) - Daniel Cesario * use empty string to prevent uninitialized warning messages - krimdomu * make environment available inside cmdb template - krimdomu * color for windows - krimdomu * defaults for cpus and memory - krimdomu * call after task hook, even if connection to system failed - krimdomu * fix after hook call for connections that wasn't successfull - krimdomu * Fix passing memory/cpus options to constructor - Ferenc Erki * Allow tests so set Box memory and cpus (close #839) - Patrick Lauer * Exit with an error message when a bad task name is used. - Eric Johnson * Exit codes now work when using 'rex -e' (fix #761, close #840) - Eric Johnson * Fix indentation - Eric Johnson * Refactor out some code into handle_lock_file() - Eric Johnson * Refactor out logic for loading server ini file into load_server_ini_file(). - Eric Johnson * Put Rexfile exists check inside load_rexfile() - Eric Johnson * Simplify loading the Rexfile and improve error handling. - Eric Johnson * Add missing Data::Dumper import - Ken Crowell * Tests for needs() were not useful. Now they are. - Eric Johnson * allow mix of modules.rexify.org modules and git modules - Jan * Fix summary test tasks for Windows - Ferenc Erki * Add support for ip6tables (close #795) - Dmitry Kopytov * Prefer uid over getlogin as fallback username (close #793, fix #792) - Ferenc Erki * Sort failure list - Ferenc Erki * Further simplify summary output - Ferenc Erki * Refactor summary output - Ferenc Erki * Use error level output in the summary if some tasks failed - Ferenc Erki * added AWS::Signature4 directly to code, due to problems with Centos5 dependencies (#737) - Jan * ignore eclipse files - Jan * fixed call of task as functions with normal parameters, #827 - Jan * allow config (set) vars in cmdb templates - Jan * Fix template handling in YAML CMDB files - Ferenc Erki * YAML cmdb can now contain template code - Jan * don't concat caller package anymore - Jan * fixed some unitialized warnings - Jan * fixed lookup path for arp command - Jan * report all packages - Jan * allow nested resources in reports - Jan * use md5 binary if perl module is not available - fix for #834 - Jan * default to empty task list - Jan * don't print summary on rex -T - Jan * use can_run to detect arp command - Jan Gehring * Custom storage path for kvm - Prajithp * give server configuration precedence - Jan * detect iptables version and choose syntax for ip adresses - Jan * fixed a logic bug, that prevents batch creation - Jan * fixed a bug that was introduced by b0416b1710611b900284ba5a2d9d74991fc4351b - Jan * Ensuring that labels can be added to swap partitions - Peter H. Ezetta * Ensure proper test-time connection type (fix #821) - Ferenc Erki * Fix examples - Ferenc Erki * Slightly reword synopsis - Ferenc Erki * Updated pod in bin/rex and lib/Rex.pm - Eric Johnson * Fix static side comments - Ferenc Erki * Use static comments to avoid alignment change later - Ferenc Erki * Checking for a running rex process failed if the pidfile contains a pid that partially matches a running process. - petersonchen * Updated documentation in bin/rex (pod/man page) - Eric Johnson * fixed loading of Rex::Commands via require. RexOps/Rex#789 - Jan Gehring * Recognize Raspbian as a Debian clone - Ferenc Erki * Mute noisy tests - Ferenc Erki * Added option for rebase during pull. - Eivin Giske Skaaren * added feature flag for 1.4 and removed deprecated warning of Rex::RunList - Jan * Added a firewall module to manage different firewall models with one interface. - Jan * resource and task now have own namespace for template variables - Jan * Exit codes need to be bit shifted by 8. Do it in one place. - Eric Johnson * Summary tests for all permutations of Parallel::ForkManager, Base, and exec_autodie - Eric Johnson * Summarize success/failure at the end of a run - Eric Johnson * added ParamLookup and registering task parameter as template variables. - Jan * Suppress STDERR during crontab listings - Ferenc Erki * Fix managing a user's own crontab via cron_entry - Ferenc Erki * Add _whoami to determine remote effective username - Ferenc Erki * Add docs on glob usage with file command (close #712, fix #738) - Jose Luis Perez Diez * Map Debian clones correctly - Ferenc Erki * Recognize LinuxMint as a Debian clone (close #506) - Ferenc Erki * Fix GetRex link url - hiroraba * Avoid unnecessary package rebuilds on Gentoo (close #809) - Patrick Lauer * Don't parse @ARGV on import. - Eric Johnson * Refactor child/parent pid logic + comments to be more readable - Eric Johnson * Avoid race condition when doing IPC. - Eric Johnson * Test for race condition problem in Rex::Shared::Var. - Eric Johnson * Refactoring LVM.pm to use mkfs(); - Peter H. Ezetta * refactor Partition.pm, fix $lv_name in Mkfs.pm - Peter H. Ezetta * refactoring swap logic into mkfs logic, silly to separate. - Peter H. Ezetta * code cleanup and addition of POD - Peter H. Ezetta * error checking on $lvname - Peter H. Ezetta * setting up logic for lvm vs raw disk - Peter H. Ezetta * starting to add option parsing - Peter H. Ezetta * start of refactor mkfs/mkswap into their own pm - Peter H. Ezetta * Fix undef warnings if calling get() without param - Andy Beverley * lower exitecode to less than 255 because some os have problems with larger ones - Jan * Fatal exceptions if git switching fail - alex1line * fix for Net::OpenSSH can't set initialize_options for the constructor - #796 - Jan Gehring * fixes the problem of run_task connecting to the wrong host #788 - Jan Gehring 2015-09-04 Ferenc Erki (1.3.3) * Catch another way to manage services on FreeBSD (close #773) - timp87 * Fix generated links for Commands modules (fix #776) - Ferenc Erki * Fix code block output when there's no space before closing tag - Ferenc Erki * Escape curly braces in template content - Ferenc Erki * Force syncing package information on FreeBSD - Ferenc Erki * Take service name and rcvar mapping into account on FreeBSD (close #770) - timp87 * Check if systemctl is functional before using it (fix #753) - Ferenc Erki * Rearrange dist.ini - Ferenc Erki * Fail early on unsupported Windows versions (fix #751) - Ferenc Erki * Warn about missing environment instead of panicking (fix #742) - Ferenc Erki * Document alias of get_operating_system() - Ferenc Erki * Fix perlcritic warnings about modifying list elements - Ferenc Erki * Fix perlcritic warnings about two-argument open - Ferenc Erki * Update auth docs - Ferenc Erki * Fix docs for transaction (fix #686, close #766) - Elmer Quintanilla * Fix ensure option for NetBSD services (close #759) - timp87 * Do not silently fail on update errors (close #758) - Andrew Beverley * Fix ensure option for FreeBSD services (close #752) - timp87 * Enable Rex to manage system services on FreeBSD (#752) - timp87 * Recognize multi-arch packages on Debian (fix #748, close #755) - Erik Huelsmann * Fix Gentoo service detection (fix #747) - Ferenc Erki * force apt-listchanges to not run - Anders Ossowicki * Added doc about using regex for auth - Eivin Giske Skaaren * Fixes #760 timeout for OpenSSH - Eivin Giske Skaaren * Use the correct class for managing forks (fix #743) - Ferenc Erki * Support key files for Debian repositories + add docs (close #736) - John Karr * Don't recreate connection during rethink_connection (fix #694, close #727) - Mitch Broadhead * Pass exception to on_rollback (fix #687, close #732) - Mitch Broadhead * Document hostname expressions support in INI files - Ferenc Erki * Support hostname expressions in INI files (close #713) - okaoka * Document -O CLI option - Ferenc Erki * Revert "Remove unused CLI option" (fix RexOps/rex-jobcontrol#10) - Ferenc Erki * make Fcntl calls os independent - Jan * added a wrapper module for File::Spec - Jan * Fix LEFT_PRECEDENCE typo - Dmitry Kopytov * Add function for checkout of Git tags - Eivin Giske Skaaren 2015-06-17 Ferenc Erki (1.3.2) * Use binmode as a function - Jan * Use raw Rex::Interface::Exec to call can_run - Jan * Remove md5sum usage - Ferenc Erki * Fix for filenames with at sign in them - Ferenc Erki * Add test case for filenames with at sign - Ferenc Erki * Add initial MD5 test - Ferenc Erki * Fix check for environment-specific filenames - Ferenc Erki * Use OS-agnostic perl executable detection - Ferenc Erki * Fix RC version handling - Ferenc Erki * Refactor MD5 checksum calculation (fix #719) - Ferenc Erki * Add missing test names - Ferenc Erki * Only display diagnostic message if something went wrong - Ferenc Erki * Explicitly test for optional dependencies - Ferenc Erki * Fix LEFT_PRECEDENCE typo - Dmitry Kopytov 2015-06-08 (1.3.1) * Cleanup db tests (fix #714) - Ferenc Erki * Update parallelism docs - Ferenc Erki 2015-06-03 (1.3.0) * Fix regex pattern for perl-5.22.0 - Ferenc Erki * Filter changelogger output - Ferenc Erki * Cleanup group command POD - Ferenc Erki * Convert POD directives of methods and DSL functions (fix #685, close #705) - Brian Manning * pass cmdb() arguments to callback - fixed #709 - Jan * allow creation of inherited Rex::Group::Entry::Server objects - fixed #708 - Jan * Reuse VM name as image filename when importing - Ferenc Erki * Recognize CloudLinux as Red Hat clone (close #699) - Dmitry Kopytov * Fix auth test when REX_USER env is present - Dmitry Kopytov * Remove unused CLI option - Ferenc Erki * More helpful/verbose help message (close #698) - Eric Johnson * Cleanup a file used during testing - Ferenc Erki * Cleanup tests with optional dependencies - Ferenc Erki * Use explicit test plans everywhere - Ferenc Erki * Cleanup test imports - Ferenc Erki * Remove tests doing nothing else than use_ok() - Ferenc Erki * Automatically use all modules during testing - Ferenc Erki * Remove redundant hostname evaluation tests - Ferenc Erki * Remove unused Data::Dumper from tests - Ferenc Erki * Add CMDB docs - Ferenc Erki * Don't run into an endless loop: fix for #692 - Jan Gehring * Iptables.pm: add long-form iptables examples to POD - Brian Manning * Iptables.pm: show error from iptables on non-zero exit status - Brian Manning * ignore backfiles from editors - Jan * Log STDERR on errors where auto_die is enabled - Dmitry Kopytov * Set parallelism automatically (fix #491) - Ferenc Erki * added template_ng tests - Jan * fixed a problem when one template object was used twice - Jan * new template engine for 1.3 - Jan * Fix checking of virsh command result upon VM creation - Ferenc Erki * Update clearpart command documentation - Ferenc Erki * Add bios_boot option to GPT initialization - Ferenc Erki * make report filename configurable. don't sleep in test - Jan * Check for CLI argument definedness (fix #668) - Ferenc Erki * this commit fixes #667. this bug was introduced with the fix for #629 - Jan * first detect if uname and md5sum can be run, then use it. fixed #665 - Jan * fix warning if local file is not given. fixed #647 - Jan * Include provided modules in META.yml - Ferenc Erki * added systemd support for debian. fixes #659 - Jan * this prevents the stderr output of perl to get send over the wire. fixes #658 - Jan 2015-05-04 (1.2.1) * first detect if uname and md5sum can be run, then use it. fixed #665 - Jan 2015-05-02 (1.2.0) * Set version to 9999.99.99 if version is not present (e.g. during development) - Jan * Fix Rexfile parsing (fix #629) - Jan * Refactor tasklist output (fix #631, #653) - Eric Johnson * Remove -w from shebang (fix #650) - Eric Johnson * Add tab completion (fix #636, #652) - Eric Johnson * Update modules listed in POD - Brian Manning * Add another Red Hat synonym for RHWS version 3 - Brian Manning * Fix file manipulation when using Net::OpenSSH and sudo (fix #640) - Jan * Check if requested environment is defined (fix #639) - Ferenc Erki * Use normal DSL for internal task - Ferenc Erki * Fix SCM documentation - Ferenc Erki * Sort server names naturally - Ferenc Erki * Sort tasklist output (fix #633) - Ferenc Erki * Add YAML CMDB merging support (fix #499) - Ferenc Erki * Add CMDB merge tests - Ferenc Erki * Update list of contributors - Ferenc Erki * Check number of elements returned by stat - Ferenc Erki * Rex::Commands::Fs::stat() should return a hash or throw an exception. - Mitch Broadhead * Allow single-character tasknames (fix #621) - Ferenc Erki * Fix reporting for umount - Ferenc Erki 2015-03-29 (1.1.0) * Fix dependencies for openssh + pass_auth - Ferenc Erki * prevent faulty debug message - Jan * fixed merge_auth() method for #615 -Jan * Update task tests (fix #614) - Jan * Recognize usable SSH modules separately - Ferenc Erki * Describe platform-specific dependencies - Ferenc Erki * Only check for iDrac signature if there was an output - Ferenc Erki * added a prototype, this will prevent failures like RexOps/Rex#584 - references: RexOps/Rex#608 - Jan * we need to do an explicit return undef. if we only use 'return' and the return of is_file/is_dir is passed to an array, this will not appear in the array. reference: RexOps/Rex#608 - Jan * updated tests to use is() instead of ok(). reference: RexOps/Rex#608 - Jan * changed return code to undef if file/dir not found. fix for RexOps/Rex#608 - Jan * Add initial iDrac shell support - Ferenc Erki * Allow can_run method to accept command to do the check with - Jan * Check if a command can be run before its execution (fix #514) - Ferenc Erki * Refactor can_run - Ferenc Erki * Support can_run command on Windows - Ferenc Erki * Move can_run to Rex::Interface::Exec - Ferenc Erki * Add can_run tests - Ferenc Erki * Create target directory before extracting an archive (fix #600, close #604) - Arnold Bechtoldt * Update POD (close #598) - Ferenc Erki * added a Rex::Logger::masq() function that can masq sensitiv data for logging output. (fix for RexOps/Rex#554) - Jan * added code to make shells pluggable. - RexOps/Rex#602 - Jan * Avoid noisy test output - Ferenc Erki * added support to export resources to main namespace and added more events - Jan * added possibility to use auth(for => task) before a task is created - fixed #402 - Jan * Fix handling of symbolic links (fix #591, close #592) - Jan * this adds a new method to the server objects called group() - Jan * fixed setting of path environment variable - #583 - Jan * Add preferred type option for mount command (fix #469) - Ferenc Erki * Reword has_{dir,file} test outputs - Ferenc Erki * Add has_dir test - Ferenc Erki * Print explicit PASS or FAIL output upon finishing a test suite - Ferenc Erki * Fix has_stat test for non-existing UIDs and GIDs - Ferenc Erki * Allow has_stat test to handle directories (fix #582) - Ferenc Erki * Add diag method for Rex::Test::Base - Ferenc Erki * Ensure proper return values for is_{dir,file} functions (fix #584) - Ferenc Erki * Drop potentially dangerous --force-yes option (fix #559) - Ferenc Erki * Generate docs for Rex::Test (fix #483) - Ferenc Erki 2015-03-08 (1.0.0) * Remove hardcoded connection type (fix #579) - Ferenc Erki * fixed systemd status query - Jan * test output of tmpdir gathering before working with it. - Jan * fixed removing some tmp files - Jan * fixed some warnings for netstat listing if reading an unknown transport layer - Jan * added no_autodie feature flag - Jan * fixed is_file() for files with spaces - Jan * Fix POD - Ferenc Erki * don't redirect stdout 2 times. freebsd don't like it - Jan * added tty feature flag and sorted the flags - Jan * added some defaults for kvm boxes (network) - Jan * fixed autodie bug for is_symlink function - Jan * added reconnect tries to Net::OpenSSH connections - Jan * Allow spaces in Augeas values - Andrew Beverley * Warn if Augeas command fails - Andrew Beverley * fixed a problem if a server group was empty - Jan * dist.ini: Add Twitter and IRC links to metaresources block - Brian Manning * Rex::Commands::MD5: Make Rex use the /sbin/md5 binary on OS X - Brian Manning * Rex::Commands::User: fix typo in POD (user_group -> user_groups) - Brian Manning * Rex::Helper:Run->i_run: check no_path_cleanup before calling get_path - Brian Manning * stop 'profile' before returning from a subroutine. - Andrej Zverev * Speed up connecting to Boxes - Ferenc Erki * allow call of run() command with arrayRef - Jan * fixed sync_up/sync_down with Text::Glob - Jan * fixed authentication, failing if try password auth without mentioning pass_auth directly - Jan * fixed line endings with openssh and pty - Jan * Added possibility to use group() also as a resource function - Jan * added possibility to define task parameters multiple times from cli. - #516 - Jan * fixed windows tests - #514 - Jan * fixed #555 - default for Net::OpenSSH now also spawn a pty. Can be disabled with feature no_tty - Jan * Die if trying to run augeas without augtool installed (close #547) - Andrew Beverley * Skip db tests if there are missing dependencies (fix #548, close #549) - Volker Kroll (vkroll) * Restore perl-5.8.9 compatibility - Ferenc Erki * Add test for minimum perl version required - Ferenc Erki * Be more explicit about required perl version - Ferenc Erki * Stop append_or_amend_line inserting extra blank lines - Andrew Beverley * Add tests for append_or_amend_line - Andrew Beverley * fixed Rex::Box with Net::OpenSSH - Jan * rex/CLI.pm: update docs for Rex options - Brian Manning * CLI.pm: throw error if -T used with task arg, but no matching task found - Brian Manning * fixed #539 - detect primary network address - Jan * Add append_or_amend_line function to File command - Andrew Beverley * Drop Rex::Helper::Glob - Ferenc Erki * Suppress noisy test output - Ferenc Erki * Suppress warning about a variable being used only once - Ferenc Erki * Tidy up all the tests - Ferenc Erki * Use more helpful test functions - Ferenc Erki * use more appropriate functions from Test::More in tests - reneeb * some class inherit cleanup - Jan * Use correct path when using augeas insert - Andrew Beverley * Return correct output from augtool - Andrew Beverley * Fix false positive when using "augeas exists" - Andrew Beverley * Optimise Rex::Commands::Augeas - Andrew Beverley * Add user base class for those calls not supported in all OS - Andrew Beverley * Add password lock/unlock functions (Linux only) - Andrew Beverley * fixed resource end - Jan * Add PkgConf command to configure packages - Andrew Beverley * fixed set_openssh_opt() function to allow multiple options - Jan * fixed #527 - Rex::Output leaks semaphores and shared memory - Jan * improved continous_read option for Net::SSH2 connection mode - Jan * tail now also works with sudo also fixed #530 - Jan * use Net::OpenSSH is now default. don't need feature flag 0.55 - Jan * fixed line based operation with OpenSSH connection mode - Jan * migrated augeas module into Rex core - #532 - Jan * added partial sudo support for rsync command - Jan * fixed #529 - odd number of elements - Jan * fixed #528 -Amazon list_services, doesn't get all ec2 instances - thanks to David Golovan - Jan * fixed an issue that causes the parser to think the rexfile has an error - Jan * start of unit-test for Rex::Commands::DB - Volker Kroll (vkroll) * better fix for #521, don't print all servers by rex -T. Also fixed group authentication. - Jan * patch from twitter/@tekcurmudgeon to allow setting of gpgkey for a repository - Jan * fixed late group lookup - #521 - Jan * if the evaluation of the Rexfile was without syntax errors, but don't return a true value, try to evaluate it manually. so is is not needed to return a true value at the end. - fix for #513 - Jan * fixed path resolution for private_key and public_key when used a ~ (tilde) sign. #517 - Jan * fix rsync with port definition - #520 - Jan * added parse_templates option to sync_up function, so that template parsing can be prevented - #519 - Jan * Rex::FS::File accepts filenames now - reneeb * Add initial version of changelog generator - Ferenc Erki 2015-01-11 Jan Gehring (0.57.0) * allow definition of gpgkey for redhat/yum repositories - #522 - tekcurmudgeon * fixed Group defined after task definition - #521 * fixed rsync will execute failed when use -H 127.0.0.1:2222 - #520 * added new sync_up/down option for sync_up function will replace template variable of *.tpl - #519 * fixed failed authentication when used ~ symbol - #517 * fixed before_task_start() fails with an ambiguous error when your Rexfile does not return a true value - #513 2015-01-06 Jan Gehring (0.56.1) * tasks doesn't return a value when called as a sub (#523) - Jan 2014-12-26 Jan Gehring (0.56.0) * Extend documentation of run() options (#466) - Ferenc Erki * New template engine with better error reporting. - Jan * Only try to run umount if mount point is already mounted - Ferenc Erki * Set changed flag for umount after the command has been run - Ferenc Erki * Fix error when only grow option was given to partition() - Ferenc Erki * Fix regex to find end of partition instead of size - Ferenc Erki * Use kB as unit when determining partition boundaries - Ferenc Erki * Fix missing `strict` and `warnings` pragmas - Ferenc Erki * Add LICENSE section to POD - Ferenc Erki * Replace defined-or operator to restore perl-5.8.0 compatibility - Ferenc Erki * Fix test for Parallel::ForkManager - Ferenc Erki * Fix test for Amazon Cloud Module - Ferenc Erki * Add rex_kvm_agent feature flag - Ferenc Erki * Allow multiple tasks to run with Rex::Test::Base (fix #476) - Robert Abraham * Improve evaluation of hostnames (fix #479, close #480) - Renee Bäcker * Fix POD (close #488) - Brian Manning * Fixed some deprecated docker calls - Jan * Fix mode option for mkdir command - Ferenc Erki * Fixed needs function - Jan * Added before_execute and after_execute task hooks - Jan * Add basic tests for Rex::Logger (close #484) - Renee Bäcker * Clarify/correct documentation (close #486) - Sascha Askani * Check versions of installed packages - Ferenc Erki * Refactor has_package test for simpler version matching - Ferenc Erki * Use OurPkgVersion for automatic module versioning - Ferenc Erki * Add has_stat to Rex::Test - Robert Abraham * Add documentation for has_stat (close #474) - Ferenc Erki * fix for #473 - download root restricted files in sudo mode - Jan * fix for #498 - added autodie feature flag - Jan * fixed local mkdir return code - Jan * fixed path quoting for #512 - Jan * added glob_to_regex function (Text::Glob) - fix for #495 - Jan * use test binary instead of '[ ... ]' for file tests - Jan 2014-11-02 Ferenc Erki (0.55.3) * Fix @INC compilation for Windows - Ferenc Erki 2014-11-01 Ferenc Erki (0.55.2) * Don't return leading ./ on pathes - Ferenc Erki * Make helper_path tests OS-agnostic - Ferenc Erki * Convert ok() tests to is() - Ferenc Erki * Remove unnecessary variable assignment - Ferenc Erki * Update installation instructions - Ferenc Erki * Use ChangeLog file in tests - Ferenc Erki * Fix typo - Ferenc Erki 2014-10-25 Jan Gehring (0.55.1) * status call for services with upstart and systemd may not work properly - #460 - Jan * sudo with -e cli flag doesn't work - #461 - Jan * Cannot pass an argument with the value zero to a task - #463 - Jan * Issue tracker not in META.yml - #464 - Ferenc Erki * Allow specifying which tests to run as a parameter for Test:run #462 - Ferenc Erki 2014-10-19 Ferenc Erki (0.55.0) * vm names in quote. so they can contain spaces - Jan * fallback to arp query if no answer from rex-kvm-agent - #454 - Jan * Print out error messages during Test:run (fix #450) - Ferenc Erki * Clarify error message during image download - Ferenc Erki * Remove explicit setting of VERSION - Ferenc Erki * return 0 if no swap given - #452 - Jan * fixed windows crashing on multiple connects - #448 - Jan * Work when swap in not enabled and values are undefined. FreeBSD only for now. - Graham Todd * possibility to modify Net::OpenSSH constructor - Jan * Revert use Rex::Group (#447) - Ferenc Erki * added late-group lookup, if group is not defined yet. fixed #447 - Jan * Make PkgVersion happy - Ferenc Erki * Fix typo - Ferenc Erki * removed unlink - Jan * Added possibility to query rex-kvm-agent. fixed #436 - Jan * dont throw error with multiple test files - Robert Abraham * Add ROSA systems support - Denis Silakov * fixed get_installed and is_installed functions Rex::Pkg::Gentoo - Robert Abraham * create binary installers from Rexfiles - Jan * Correct ChangeLog - Ferenc Erki * use Net::OpenSSH as default when available - #435 - Jan * removed executable bit - Jan * Added possibility to pack rex with PAR - Jan 2014-10-03 Jan Gehring (0.54.3) * added possibility to clone an jiffybox image - #439 - Peter Manthey * only execute testfiles which end on .t - #434 - Robert Abraham * close last used ssh connection after test - #433 - Robert Abraham * Add error message when attempting to run a non-existing task - FErki * Check if file exists before checking contents - fix #432 - FErki * refactored the behaviour of set() function to do what it is saying. So with feature 0.54 enabled set is always overwriting the existing values. (#425) * Add service_exists for Gentoo - FErki * fixing nested sudo operations. - #423 * added check if service exists - #407 * Handle hostgroup members with leading numeric ranges - FErki * redirect nohup output to /dev/null * fixed return value for flavors function - #406 - exzz * make apt-listchanges non-interactive - #417 - aowi * added path_map function - Erik Huelsmann * don't try to run dmidecode if it is not in PATH - Andrej Zverev * enhanced support for pkgng (FreeBSD) - Andrej Zverev * fixed Rex::Commands::MD5::md5() to obey path settings * Add documentation for run() function. - #440 - Erik Huelsmann 2014-09-13 Jan Gehring (0.53.1) * added Rex::JobControl functions * fixed hanging VBox with CentOS 7 and delayed dhcp ip lease * fixed on_change hook for file() resource when file was removed * added cmdb variables to template with feature flag - #420 * export Rex::Config variables to all template variants - #419 * fixed chkconfig bug for Mageia, Redhat and SuSE - chenryn * added resource() function, to define own resources. 2014-08-30 Jan Gehring (0.52.0) * fixed #381 - file NAME, ensure => 'absent' for a directory * fixed #392 - run conditional options with exec_autodie * added on_change hook for update_system function. fixed #401 * Added support for end_if_matched option to run command * Rex::Output to persist across different processes (forks) * Add floating ip support for openstack provider - #398 * Auto upload ssh key to openstack cloud provider * Implement feature to tie server.ini to specified -E environment (server.$environment.ini). * #409 - added before_task_start and after_task_finished hooks * [#408] - define fallback authentication * [#416] - fixed pkg with ensure => 'ver.si.on' 2014-07-29 Jan Gehring (0.51.2) * Fixed #394 - export of is_symlink function * Fixed #395 - is_file compatibility bug, doesn't detect symlinks anymore * Added "." in the allowed char of lvm create #393 - samuelet * possibility to call tasks as a method (prettier dsl) * load cmdb by default * load ini group module by default, if server.ini exists 2014-07-20 Jan Gehring (0.50.0) * Use stat() output for directory and file tests (fix #391) - FErki * added Paralell::ForkManager as optional component - #295 * fixed Problem with failed conditionals in Rex::Command::Run - #389 * activate exit_status feature by default for non parallel task execution * added proxy_command support - fixed #380 2014-07-12 Jan Gehring (0.49.0) * Added FreeBSD 10 Support for pkgng. #280 2014-07-10 Jan Gehring (0.48.0) * Added CentOS 7 support 2014-07-05 Jan Gehring (0.47.0) * Rex::Test now also working with KVM - FErki * Update default VNC listen address for KVM machines - FErki * added pkgng commands for FreeBSD 10 * A module which allows to read configuration files from an XML file. - nathanIL * Fixed a problem with auth_type try for rsync * Fixed using -G cli switch with a non existing group it will run localy #379 * added Darwin (MacOSX) network module * documentation updates - FErki * Ident task description when running -T - Nathan Abu * use https to communicate with amazon * allow set callback and environment together #374 (run command) - alex1line * added exclude option to sync commands - Cameron Daniel * General tasks before/after sub #353 * shell_path variable is not checked for empty value before use #376 - Ilya Evseev * verbose_run feature flag #375 * Syntax enhancement of "group" command #369 - Jens Berthold * extend service() function, so that it knows how to get a status for a service if the init script doesn't have a status call * Avoid warning if there's nothing to upgrade (update_system) - FErki * Possible precedence issue with control flow operator with perl-5.20.0 - FErki * added Rex::Constants library * don't use shadow file if not present - user module * detect amazon system and use redhat classes * fixed inline templates for modules 2014-05-22 Jan Gehring (0.46.2) * fixed tmp_dir configuration * load Rex::Commands::Box if Rex::Test is loaded, so that set(box => '') work * fixed a problem with Test:run 2014-05-19 Jan Gehring (0.46.1) * fixed a dependency problem * fixed a problem detecting the temporary directory * 'set port' ignored in Rexfile - #366 * update_system / better error message - #367 * set sudo auth for a special server in a group causes endless loops - #368 2014-05-01 Jan Gehring (0.46.0) * Rex::Box, added kvm support - #174 * core: allow passing template content to template command - #345 - reneeb * core/report: refactored report generation. This change break backward compat. because the report format changed. * core: added groups_dbi() function to generate server groups from sql - #346 - Jean-Marie RENOUARD * core: added groups_yaml() function to generate server groups from yaml file - Jean-Marie RENOUARD * core: add support for df on a given mount point - Simon Bertrang * cloud/amazon: fixed a case where amazon returns instance item in an array - Kasim Tuman * core: added authentication to download() function. - #340 * core: refactored tmp dir generation - FErki * cloud: added cloud_volume detach/attach function * cloud/amazon: fixed multiple tags - David Golovan * core: added description to environments - #274 * refactored README.pod to README.md, added build badge - eduardoj * core: extended rexify command to work with git * core/test: Added Rex::Test - Framework to run tests * core: Connect failure reports "Error running task/batch: Wrong username/password or wrong key" - #359 - eduardoj * core/cmdb: path can now have variables / can be extended * core/user: refactored handling of home directory creation. This might break compat. because we are following the system default now. (added create_home option) - #270 - FErki * core: added a special load path for perl libraries, so that we don't mix up perl and rex modules. * core/service: using nohup to work around a bug in Net::SSH2/libssh2 * core: the caching is now enabled by default. this might break backward compat. if you are using chroot() to another system inside a task. * core: added 'no_cache' feature. 2014-04-13 Jan Gehring (0.45.3) * fixed jiffybox endless loop on creating instances. #344 - reneeb 2014-04-12 Jan Gehring (0.45.2) * fixed special mkdir() case on local windows runs. 2014-04-11 Jan Gehring (0.45.1) * no_overwrite option for file() function * ensure 'directory' option for file() function * added notifications * pkg resource (replacement for install function) * allow array for file() function * check if iptables rule already exists * creates option for run() resource * only_if and unless option for run() resrouce * added notification for service() resource * added account() resource (as replacement for create_user) * fixed SCM::Git to work with sudo * update _parse_ip subroutine. be possible to parse ppp0. #328 - Tomohiro Hosaka * support -g to supply group name - #330 - fanyeren * added openstack cloud support - Ferenc Erki * run() resource support customized environments - #316 andrejzverev * can_run() now returns the first command found as string - #193 * read cpu information out of /proc/cpuinfo if dmidecode is not available - #306 * Handles the case where rsync is missing, and that makes Rex wait forever - #331 - Joris DE POOTER * fixed $Rex::Logger::format does not apply. - #335 * fixed download() command on windows - #271 * added cloud_image_list function() * fixed debian system_update - #339 - Niklas Larsson 2014-03-02 Jan Gehring (0.44.6) * fixed wrong expansion of home paths #324 * fixed return code on failed connects is wrong #317 * get as much output from ps(1) as possible #323 - sbertrang 2014-02-25 Jan Gehring (0.44.5) * fixed shell gathering 2014-02-17 Jan Gehring (0.44.4) * fixed manifest file 2014-02-14 Jan Gehring (0.44.3) * do not call sprintf on undefined values to prevent warnings - #312 - sbertrang * add ksh to shells - #310 - sbertrang * only pass actual option strings to prevent ssh crashes - #309 - sbertrang * rexify does not create projects due to missing file error - #318 * sed command changes mode of target file - #314 2014-02-08 Jan Gehring (0.44.2) * fixed sudo_without_sh - #305 * added warning if no perl interpreter was found on the remote system - #302 2014-02-02 Jan Gehring (0.44.1) * fixed detection of openSUSE with lsb-release installed - #297 * use Makefile.PL for tests. fixed #300 * Support for DBI \%attr hashref - #296 - stefb69 * Make quiet mode not mute warnings and errors. - #294 - slashbeast * new cli parameter -qw for quiet with warnings - #294 2014-01-25 Jan Gehring (0.44.0) * docker support (experimental) - #278 - chenryn * format the output of say() - #155 * userdefined columns for ps() command - #175 - dirkcjelli * using tilde (~) sign for directories - #198 - gnouc, krimdomu * SCM::Git, now uses cwd option of run() command, so it works also remote. - #211 - atrodo * Box default pkg update - #217 - endyman * run_batch() command to run batches on demand - #222 - jorisd * Allow "sed" function to work on multiple lines. - #227 - davidolrik, krimdomu * Added bulk_install() method for packages installing - #229 - jorisd * Enable bulk_install for Gentoo and OpenWrt - #231 - ferki * Added some hooks at central points in rex, so that it is possible to control the behaviour of rex in some points. * added on_change hook for sync_up/sync_down - #232 * Rex::Group::Lookup::Command - read hostnames from a command. - #233 - fanyeren * Improve user and group management on OpenWrt - #242 - ferki * Add kernel module (un)loading support for OpenWrt - #243 - ferki * Add service status support for OpenWrt - #246 - ferki * make ssh read buffer configurable (for Net::SSH2 connections) - #247 this will speedup the connection, but may break on older systems! * Add systemd service provider support for Gentoo - #250 - ferki * Add systemd service provider support for Mageia - #282 * feature flag to deactivate path cleanup - #261 * feature flag to parse $HOME/.profile - #262 * Cloud::Amazon Check to make sure it is HASH before key look up - #263 - oneness * autodie feature if run() fail - #265 * added support for tcsh shell - #284 * Fix guestinfo for Gentoo - #236 - ferki * get_host can't find aliases - #239, #240 - jorisd, ferki * rsync get wrong user if using "auth for $task" - #252 * Flag existing feature sudo_without_sh as found - #253 - gittex * Cleaned-Up Data module dependence - #254 * iptables arguments needs quote if they are whitespaced - #257 - jorisd * add Pod encoding marker - #259 - sergeyromanov * "needs" doesn't know how to call tasks from the main Rexfile - #260 * gathering alias network interfaces like eth0:0 - #264 * Cron: Jobs can be duplicated - #269 - jorisd * FreeBSD: store netmask in dotted decimal format - #287 - andrejzverev * Uninitialized value in OpenSSH.pm - #290 - samuelet 2013-10-03 Jan Gehring (0.43.7) * fixed problem with unconfigured network devices * fixed return of complete cmdb * fixed bug with the reporting initialization 2013-09-17 Jan Gehring (0.43.3) * fixed return of string '0' on stdout * fixed loading of report via env variable * fixed manifest 2013-09-17 Jan Gehring (0.43.2) * #234 - Silent yum operations - Chris Steigmeier 2013-09-16 Jan Gehring (0.43.0) * #223 - generating reports of changed things on the remote system (report infrastructure) * #220 - atomic uploads * #219 - df function doesnt parse errors - jorisd * #218 - problems with escaping of special characters, reverted back to old behaviour (pre 0.42) * #215 - input validation for rexify to prevent creation of invalid module names * #214 - yum operations should be silent * #213, #231 - rexify to use proxy settings - Chris Steigmeier * #231 - Another Red Hat Enterprise flavor - Chris Steigmeier * #200 - Refactor OpenWrt user module - Ferenc Erki * #195 - Fix a typo in example code - Boris Däppen * #194 - Prefer ip command over ifconfig - Ferenc Erki * #189 - Zero values in crontab fields - Ferenc Erki * #186 - Turn "eval your Rexfile" into a debug message - Anders Ossowicki * #210 - zypper --no-gpg-checks option * #208 - Cache inventory of servers for faster execution * #206 - sync_up function doesn't work in modules. * #196 - Support port with ranged hostnamed * #180 - support of other shells than bash for the PATH / environment variable - Cuong Manh Le * #177 - if a feature can't be satisfied, die() * #166 - tmp directory now configurable 2013-07-04 Jan Gehring (0.42.4) * fixed an issue with append_if_no_such_line when searching for a string containing a quote. 2013-06-29 Jan Gehring (0.42.3) * #189 fixed zero values in crontab - ferki * fixed ownership problem with sudo mode and file manipulation 2013-06-23 Jan Gehring (0.42.2) * fixed local run of run_with helper command 2013-06-22 Jan Gehring (0.42.1) * #178 - no_ssh option doesn't work with OpenSSH connection * #181 - ssh ports doesn't work with Net::OpenSSH * #182 - addition parameters doesn't work for ini files * #183 - fqdn doesn't work in ini files * #184 - hostname evaluation and additional parameters doesn't work together 2013-06-15 Jan Gehring (0.42.0) * added Net::OpenSSH support - chenryn, jfried * custom user for sudo command * new function: delete_lines_according_to * new feature flag use_server_auth * improved ini file parsing * support for custom server parameters * cwd option for run command * speed improvements: #123, #133, #135, #136, #137, #143 - liedekef * removed blastwave package capabilities from solaris, because blastwave doesn't exists anymore * #129 - removed an unnecessary opendir call - liedekef * #148 - use equery for Rex::Pkg::Gentoo in get_installed, later replaced by #165 - tianon * #149 - updated most of Rsync with server-specific auth - tianon * #159 - don't execute a task if the defined group doesn't contain servers. There is a feature flag to disable this behavior (empty_groups) * #163 - Rex::Pkg::Gentoo: Fix separator character between package name and version * #165 - Rex::Pkg::Gentoo: Replace get_installed checking method 2013-05-03 Jan Gehring (0.41.3) * fixed using -c flag with Rex::Box - #160 * fixed parsing ssh/config file - #158 * get_box() : better error message - #157 * fixed PATH variable for run() with multiple commands - #156 2013-04-19 Jan Gehring (0.41.2) * fixed exit status code for some situations where it fails * fixed rexify --use=module command * fixinig 'unititialized value' output of inspect() function. returning now 'no value'. - #152 * added check if template result is empty. if so - die() - #152 * fixing problem with invalid variable names in templates - #152 2013-03-30 Jan Gehring (0.41.1) * fixed a parsing bug in df output * fixed mount command with persistend option 2013-03-30 Jan Gehring (0.41.0) * Function to get the last output of a command that uses run() #104 * Refactores Cron module, added environment variable support to cron * New sync module * added Hardware::VirtInfo module #119 - Franky Van Liedekerke * new keyword "case" * Refactored net_ssh2_exec() function - Peter H. Ezetta * Refactored local command execution to use IPC::Open3 * Changed the Debian is_installed function to use the more accurate get_installed function. - Samuele Tognini * '-t' option should work with '-e' option. rex -t 2 -H "hostA hostB" -e 'run "sleep 10"; say run "uptime"' should run in parallel. - Tokuhiro Matsuno * Fixed loading of modules in $HOME/.rex/recipes * Don't calculate md5 sums if there is no on_change hook for file() function - Franky Van Liedekerke * Better understandable error messages for authentication - Franky Van Liedekerke * Cloud/Amazon: support multiple security groups - RenatoCRON * Fix Pod about pubkey authentication - Joris * Rex/Gearman: Fixed get_exit_codes use flag * Rex/Boxes: Creation order of VMs can now be defined in YAML file 2013-03-10 Jan Gehring (0.40.4) * fixed get_box() command if task is run on a remote host * fixed sudo without password 2013-03-09 Jan Gehring (0.40.3) * fixed loading of files in lib directory @INC sometime got populated too late 2013-03-02 Jan Gehring (0.40.2) * fixed #117 - encode everything except a-z, 0-9 and _ 2013-02-27 Jan Gehring (0.40.1) * fixed #114 - used only once warnings * fixed #115 - passwordless sudo didn't work 2013-02-23 Jan Gehring (0.40.0) * fixed bug and refactored file path calculation #103 and #102 * added ini style groups #99 - Franky Van Liedekerke * VirtualBox Headless mode #105 * added default environment "default" * basic cmdb via YAML #107 * crypt sudo password * sudo without locales and password #98 - Dominik Schulz * fixed dmidecode on openbsd * export update_system sub - Ferenc Erki * fixing regex which gets name interfaces - Fran Rodriguez 2013-02-07 Jan Gehring (0.39.0) * don't need PERL5LIB env any more, fixed bug #95 * match comments better - Naveed Massjouni * more allowed characters in lvname - Samuele Tognini * new feature flag: exit_status - rex will now return a number higher than 0 if a task execution fails. * new function: is_installed - Daniel Baeurer * optimized lookup_file function - Franky Van Liedekerke * rexify command now allows usage of private module server 2013-01-27 Jan Gehring (0.38.0) * added security groups to amazon cloud - Jonathan Delgado * updated pod documentation - jorisd * fixed a problem with do_task() and lost ssh connections * rexify command now allows usage of local templates * added architecture and apt key to Pkg::Debian - Daniel Baeurer * added updated_system command to update a system * box: added function to list all vms * box: amazon support * box: describe boxes with an YAML file 2013-01-15 Jan Gehring (0.37.1) * fixed template bug in modules 2013-01-05 Jan Gehring (0.37.0) * box module is now plugable * run_task now accepts additional parameters * virtualization module is now plugable * hardware provider now plugable * package provider now plugable * service provider now plugable * fixed zypper ref call for SuSE on unsigned repositories 2012-12-22 Jan Gehring (0.36.0) * added run_task a new function to run tasks on specific hosts * added feature to install perl dependencies via cpanm/cpan with rexify command * get private IP of amazon ec2 instances - jdelgado7 * added possibility to extend cloud api with external modules * spawn a pty to execute commands #80. this fixes the requiretty thing. 2012-12-22 Jan Gehring (0.35.1) * VirtualBox support for virtualization module * new command - Rex::Commands::Box * auth for - supports regular expresions * repository function now supports multiple distributions in one call * Unwrapped double looping over files and regexes and adding $new_line if not present in absence of regexes - Mario Domgoergen * Better handling of $option parameter for 'install' - joris 2012-12-14 Jan Gehring (0.34.2) * fixed login for Rex::connect 2012-11-25 Jan Gehring (0.34.1) * fixed #77. added iptables tests - Dominik Danter * Relax ssh config file parsing - Dominik Schulz 2012-11-02 Jan Gehring (0.34.0) * Use Storable module for shared variables * Enhanced lookup_file function. Comments (#) and empty lines are now skipped. (chenryn) * get_network_devices now detects ppp devices (under linux). (bokutin) * Fixed pkg_info execution on FreeBSD (bokutin) * Set $? to match effective command return value (joris) * If a task is defined multiple times it will now print out a warning. * Fixed mount bug #75 * Fixed Redhat Repo bug #73 * Fixed a bug with profiling and http endpoint 2012-10-04 Jan Gehring (0.33.3) * fixed rename() bug, #67 2012-10-02 Jan Gehring (0.33.2) * fixed windows bug 2012-09-22 Jan Gehring (0.33.1) * speed improvements * profiler class * mounts can now be persisted in /etc/fstab - Laird Liu * the partition command accepts a mount parameter to mount the partition after creation - Laird Liu * new keyword "make" * some usability improvements - Anders Ossowicki * create_user got a new option "no_create_home" * it is now possible to use other template engines. * it is now easier to write independant modules * fixed expire date for create_user * check shell before executing things 2012-08-30 Jan Gehring (0.32.1) * fixed a bug in the transaction module 2012-08-21 Jan Gehring (0.32.0) * made the worker model exchangeable * added a reporting base class * replaced Getops::Std with Rex::Args * added start command to amazon cloud module * fixed stop command in amazon cloud module 2012-08-16 Jan Gehring (0.31.5) * fixed cli parameter bug (-G) * fixed logging bug with %h 2012-08-04 Jan Gehring (0.31.0) * fixed a bug for task with the no_ssh attribute * added http transport layer * added possibility to modify task and server authentication 2012-07-21 Jan Gehring (0.30.1) * fixed a cli parameter bug for custom user authentication * fixed a batch display bug 2012-06-15 Jan Gehring (0.30.0) * rex -T now show the server groups as well * new option "type" for the extract function - Sven Dowideit * Added user_list and user_groups - Jean Charles Passard * fixed the problem with pass_auth and rsync (#30) * Better Error Messages for compile failures in modules * Added support for task specific parallelism * fixed upload and download to work in sudo environments * add mode to extract function * Added the on_change support to the append_if_no_such_line - Samuele Tognini 2012-05-17 Jan Gehring (0.29.0) * Fixed wrong error message in LibVirt/create.pm - Sven Dowideit * Added dumpxml command (LibVirt) - Sven Dowideit * Updated docs - Sven Dowideit * Default listening on all ip's for vnc (LibVirt) - Sven Dowideit * Added more colorized output options - Samuele Tognini * Fixed a logging bug - Samuele Tognini * -Tv command line option output information about requested task - Samuele Tognini * -Tv command line option output information about batches and environments - Samuele Tognini * Updated some error messages output - Samuele Tognini * Added iflist command (LibVirt) - Jean Charles Passard * Added blklist command (LibVirt) - Jean Charles Passard * Added vncdisplay command (LibVirt) - Sven Dowideit * Fixed a bug with hooks and packages #41 - Jan Gehring * Refactored Task Module. Task is now an object - Jan Gehring * Added module to parse cli parameters - Jan Gehring * Added driver_type for kvm disks * fixed a md5 check bug for 'install file =>' * fixed bug #50 (extract function) * if no rexfile is in the current path try to guess the rexfile from the taskname - Sven Dowideit * added experimental feature: shared variables - Jan Gehring 2012-05-08 Jan Gehring (0.28.0) * fixed a bug with relative source file names inside external modules * new parameter -Tv to display more information about tasks * allow additional parameters for rsync * more code refactoring * fixed a bug in the libvirt module (thanks to SvenDowideit for reporting and testing) 2012-05-04 Jan Gehring (0.27.0) * added callback parameter to run command * added logformat function to define custom logging * reworked the output classes for better jenkins integration * code refactoring * added coloriszed output if Term::ANSIColor is available * fixed a bug with older lvm versions * added on_change option to sed command, thanks to Samuele Tognini 2012-04-26 Jan Gehring (0.26.3) * fixed a notification bug in the on_change event of the file function. * fixed a cli parameter bug in the rexify command. 2012-03-28 Jan Gehring (0.26.2) * fixed a cli parameter bug (-G) 2012-03-13 Jan Gehring (0.26.1) * fixed a bug in the libvirt module 2012-02-19 Jan Gehring (0.26.0) * changed license to Apache 2.0 * added sudo compatibility * added support for custom init commands * added rex-agent compatibility * added overmind compatibility * added lvm support to libvirt module * allow package installation with "install $pkg" * added sed function * added chdir parameter to extract function * added include function to include Rex recipes without registering the tasks * Specify the sudo password prompt to avoid different prompts in different locales. thanks to Hiroaki Nakamura * Sync exclude option now takes a string or an array of strings. thanks to Hiroaki Nakamura 2012-02-16 Jan Gehring (0.25.3) * fixed a display bug in rexify --search command * fixed a bug in the libvirt module if it gets executed local * fixed a bug in the service module for ubuntu 2012-02-15 Jan Gehring (0.25.2) * display the correct module name in rexify --search command 2012-02-15 Jan Gehring (0.25.1) * don't use github for recipes query 2012-02-15 Jan Gehring (0.25.0) * added public repository commands to rexify * added patch from JEEN Lee for gpgcheck on yum repositories 2012-02-13 Jan Gehring (0.24.1) * fixed a dependeny bug 2012-02-10 Jan Gehring (0.24.0) * Added patches from Alexandr Ciornii for Makefile.PL and home-directory detection * it is now possible to use Rex as a library * fixed/simplified SCM module * added iptables flush command * added a simple tcp alive test * allow inline templates * cloud_instance returns vm info after create * added cli parameters to before/around hooks * fixed before/around/after hooks for lokal tasks * added lvm create functions 2012-01-14 Jan Gehring (0.23.0) * Redhat Enterprise Linux Support (5/6) * read ssh_config file * rsync now automatically accept keys 2012-01-04 Jan Gehring (0.22.0) * systemd service provider (for redhat and suse) * before, around and after hooks for tasks * curl: Allow connections to SSL sites without certs * don't override db config if no import options given * fixed suse detection bug * user: set crypted passwords * added OpenSuSE 12.1 compatibility * fixed redhat versiond detection * automatically use systemd service class if opensuse >= 12.1 * added fusioninventory-agent output to the inventory module (if available) 2011-10-28 Jan Gehring (0.21.1) * fix for #8 - HOME environment variable on Windows * fix for #5 - hostname evaluation with ips 2011-10-10 Jan Gehring (0.21.0) * fixed running of multiple tasks by do_task * allow multiple groups for a task * every task can have its own auth information * user module: add ssh key * ssh port isn't fix anymore (patch from Jose Luis Martinez) * use generic auth method from Net::SSH2 (patch from Jose Luis Martinez) * add SCM module (Subversion and Git) * file and upload now scans for environment specifiy files first * added a file lookup function to build groups from * fixed windows syslog bug #6, thanks to aero * added -nolog parameter to logging function to disable logging at all * added posibility to evaluate perl code within the -H cli parameter 2011-09-16 Jan Gehring (0.20.0) * added virtualization module (from Sascha Guenther) * added extract function * flattend hardware gather template variables * fixed set_path and get_path * fixed get_random to return not 1 char too much * added set and get commands to set config values 2011-09-01 Jan Gehring (0.19.0) * added JUnit output module * added environment support * load Rex::Commands::Process as default 2011-09-01 Jan Gehring (0.18.1) * fixed a bug registering tasks as functions 2011-09-01 Jan Gehring (0.18) * added network support for Solaris, NetBSD, FreeBSD and OpenBSD * added is_solaris, is_bsd and is_linux function 2011-09-01 Jan Gehring (0.17) * added solaris 11 support * added solaris 10 support * added a caching module * added a clear task function (for rex-swarm) * added a function to get os release * fixed local copy error handling 2011-08-28 Jan Gehring (0.16) * added NetBSD support * added OpenBSD support * fixed a bug in the gentoo pkg management module 2011-08-07 Jan Gehring (0.15) * new function to detect a redhat system (or clone like CentOS, Scientific Linux) * increased timeouts for jiffybox * fixed template bug with $ signs * added support for scientific linux * added support for gentoo 2011-08-07 Jan Gehring (0.14.0) * Extended API to allow passing of arguments to Rex::Task->run * FreeBSD support * Ubuntu support 2011-08-07 Jan Gehring (0.13.0) * cache sftp object - for speed * added function to update package database * added windows support * license changed to GPL3 * added an alias for unlink (rm) * added functions to manage repositories * revised error handling * added jiffybox support, a german cloudservice from domainfactory * fixed template parsing bug (port from 0.12.1) * fixed bug with too long content in file function (port from 0.12.2) 2011-07-23 Jan Gehring (0.12.0) * allow array refs for Pkg::remove * register every task as a sub if not in main package * use lsb_release if available as default to detect operating system/version * added sudo command * allow to manage multiple services at once * added possibility to add and remove services from runlevels * added iptables module for basic iptables commands * added cloud layer and support for amazon ec2 instances 2011-07-26 Jan Gehring (0.11.1) * fixed output of netstat (reported by Thomas Biege) * fixed inclusion of some modules in Run.pm that causes errors under some circumstances (reported by Thomas Biege) 2011-07-22 Jan Gehring (0.11.0) * added lvm module * added lvm to inventory * fixed inventory string * fixed multiplicator for GB and TB * added order key to selects * added support for hpacucli * added centos 6 support 2011-07-17 Jan Gehring (0.10.1) * fixed db disconnect on forks * fixed some typos 2011-07-12 Jan Gehring (0.10.0) * added network module for route, default gateway and netstat * added mount and umount function * added cron module * added more information (basic system information) to the inventor function * added installed_packages function to get all the installed packages 2011-07-03 Jan Gehring (0.9.0) * register tasks as function if possible * add "lib" to INC if exists * added function get_operating_system * added transactions * deprecated "package file =>" * added hal module to access hardware information detected by hal * added dmidecode module to access bios information * added inventory function "inventor" * added ubuntu support (tested with lts 10.04) * added can_run function, to test if a command is present 2011-07-03 Jan Gehring (0.8.1) * fixed mageia detection * fixed bug if dnsdomainname returns no domainname * fixed mkdir bug on setting permissions, caused by a wrong merge 2011-06-26 Jan Gehring (0.8.0) * added mageia support for services and packages * added chown, chgrp and chmod functions * mkdir, added possibility to specify the permission, the user and the group * added function delete_lines_matching * added function append_if_no_such_line * added reload action for services * extended db module to support insert, delete, update 2011-06-25 Jan Gehring (0.7.1) * restored the backward compatibility with perl 5.8.x * suppress warning if no parameter is given * fixed mkdir function 2011-06-23 Jan Gehring (0.7.0) * preload a lot more default modules * added new functions (df, du, cp) * added some aliases (ln, cp, cd, ls) * added process management functions (kill, killall, nice, ps) * splitted out rex-agent and rex-master. 2011-06-19 Jan Gehring (0.6.1) * fixed documentation bugs (thanks to djill) * fixed #68827, rewrote is_readable/is_writable * handle auth failure correctly * mkdir now created directories recursive hooks.t100644001751001751 262413232707066 12627 0ustar00janjan000000000000Rex-1.6.0/tuse strict; use warnings; BEGIN { use Test::More tests => 8; use File::Temp; use Rex::Commands; use Rex::RunList; use Rex::Shared::Var; share qw( $before_task_start_all $before_task_start $before_all $before $after $after_all $after_task_finished $after_task_finished_all); } $::QUIET = 1; timeout 1; task foo => "asdfsadfasdf" => sub { return "yo" }; before_task_start ALL => sub { $before_task_start_all += 1 }; before_task_start foo => sub { $before_task_start += 1 }; before ALL => sub { $before_all += 1 }; before foo => sub { $before += 1 }; after foo => sub { $after += 1 }; after ALL => sub { $after_all += 1 }; after_task_finished foo => sub { $after_task_finished += 1 }; after_task_finished ALL => sub { $after_task_finished_all += 1 }; @ARGV = qw(foo); my $run_list = Rex::RunList->instance; $run_list->parse_opts(@ARGV); $run_list->run_tasks; is $before_task_start_all, 1, 'before_task_start ALL hook'; is $before_task_start, 1, 'before_task_start hook'; is $before_all, 1, 'before ALL hook'; is $before, 1, 'before hook'; is $after, 1, 'after hook'; is $after_all, 1, 'after ALL hook'; is $after_task_finished, 1, 'after_task_finished hook'; is $after_task_finished_all, 1, 'after_task_finished ALL hook'; ip.out3100644001751001751 23213232707066 12514 0ustar00janjan000000000000Rex-1.6.0/t4: ppp0: mtu 1454 qdisc pfifo_fast qlen 3 link/ppp inet 123.117.251.17 peer 234.165.249.179/32 scope global ppp0 ip.out1100644001751001751 30613232707066 12514 0ustar00janjan000000000000Rex-1.6.0/t2: wlp2s0: mtu 1500 qdisc mq state UP qlen 1000 link/ether aa:bb:cc:dd:ee:ff brd ff:ff:ff:ff:ff:ff inet 10.20.30.40/24 brd 10.20.30.255 scope global wlp2s0 needs.t100644001751001751 213413232707066 12576 0ustar00janjan000000000000Rex-1.6.0/tuse Test::More; use Rex::Commands; { package MyTest; use strict; use warnings; use Rex::Commands; $::QUIET = 1; task "test1", sub { open( my $fh, ">", "test1.txt" ); close($fh); }; task "test2", sub { open( my $fh, ">", "test2.txt" ); close($fh); }; 1; } task "test", sub { needs MyTest; if ( -f "test1.txt" && -f "test2.txt" ) { unlink("test1.txt"); unlink("test2.txt"); return 1; } die; }; task "test2", sub { needs MyTest "test2"; if ( -f "test2.txt" ) { unlink("test2.txt"); return 1; } die; }; task "test3", sub { needs("test4"); if ( -f "test4.txt" ) { unlink("test4.txt"); return 1; } die; }; task "test4", sub { open( my $fh, ">", "test4.txt" ); close($fh); }; my $task_list = Rex::TaskList->create; my $run_list = Rex::RunList->instance; $run_list->parse_opts(qw/test test2 test3/); for my $task ( $run_list->tasks ) { $task_list->run($task); my @summary = $task_list->get_summary; is_deeply $summary[-1]->{exit_code}, 0, $task->name; $run_list->increment_current_index; } done_testing; cron.ex100644001751001751 130013232707066 12604 0ustar00janjan000000000000Rex-1.6.0/t#----------------------------------------------------------------- # Shell variable for cron SHELL=/bin/bash # PATH variable for cron PATH=/usr/local/bin:/usr/local/sbin:/sbin:/usr/sbin:/bin:/usr/bin:/usr/bin/X11 MYVAR="foo=bar" #M S T M W Befehl #----------------------------------------------------------------- 5 9-20 * * * /home/username/script/script1.sh > /dev/null */10 * * * * /usr/bin/script2.sh > /dev/null 2>&1 59 23 * * 0,4 cp /pfad/zu/datei /pfad/zur/kopie * * * * * DISPLAY=:0 LANG=de_DE.UTF-8 zenity --info --text "Beispiel für das Starten eines Programmes mit GUI" 0 0 * * * backup #----------------------------------------------------------------- df.out1100644001751001751 43613232707066 12501 0ustar00janjan000000000000Rex-1.6.0/tFilesystem 1K-blocks Used Available Use% Mounted on /dev/mapper/vg_c6test0232-lv_root 18102140 1693244 15489344 10% / tmpfs 255160 0 255160 0% /dev/shm /dev/sda1 495844 67557 402687 15% /boot ip.out2100644001751001751 17713232707066 12523 0ustar00janjan000000000000Rex-1.6.0/t3: eth1: mtu 1500 qdisc noop state DOWN qlen 1000 link/ether 00:1c:42:73:ad:3c brd ff:ff:ff:ff:ff:ff batch.t100644001751001751 76613232707066 12552 0ustar00janjan000000000000Rex-1.6.0/tuse Test::More tests => 2; use Rex::Commands; use Rex::Batch; desc("Test"); task( "test", "server01", "server02", sub { } ); desc("Test 2"); task( "test2", "fe[01..10]", sub { } ); batch( "mybatch", "test", "test2" ); my @batches = Rex::Batch->get_batchs(); my @task_names = Rex::Batch->get_batch("mybatch"); is_deeply( \@batches, ["mybatch"], "Batch 'mybatch' registered successfully." ); is_deeply( \@task_names, [ "test", "test2" ], "Batch 'mybatch' has all tasks." ); group.t100644001751001751 341713232707066 12641 0ustar00janjan000000000000Rex-1.6.0/tuse strict; use warnings; use Test::More tests => 95; use Rex -feature => '0.31'; delete $ENV{REX_USER}; user("root3"); password("pass3"); private_key("priv.key3"); public_key("pub.key3"); no warnings; $::FORCE_SERVER = "server1 foo[01..10]"; use warnings; group( "forcetest1", "bla1", "blah2", "bla1" ); task( "tasktest3", "group", "forcetest1", sub { } ); my @servers = Rex::Group->get_group("forcetest1"); is( $servers[0], "bla1", "forceserver - 1" ); ok( !defined $servers[2], "group - servername uniq" ); my $task = Rex::TaskList->create()->get_task("tasktest3"); my @all_server = @{ $task->server }; is( $all_server[0], "server1", "forceserver - task - 0" ); is( $all_server[1], "foo01", "forceserver - task - 1" ); is( $all_server[5], "foo05", "forceserver - task - 5" ); is( $all_server[10], "foo10", "forceserver - task - 10" ); for my $server (@all_server) { my $auth = $task->merge_auth($server); is( $auth->{user}, "root3", "merge_auth - user" ); is( $auth->{password}, "pass3", "merge_auth - pass" ); is( $auth->{public_key}, "pub.key3", "merge_auth - pub" ); is( $auth->{private_key}, "priv.key3", "merge_auth - priv" ); } auth( for => "tasktest3", user => "jan", password => "foo" ); for my $server (@all_server) { my $auth = $task->merge_auth($server); is( $auth->{user}, "jan", "merge_auth - user" ); is( $auth->{password}, "foo", "merge_auth - pass" ); is( $auth->{public_key}, "pub.key3", "merge_auth - pub" ); is( $auth->{private_key}, "priv.key3", "merge_auth - priv" ); } group( "duplicated_by_list", "s[1..3,2..4]" ); my @cleaned_servers = Rex::Group->get_group("duplicated_by_list"); is_deeply [ $cleaned_servers[0]->get_servers ], [ qw/ s1 s2 s3 s4 / ], "duplicated_by_list"; df.out2100644001751001751 37413232707066 12503 0ustar00janjan000000000000Rex-1.6.0/tFilesystem 1K-blocks Used Available Use% Mounted on /dev/sda2 18102140 1693244 15489344 10% / tmpfs 255160 0 255160 0% /dev/shm /dev/sda1 495844 67557 402687 15% /boot META.json100644001751001751 13236613232707066 12544 0ustar00janjan000000000000Rex-1.6.0{ "abstract" : "Remote Execution", "author" : [ "Jan Gehring " ], "dynamic_config" : 1, "generated_by" : "Dist::Zilla version 6.010, CPAN::Meta::Converter version 2.150010", "license" : [ "apache_2_0" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "Rex", "prereqs" : { "build" : { "requires" : { "Test::Pod" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "develop" : { "requires" : { "Test::MinimumVersion" : "0", "Test::Perl::Critic" : "0", "Test::Pod" : "1.41" } }, "runtime" : { "requires" : { "AWS::Signature4" : "0", "Carp" : "0", "Cwd" : "0", "Data::Dumper" : "0", "Data::Validate::IP" : "0", "Devel::Caller" : "0", "Digest::HMAC_SHA1" : "0", "Digest::MD5" : "0", "Exporter" : "0", "Fcntl" : "0", "File::Basename" : "0", "File::Spec" : "0", "File::Spec::Unix" : "0", "File::Spec::Win32" : "0", "FindBin" : "0", "HTTP::Request" : "0", "HTTP::Request::Common" : "0", "Hash::Merge" : "0", "IO::File" : "0", "IO::Select" : "0", "IO::Socket" : "0", "IO::String" : "0", "IPC::Open3" : "0", "JSON::MaybeXS" : "0", "LWP::UserAgent" : "0", "List::MoreUtils" : "0", "List::Util" : "0", "MIME::Base64" : "0", "Net::OpenSSH::ShellQuoter" : "0", "POSIX" : "0", "Scalar::Util" : "0", "Sort::Naturally" : "0", "Storable" : "0", "Symbol" : "0", "Term::ReadKey" : "0", "Test::Builder::Module" : "0", "Text::Glob" : "0", "Text::Wrap" : "0", "Time::HiRes" : "0", "UNIVERSAL" : "0", "URI" : "0", "URI::QueryParam" : "0", "XML::Simple" : "0", "YAML" : "0", "attributes" : "0", "base" : "0", "constant" : "0", "lib" : "0", "overload" : "0", "perl" : "5.008008", "strict" : "0", "vars" : "0", "version" : "0", "warnings" : "0" } }, "test" : { "requires" : { "File::Temp" : "0", "Parallel::ForkManager" : "0", "String::Escape" : "0", "Test::Deep" : "0", "Test::More" : "0", "Test::UseAllModules" : "0" } } }, "provides" : { "Rex" : { "file" : "lib/Rex.pm", "version" : "v1.6.0" }, "Rex::Args" : { "file" : "lib/Rex/Args.pm", "version" : "v1.6.0" }, "Rex::Args::Integer" : { "file" : "lib/Rex/Args/Integer.pm", "version" : "v1.6.0" }, "Rex::Args::Single" : { "file" : "lib/Rex/Args/Single.pm", "version" : "v1.6.0" }, "Rex::Args::String" : { "file" : "lib/Rex/Args/String.pm", "version" : "v1.6.0" }, "Rex::Batch" : { "file" : "lib/Rex/Batch.pm", "version" : "v1.6.0" }, "Rex::Box" : { "file" : "lib/Rex/Box.pm", "version" : "v1.6.0" }, "Rex::Box::Amazon" : { "file" : "lib/Rex/Box/Amazon.pm", "version" : "v1.6.0" }, "Rex::Box::Base" : { "file" : "lib/Rex/Box/Base.pm", "version" : "v1.6.0" }, "Rex::Box::Docker" : { "file" : "lib/Rex/Box/Docker.pm", "version" : "v1.6.0" }, "Rex::Box::KVM" : { "file" : "lib/Rex/Box/KVM.pm", "version" : "v1.6.0" }, "Rex::Box::VBox" : { "file" : "lib/Rex/Box/VBox.pm", "version" : "v1.6.0" }, "Rex::CLI" : { "file" : "lib/Rex/CLI.pm", "version" : "v1.6.0" }, "Rex::CMDB" : { "file" : "lib/Rex/CMDB.pm", "version" : "v1.6.0" }, "Rex::CMDB::Base" : { "file" : "lib/Rex/CMDB/Base.pm", "version" : "v1.6.0" }, "Rex::CMDB::YAML" : { "file" : "lib/Rex/CMDB/YAML.pm", "version" : "v1.6.0" }, "Rex::Cloud" : { "file" : "lib/Rex/Cloud.pm", "version" : "v1.6.0" }, "Rex::Cloud::Amazon" : { "file" : "lib/Rex/Cloud/Amazon.pm", "version" : "v1.6.0" }, "Rex::Cloud::Base" : { "file" : "lib/Rex/Cloud/Base.pm", "version" : "v1.6.0" }, "Rex::Cloud::Jiffybox" : { "file" : "lib/Rex/Cloud/Jiffybox.pm", "version" : "v1.6.0" }, "Rex::Cloud::OpenStack" : { "file" : "lib/Rex/Cloud/OpenStack.pm", "version" : "v1.6.0" }, "Rex::Commands" : { "file" : "lib/Rex/Commands.pm", "version" : "v1.6.0" }, "Rex::Commands::Augeas" : { "file" : "lib/Rex/Commands/Augeas.pm", "version" : "v1.6.0" }, "Rex::Commands::Box" : { "file" : "lib/Rex/Commands/Box.pm", "version" : "v1.6.0" }, "Rex::Commands::Cloud" : { "file" : "lib/Rex/Commands/Cloud.pm", "version" : "v1.6.0" }, "Rex::Commands::Cron" : { "file" : "lib/Rex/Commands/Cron.pm", "version" : "v1.6.0" }, "Rex::Commands::DB" : { "file" : "lib/Rex/Commands/DB.pm", "version" : "v1.6.0" }, "Rex::Commands::Download" : { "file" : "lib/Rex/Commands/Download.pm", "version" : "v1.6.0" }, "Rex::Commands::File" : { "file" : "lib/Rex/Commands/File.pm", "version" : "v1.6.0" }, "Rex::Commands::Fs" : { "file" : "lib/Rex/Commands/Fs.pm", "version" : "v1.6.0" }, "Rex::Commands::Gather" : { "file" : "lib/Rex/Commands/Gather.pm", "version" : "v1.6.0" }, "Rex::Commands::Host" : { "file" : "lib/Rex/Commands/Host.pm", "version" : "v1.6.0" }, "Rex::Commands::Inventory" : { "file" : "lib/Rex/Commands/Inventory.pm", "version" : "v1.6.0" }, "Rex::Commands::Iptables" : { "file" : "lib/Rex/Commands/Iptables.pm", "version" : "v1.6.0" }, "Rex::Commands::JobControl" : { "file" : "lib/Rex/Commands/JobControl.pm", "version" : "v1.6.0" }, "Rex::Commands::Kernel" : { "file" : "lib/Rex/Commands/Kernel.pm", "version" : "v1.6.0" }, "Rex::Commands::LVM" : { "file" : "lib/Rex/Commands/LVM.pm", "version" : "v1.6.0" }, "Rex::Commands::MD5" : { "file" : "lib/Rex/Commands/MD5.pm", "version" : "v1.6.0" }, "Rex::Commands::Mkfs" : { "file" : "lib/Rex/Commands/Mkfs.pm", "version" : "v1.6.0" }, "Rex::Commands::Network" : { "file" : "lib/Rex/Commands/Network.pm", "version" : "v1.6.0" }, "Rex::Commands::Notify" : { "file" : "lib/Rex/Commands/Notify.pm", "version" : "v1.6.0" }, "Rex::Commands::Partition" : { "file" : "lib/Rex/Commands/Partition.pm", "version" : "v1.6.0" }, "Rex::Commands::Pkg" : { "file" : "lib/Rex/Commands/Pkg.pm", "version" : "v1.6.0" }, "Rex::Commands::PkgConf" : { "file" : "lib/Rex/Commands/PkgConf.pm", "version" : "v1.6.0" }, "Rex::Commands::Process" : { "file" : "lib/Rex/Commands/Process.pm", "version" : "v1.6.0" }, "Rex::Commands::Rsync" : { "file" : "lib/Rex/Commands/Rsync.pm", "version" : "v1.6.0" }, "Rex::Commands::Run" : { "file" : "lib/Rex/Commands/Run.pm", "version" : "v1.6.0" }, "Rex::Commands::SCM" : { "file" : "lib/Rex/Commands/SCM.pm", "version" : "v1.6.0" }, "Rex::Commands::Service" : { "file" : "lib/Rex/Commands/Service.pm", "version" : "v1.6.0" }, "Rex::Commands::SimpleCheck" : { "file" : "lib/Rex/Commands/SimpleCheck.pm", "version" : "v1.6.0" }, "Rex::Commands::Sync" : { "file" : "lib/Rex/Commands/Sync.pm", "version" : "v1.6.0" }, "Rex::Commands::Sysctl" : { "file" : "lib/Rex/Commands/Sysctl.pm", "version" : "v1.6.0" }, "Rex::Commands::Tail" : { "file" : "lib/Rex/Commands/Tail.pm", "version" : "v1.6.0" }, "Rex::Commands::Upload" : { "file" : "lib/Rex/Commands/Upload.pm", "version" : "v1.6.0" }, "Rex::Commands::User" : { "file" : "lib/Rex/Commands/User.pm", "version" : "v1.6.0" }, "Rex::Commands::Virtualization" : { "file" : "lib/Rex/Commands/Virtualization.pm", "version" : "v1.6.0" }, "Rex::Config" : { "file" : "lib/Rex/Config.pm", "version" : "v1.6.0" }, "Rex::Constants" : { "file" : "lib/Rex/Constants.pm", "version" : "v1.6.0" }, "Rex::Cron" : { "file" : "lib/Rex/Cron.pm", "version" : "v1.6.0" }, "Rex::Cron::Base" : { "file" : "lib/Rex/Cron/Base.pm", "version" : "v1.6.0" }, "Rex::Cron::FreeBSD" : { "file" : "lib/Rex/Cron/FreeBSD.pm", "version" : "v1.6.0" }, "Rex::Cron::Linux" : { "file" : "lib/Rex/Cron/Linux.pm", "version" : "v1.6.0" }, "Rex::Cron::SunOS" : { "file" : "lib/Rex/Cron/SunOS.pm", "version" : "v1.6.0" }, "Rex::Exporter" : { "file" : "lib/Rex/Exporter.pm", "version" : "v1.6.0" }, "Rex::FS::File" : { "file" : "lib/Rex/FS/File.pm", "version" : "v1.6.0" }, "Rex::File::Parser::Data" : { "file" : "lib/Rex/File/Parser/Data.pm", "version" : "v1.6.0" }, "Rex::File::Parser::Ini" : { "file" : "lib/Rex/File/Parser/Ini.pm", "version" : "v1.6.0" }, "Rex::Fork::Manager" : { "file" : "lib/Rex/Fork/Manager.pm", "version" : "v1.6.0" }, "Rex::Fork::Task" : { "file" : "lib/Rex/Fork/Task.pm", "version" : "v1.6.0" }, "Rex::Group" : { "file" : "lib/Rex/Group.pm", "version" : "v1.6.0" }, "Rex::Group::Entry::Server" : { "file" : "lib/Rex/Group/Entry/Server.pm", "version" : "v1.6.0" }, "Rex::Group::Lookup::Command" : { "file" : "lib/Rex/Group/Lookup/Command.pm", "version" : "v1.6.0" }, "Rex::Group::Lookup::DBI" : { "file" : "lib/Rex/Group/Lookup/DBI.pm", "version" : "v1.6.0" }, "Rex::Group::Lookup::File" : { "file" : "lib/Rex/Group/Lookup/File.pm", "version" : "v1.6.0" }, "Rex::Group::Lookup::INI" : { "file" : "lib/Rex/Group/Lookup/INI.pm", "version" : "v1.6.0" }, "Rex::Group::Lookup::XML" : { "file" : "lib/Rex/Group/Lookup/XML.pm", "version" : "v1.6.0" }, "Rex::Group::Lookup::YAML" : { "file" : "lib/Rex/Group/Lookup/YAML.pm", "version" : "v1.6.0" }, "Rex::Hardware" : { "file" : "lib/Rex/Hardware.pm", "version" : "v1.6.0" }, "Rex::Hardware::Host" : { "file" : "lib/Rex/Hardware/Host.pm", "version" : "v1.6.0" }, "Rex::Hardware::Kernel" : { "file" : "lib/Rex/Hardware/Kernel.pm", "version" : "v1.6.0" }, "Rex::Hardware::Memory" : { "file" : "lib/Rex/Hardware/Memory.pm", "version" : "v1.6.0" }, "Rex::Hardware::Network" : { "file" : "lib/Rex/Hardware/Network.pm", "version" : "v1.6.0" }, "Rex::Hardware::Network::Darwin" : { "file" : "lib/Rex/Hardware/Network/Darwin.pm", "version" : "v1.6.0" }, "Rex::Hardware::Network::FreeBSD" : { "file" : "lib/Rex/Hardware/Network/FreeBSD.pm", "version" : "v1.6.0" }, "Rex::Hardware::Network::Linux" : { "file" : "lib/Rex/Hardware/Network/Linux.pm", "version" : "v1.6.0" }, "Rex::Hardware::Network::NetBSD" : { "file" : "lib/Rex/Hardware/Network/NetBSD.pm", "version" : "v1.6.0" }, "Rex::Hardware::Network::OpenBSD" : { "file" : "lib/Rex/Hardware/Network/OpenBSD.pm", "version" : "v1.6.0" }, "Rex::Hardware::Network::Solaris" : { "file" : "lib/Rex/Hardware/Network/Solaris.pm", "version" : "v1.6.0" }, "Rex::Hardware::Swap" : { "file" : "lib/Rex/Hardware/Swap.pm", "version" : "v1.6.0" }, "Rex::Hardware::VirtInfo" : { "file" : "lib/Rex/Hardware/VirtInfo.pm", "version" : "v1.6.0" }, "Rex::Helper::Array" : { "file" : "lib/Rex/Helper/Array.pm", "version" : "v1.6.0" }, "Rex::Helper::DBI" : { "file" : "lib/Rex/Helper/DBI.pm", "version" : "v1.6.0" }, "Rex::Helper::Encode" : { "file" : "lib/Rex/Helper/Encode.pm", "version" : "v1.6.0" }, "Rex::Helper::File::Spec" : { "file" : "lib/Rex/Helper/File/Spec.pm", "version" : "v1.6.0" }, "Rex::Helper::File::Stat" : { "file" : "lib/Rex/Helper/File/Stat.pm", "version" : "v1.6.0" }, "Rex::Helper::File::Stat::Unix" : { "file" : "lib/Rex/Helper/File/Stat/Unix.pm", "version" : "v1.6.0" }, "Rex::Helper::File::Stat::Win32" : { "file" : "lib/Rex/Helper/File/Stat/Win32.pm", "version" : "v1.6.0" }, "Rex::Helper::Hash" : { "file" : "lib/Rex/Helper/Hash.pm", "version" : "v1.6.0" }, "Rex::Helper::INI" : { "file" : "lib/Rex/Helper/INI.pm", "version" : "v1.6.0" }, "Rex::Helper::IP" : { "file" : "lib/Rex/Helper/IP.pm", "version" : "v1.6.0" }, "Rex::Helper::Misc" : { "file" : "lib/Rex/Helper/Misc.pm", "version" : "v1.6.0" }, "Rex::Helper::Path" : { "file" : "lib/Rex/Helper/Path.pm", "version" : "v1.6.0" }, "Rex::Helper::Rexfile::ParamLookup" : { "file" : "lib/Rex/Helper/Rexfile/ParamLookup.pm", "version" : "v1.6.0" }, "Rex::Helper::Run" : { "file" : "lib/Rex/Helper/Run.pm", "version" : "v1.6.0" }, "Rex::Helper::SSH2" : { "file" : "lib/Rex/Helper/SSH2.pm", "version" : "v1.6.0" }, "Rex::Helper::SSH2::Expect" : { "file" : "lib/Rex/Helper/SSH2/Expect.pm", "version" : "v1.6.0" }, "Rex::Helper::System" : { "file" : "lib/Rex/Helper/System.pm", "version" : "v1.6.0" }, "Rex::Helper::URI" : { "file" : "lib/Rex/Helper/URI.pm", "version" : "v1.6.0" }, "Rex::Helper::UserAgent" : { "file" : "lib/Rex/Helper/UserAgent.pm", "version" : "v1.6.0" }, "Rex::Hook" : { "file" : "lib/Rex/Hook.pm", "version" : "v1.6.0" }, "Rex::Interface::Cache" : { "file" : "lib/Rex/Interface/Cache.pm", "version" : "v1.6.0" }, "Rex::Interface::Cache::Base" : { "file" : "lib/Rex/Interface/Cache/Base.pm", "version" : "v1.6.0" }, "Rex::Interface::Cache::YAML" : { "file" : "lib/Rex/Interface/Cache/YAML.pm", "version" : "v1.6.0" }, "Rex::Interface::Connection" : { "file" : "lib/Rex/Interface/Connection.pm", "version" : "v1.6.0" }, "Rex::Interface::Connection::Base" : { "file" : "lib/Rex/Interface/Connection/Base.pm", "version" : "v1.6.0" }, "Rex::Interface::Connection::Fake" : { "file" : "lib/Rex/Interface/Connection/Fake.pm", "version" : "v1.6.0" }, "Rex::Interface::Connection::HTTP" : { "file" : "lib/Rex/Interface/Connection/HTTP.pm", "version" : "v1.6.0" }, "Rex::Interface::Connection::HTTPS" : { "file" : "lib/Rex/Interface/Connection/HTTPS.pm", "version" : "v1.6.0" }, "Rex::Interface::Connection::Local" : { "file" : "lib/Rex/Interface/Connection/Local.pm", "version" : "v1.6.0" }, "Rex::Interface::Connection::OpenSSH" : { "file" : "lib/Rex/Interface/Connection/OpenSSH.pm", "version" : "v1.6.0" }, "Rex::Interface::Connection::SSH" : { "file" : "lib/Rex/Interface/Connection/SSH.pm", "version" : "v1.6.0" }, "Rex::Interface::Exec" : { "file" : "lib/Rex/Interface/Exec.pm", "version" : "v1.6.0" }, "Rex::Interface::Exec::Base" : { "file" : "lib/Rex/Interface/Exec/Base.pm", "version" : "v1.6.0" }, "Rex::Interface::Exec::HTTP" : { "file" : "lib/Rex/Interface/Exec/HTTP.pm", "version" : "v1.6.0" }, "Rex::Interface::Exec::IOReader" : { "file" : "lib/Rex/Interface/Exec/IOReader.pm", "version" : "v1.6.0" }, "Rex::Interface::Exec::Local" : { "file" : "lib/Rex/Interface/Exec/Local.pm", "version" : "v1.6.0" }, "Rex::Interface::Exec::OpenSSH" : { "file" : "lib/Rex/Interface/Exec/OpenSSH.pm", "version" : "v1.6.0" }, "Rex::Interface::Exec::SSH" : { "file" : "lib/Rex/Interface/Exec/SSH.pm", "version" : "v1.6.0" }, "Rex::Interface::Exec::Sudo" : { "file" : "lib/Rex/Interface/Exec/Sudo.pm", "version" : "v1.6.0" }, "Rex::Interface::Executor" : { "file" : "lib/Rex/Interface/Executor.pm", "version" : "v1.6.0" }, "Rex::Interface::Executor::Base" : { "file" : "lib/Rex/Interface/Executor/Base.pm", "version" : "v1.6.0" }, "Rex::Interface::Executor::Default" : { "file" : "lib/Rex/Interface/Executor/Default.pm", "version" : "v1.6.0" }, "Rex::Interface::File" : { "file" : "lib/Rex/Interface/File.pm", "version" : "v1.6.0" }, "Rex::Interface::File::Base" : { "file" : "lib/Rex/Interface/File/Base.pm", "version" : "v1.6.0" }, "Rex::Interface::File::HTTP" : { "file" : "lib/Rex/Interface/File/HTTP.pm", "version" : "v1.6.0" }, "Rex::Interface::File::Local" : { "file" : "lib/Rex/Interface/File/Local.pm", "version" : "v1.6.0" }, "Rex::Interface::File::OpenSSH" : { "file" : "lib/Rex/Interface/File/OpenSSH.pm", "version" : "v1.6.0" }, "Rex::Interface::File::SSH" : { "file" : "lib/Rex/Interface/File/SSH.pm", "version" : "v1.6.0" }, "Rex::Interface::File::Sudo" : { "file" : "lib/Rex/Interface/File/Sudo.pm", "version" : "v1.6.0" }, "Rex::Interface::Fs" : { "file" : "lib/Rex/Interface/Fs.pm", "version" : "v1.6.0" }, "Rex::Interface::Fs::Base" : { "file" : "lib/Rex/Interface/Fs/Base.pm", "version" : "v1.6.0" }, "Rex::Interface::Fs::HTTP" : { "file" : "lib/Rex/Interface/Fs/HTTP.pm", "version" : "v1.6.0" }, "Rex::Interface::Fs::Local" : { "file" : "lib/Rex/Interface/Fs/Local.pm", "version" : "v1.6.0" }, "Rex::Interface::Fs::OpenSSH" : { "file" : "lib/Rex/Interface/Fs/OpenSSH.pm", "version" : "v1.6.0" }, "Rex::Interface::Fs::SSH" : { "file" : "lib/Rex/Interface/Fs/SSH.pm", "version" : "v1.6.0" }, "Rex::Interface::Fs::Sudo" : { "file" : "lib/Rex/Interface/Fs/Sudo.pm", "version" : "v1.6.0" }, "Rex::Interface::Shell" : { "file" : "lib/Rex/Interface/Shell.pm", "version" : "v1.6.0" }, "Rex::Interface::Shell::Ash" : { "file" : "lib/Rex/Interface/Shell/Ash.pm", "version" : "v1.6.0" }, "Rex::Interface::Shell::Base" : { "file" : "lib/Rex/Interface/Shell/Base.pm", "version" : "v1.6.0" }, "Rex::Interface::Shell::Bash" : { "file" : "lib/Rex/Interface/Shell/Bash.pm", "version" : "v1.6.0" }, "Rex::Interface::Shell::Csh" : { "file" : "lib/Rex/Interface/Shell/Csh.pm", "version" : "v1.6.0" }, "Rex::Interface::Shell::Default" : { "file" : "lib/Rex/Interface/Shell/Default.pm", "version" : "v1.6.0" }, "Rex::Interface::Shell::Idrac" : { "file" : "lib/Rex/Interface/Shell/Idrac.pm", "version" : "v1.6.0" }, "Rex::Interface::Shell::Ksh" : { "file" : "lib/Rex/Interface/Shell/Ksh.pm", "version" : "v1.6.0" }, "Rex::Interface::Shell::Sh" : { "file" : "lib/Rex/Interface/Shell/Sh.pm", "version" : "v1.6.0" }, "Rex::Interface::Shell::Tcsh" : { "file" : "lib/Rex/Interface/Shell/Tcsh.pm", "version" : "v1.6.0" }, "Rex::Interface::Shell::Zsh" : { "file" : "lib/Rex/Interface/Shell/Zsh.pm", "version" : "v1.6.0" }, "Rex::Inventory" : { "file" : "lib/Rex/Inventory.pm", "version" : "v1.6.0" }, "Rex::Inventory::Bios" : { "file" : "lib/Rex/Inventory/Bios.pm", "version" : "v1.6.0" }, "Rex::Inventory::DMIDecode" : { "file" : "lib/Rex/Inventory/DMIDecode.pm", "version" : "v1.6.0" }, "Rex::Inventory::DMIDecode::BaseBoard" : { "file" : "lib/Rex/Inventory/DMIDecode/BaseBoard.pm", "version" : "v1.6.0" }, "Rex::Inventory::DMIDecode::Bios" : { "file" : "lib/Rex/Inventory/DMIDecode/Bios.pm", "version" : "v1.6.0" }, "Rex::Inventory::DMIDecode::CPU" : { "file" : "lib/Rex/Inventory/DMIDecode/CPU.pm", "version" : "v1.6.0" }, "Rex::Inventory::DMIDecode::Memory" : { "file" : "lib/Rex/Inventory/DMIDecode/Memory.pm", "version" : "v1.6.0" }, "Rex::Inventory::DMIDecode::MemoryArray" : { "file" : "lib/Rex/Inventory/DMIDecode/MemoryArray.pm", "version" : "v1.6.0" }, "Rex::Inventory::DMIDecode::Section" : { "file" : "lib/Rex/Inventory/DMIDecode/Section.pm", "version" : "v1.6.0" }, "Rex::Inventory::DMIDecode::SystemInformation" : { "file" : "lib/Rex/Inventory/DMIDecode/SystemInformation.pm", "version" : "v1.6.0" }, "Rex::Inventory::HP::ACU" : { "file" : "lib/Rex/Inventory/HP/ACU.pm", "version" : "v1.6.0" }, "Rex::Inventory::Hal" : { "file" : "lib/Rex/Inventory/Hal.pm", "version" : "v1.6.0" }, "Rex::Inventory::Hal::Object" : { "file" : "lib/Rex/Inventory/Hal/Object.pm", "version" : "v1.6.0" }, "Rex::Inventory::Hal::Object::Net" : { "file" : "lib/Rex/Inventory/Hal/Object/Net.pm", "version" : "v1.6.0" }, "Rex::Inventory::Hal::Object::Storage" : { "file" : "lib/Rex/Inventory/Hal/Object/Storage.pm", "version" : "v1.6.0" }, "Rex::Inventory::Hal::Object::Volume" : { "file" : "lib/Rex/Inventory/Hal/Object/Volume.pm", "version" : "v1.6.0" }, "Rex::Inventory::Proc" : { "file" : "lib/Rex/Inventory/Proc.pm", "version" : "v1.6.0" }, "Rex::Inventory::Proc::Cpuinfo" : { "file" : "lib/Rex/Inventory/Proc/Cpuinfo.pm", "version" : "v1.6.0" }, "Rex::Inventory::SMBios" : { "file" : "lib/Rex/Inventory/SMBios.pm", "version" : "v1.6.0" }, "Rex::Inventory::SMBios::BaseBoard" : { "file" : "lib/Rex/Inventory/SMBios/BaseBoard.pm", "version" : "v1.6.0" }, "Rex::Inventory::SMBios::Bios" : { "file" : "lib/Rex/Inventory/SMBios/Bios.pm", "version" : "v1.6.0" }, "Rex::Inventory::SMBios::CPU" : { "file" : "lib/Rex/Inventory/SMBios/CPU.pm", "version" : "v1.6.0" }, "Rex::Inventory::SMBios::Memory" : { "file" : "lib/Rex/Inventory/SMBios/Memory.pm", "version" : "v1.6.0" }, "Rex::Inventory::SMBios::MemoryArray" : { "file" : "lib/Rex/Inventory/SMBios/MemoryArray.pm", "version" : "v1.6.0" }, "Rex::Inventory::SMBios::Section" : { "file" : "lib/Rex/Inventory/SMBios/Section.pm", "version" : "v1.6.0" }, "Rex::Inventory::SMBios::SystemInformation" : { "file" : "lib/Rex/Inventory/SMBios/SystemInformation.pm", "version" : "v1.6.0" }, "Rex::Logger" : { "file" : "lib/Rex/Logger.pm", "version" : "v1.6.0" }, "Rex::Notify" : { "file" : "lib/Rex/Notify.pm", "version" : "v1.6.0" }, "Rex::Output" : { "file" : "lib/Rex/Output.pm", "version" : "v1.6.0" }, "Rex::Output::Base" : { "file" : "lib/Rex/Output/Base.pm", "version" : "v1.6.0" }, "Rex::Output::JUnit" : { "file" : "lib/Rex/Output/JUnit.pm", "version" : "v1.6.0" }, "Rex::Pkg" : { "file" : "lib/Rex/Pkg.pm", "version" : "v1.6.0" }, "Rex::Pkg::ALT" : { "file" : "lib/Rex/Pkg/ALT.pm", "version" : "v1.6.0" }, "Rex::Pkg::Arch" : { "file" : "lib/Rex/Pkg/Arch.pm", "version" : "v1.6.0" }, "Rex::Pkg::Base" : { "file" : "lib/Rex/Pkg/Base.pm", "version" : "v1.6.0" }, "Rex::Pkg::Debian" : { "file" : "lib/Rex/Pkg/Debian.pm", "version" : "v1.6.0" }, "Rex::Pkg::FreeBSD" : { "file" : "lib/Rex/Pkg/FreeBSD.pm", "version" : "v1.6.0" }, "Rex::Pkg::Gentoo" : { "file" : "lib/Rex/Pkg/Gentoo.pm", "version" : "v1.6.0" }, "Rex::Pkg::Mageia" : { "file" : "lib/Rex/Pkg/Mageia.pm", "version" : "v1.6.0" }, "Rex::Pkg::NetBSD" : { "file" : "lib/Rex/Pkg/NetBSD.pm", "version" : "v1.6.0" }, "Rex::Pkg::OpenBSD" : { "file" : "lib/Rex/Pkg/OpenBSD.pm", "version" : "v1.6.0" }, "Rex::Pkg::OpenWrt" : { "file" : "lib/Rex/Pkg/OpenWrt.pm", "version" : "v1.6.0" }, "Rex::Pkg::Redhat" : { "file" : "lib/Rex/Pkg/Redhat.pm", "version" : "v1.6.0" }, "Rex::Pkg::SuSE" : { "file" : "lib/Rex/Pkg/SuSE.pm", "version" : "v1.6.0" }, "Rex::Pkg::SunOS" : { "file" : "lib/Rex/Pkg/SunOS.pm", "version" : "v1.6.0" }, "Rex::Pkg::SunOS::OpenCSW" : { "file" : "lib/Rex/Pkg/SunOS/OpenCSW.pm", "version" : "v1.6.0" }, "Rex::Pkg::SunOS::pkg" : { "file" : "lib/Rex/Pkg/SunOS/pkg.pm", "version" : "v1.6.0" }, "Rex::Pkg::Ubuntu" : { "file" : "lib/Rex/Pkg/Ubuntu.pm", "version" : "v1.6.0" }, "Rex::PkgConf" : { "file" : "lib/Rex/PkgConf.pm", "version" : "v1.6.0" }, "Rex::PkgConf::Base" : { "file" : "lib/Rex/PkgConf/Base.pm", "version" : "v1.6.0" }, "Rex::PkgConf::Debian" : { "file" : "lib/Rex/PkgConf/Debian.pm", "version" : "v1.6.0" }, "Rex::Profiler" : { "file" : "lib/Rex/Profiler.pm", "version" : "v1.6.0" }, "Rex::Report" : { "file" : "lib/Rex/Report.pm", "version" : "v1.6.0" }, "Rex::Report::Base" : { "file" : "lib/Rex/Report/Base.pm", "version" : "v1.6.0" }, "Rex::Report::YAML" : { "file" : "lib/Rex/Report/YAML.pm", "version" : "v1.6.0" }, "Rex::Require" : { "file" : "lib/Rex/Require.pm", "version" : "v1.6.0" }, "Rex::Resource" : { "file" : "lib/Rex/Resource.pm", "version" : "v1.6.0" }, "Rex::Resource::Common" : { "file" : "lib/Rex/Resource/Common.pm", "version" : "v1.6.0" }, "Rex::Resource::firewall" : { "file" : "lib/Rex/Resource/firewall.pm", "version" : "v1.6.0" }, "Rex::Resource::firewall::Provider::base" : { "file" : "lib/Rex/Resource/firewall/Provider/base.pm", "version" : "v1.6.0" }, "Rex::Resource::firewall::Provider::iptables" : { "file" : "lib/Rex/Resource/firewall/Provider/iptables.pm", "version" : "v1.6.0" }, "Rex::Resource::firewall::Provider::ufw" : { "file" : "lib/Rex/Resource/firewall/Provider/ufw.pm", "version" : "v1.6.0" }, "Rex::RunList" : { "file" : "lib/Rex/RunList.pm", "version" : "v1.6.0" }, "Rex::SCM::Git" : { "file" : "lib/Rex/SCM/Git.pm", "version" : "v1.6.0" }, "Rex::SCM::Subversion" : { "file" : "lib/Rex/SCM/Subversion.pm", "version" : "v1.6.0" }, "Rex::Service" : { "file" : "lib/Rex/Service.pm", "version" : "v1.6.0" }, "Rex::Service::ALT" : { "file" : "lib/Rex/Service/ALT.pm", "version" : "v1.6.0" }, "Rex::Service::ALT::systemd" : { "file" : "lib/Rex/Service/ALT/systemd.pm", "version" : "v1.6.0" }, "Rex::Service::Arch::systemd" : { "file" : "lib/Rex/Service/Arch/systemd.pm", "version" : "v1.6.0" }, "Rex::Service::Base" : { "file" : "lib/Rex/Service/Base.pm", "version" : "v1.6.0" }, "Rex::Service::Debian" : { "file" : "lib/Rex/Service/Debian.pm", "version" : "v1.6.0" }, "Rex::Service::Debian::systemd" : { "file" : "lib/Rex/Service/Debian/systemd.pm", "version" : "v1.6.0" }, "Rex::Service::FreeBSD" : { "file" : "lib/Rex/Service/FreeBSD.pm", "version" : "v1.6.0" }, "Rex::Service::Gentoo" : { "file" : "lib/Rex/Service/Gentoo.pm", "version" : "v1.6.0" }, "Rex::Service::Gentoo::systemd" : { "file" : "lib/Rex/Service/Gentoo/systemd.pm", "version" : "v1.6.0" }, "Rex::Service::Mageia" : { "file" : "lib/Rex/Service/Mageia.pm", "version" : "v1.6.0" }, "Rex::Service::Mageia::systemd" : { "file" : "lib/Rex/Service/Mageia/systemd.pm", "version" : "v1.6.0" }, "Rex::Service::NetBSD" : { "file" : "lib/Rex/Service/NetBSD.pm", "version" : "v1.6.0" }, "Rex::Service::OpenBSD" : { "file" : "lib/Rex/Service/OpenBSD.pm", "version" : "v1.6.0" }, "Rex::Service::OpenWrt" : { "file" : "lib/Rex/Service/OpenWrt.pm", "version" : "v1.6.0" }, "Rex::Service::Redhat" : { "file" : "lib/Rex/Service/Redhat.pm", "version" : "v1.6.0" }, "Rex::Service::Redhat::systemd" : { "file" : "lib/Rex/Service/Redhat/systemd.pm", "version" : "v1.6.0" }, "Rex::Service::SuSE" : { "file" : "lib/Rex/Service/SuSE.pm", "version" : "v1.6.0" }, "Rex::Service::SuSE::systemd" : { "file" : "lib/Rex/Service/SuSE/systemd.pm", "version" : "v1.6.0" }, "Rex::Service::SunOS" : { "file" : "lib/Rex/Service/SunOS.pm", "version" : "v1.6.0" }, "Rex::Service::SunOS::svcadm" : { "file" : "lib/Rex/Service/SunOS/svcadm.pm", "version" : "v1.6.0" }, "Rex::Service::Ubuntu" : { "file" : "lib/Rex/Service/Ubuntu.pm", "version" : "v1.6.0" }, "Rex::Shared::Var" : { "file" : "lib/Rex/Shared/Var.pm", "version" : "v1.6.0" }, "Rex::Shared::Var::Array" : { "file" : "lib/Rex/Shared/Var/Array.pm", "version" : "v1.6.0" }, "Rex::Shared::Var::Common" : { "file" : "lib/Rex/Shared/Var/Common.pm", "version" : "v1.6.0" }, "Rex::Shared::Var::Hash" : { "file" : "lib/Rex/Shared/Var/Hash.pm", "version" : "v1.6.0" }, "Rex::Shared::Var::Scalar" : { "file" : "lib/Rex/Shared/Var/Scalar.pm", "version" : "v1.6.0" }, "Rex::Sudo::File" : { "file" : "lib/Rex/Sudo/File.pm", "version" : "v1.6.0" }, "Rex::Task" : { "file" : "lib/Rex/Task.pm", "version" : "v1.6.0" }, "Rex::TaskList" : { "file" : "lib/Rex/TaskList.pm", "version" : "v1.6.0" }, "Rex::TaskList::Base" : { "file" : "lib/Rex/TaskList/Base.pm", "version" : "v1.6.0" }, "Rex::TaskList::Parallel_ForkManager" : { "file" : "lib/Rex/TaskList/Parallel_ForkManager.pm", "version" : "v1.6.0" }, "Rex::Template" : { "file" : "lib/Rex/Template.pm", "version" : "v1.6.0" }, "Rex::Template::NG" : { "file" : "lib/Rex/Template/NG.pm", "version" : "v1.6.0" }, "Rex::Test" : { "file" : "lib/Rex/Test.pm", "version" : "v1.6.0" }, "Rex::Test::Base" : { "file" : "lib/Rex/Test/Base.pm", "version" : "v1.6.0" }, "Rex::Test::Base::has_content" : { "file" : "lib/Rex/Test/Base/has_content.pm", "version" : "v1.6.0" }, "Rex::Test::Base::has_cron" : { "file" : "lib/Rex/Test/Base/has_cron.pm", "version" : "v1.6.0" }, "Rex::Test::Base::has_cron_env" : { "file" : "lib/Rex/Test/Base/has_cron_env.pm", "version" : "v1.6.0" }, "Rex::Test::Base::has_dir" : { "file" : "lib/Rex/Test/Base/has_dir.pm", "version" : "v1.6.0" }, "Rex::Test::Base::has_file" : { "file" : "lib/Rex/Test/Base/has_file.pm", "version" : "v1.6.0" }, "Rex::Test::Base::has_file_content" : { "file" : "lib/Rex/Test/Base/has_file_content.pm", "version" : "v1.6.0" }, "Rex::Test::Base::has_output" : { "file" : "lib/Rex/Test/Base/has_output.pm", "version" : "v1.6.0" }, "Rex::Test::Base::has_output_matching" : { "file" : "lib/Rex/Test/Base/has_output_matching.pm", "version" : "v1.6.0" }, "Rex::Test::Base::has_package" : { "file" : "lib/Rex/Test/Base/has_package.pm", "version" : "v1.6.0" }, "Rex::Test::Base::has_service_running" : { "file" : "lib/Rex/Test/Base/has_service_running.pm", "version" : "v1.6.0" }, "Rex::Test::Base::has_service_stopped" : { "file" : "lib/Rex/Test/Base/has_service_stopped.pm", "version" : "v1.6.0" }, "Rex::Test::Base::has_stat" : { "file" : "lib/Rex/Test/Base/has_stat.pm", "version" : "v1.6.0" }, "Rex::Transaction" : { "file" : "lib/Rex/Transaction.pm", "version" : "v1.6.0" }, "Rex::User" : { "file" : "lib/Rex/User.pm", "version" : "v1.6.0" }, "Rex::User::Base" : { "file" : "lib/Rex/User/Base.pm", "version" : "v1.6.0" }, "Rex::User::FreeBSD" : { "file" : "lib/Rex/User/FreeBSD.pm", "version" : "v1.6.0" }, "Rex::User::Linux" : { "file" : "lib/Rex/User/Linux.pm", "version" : "v1.6.0" }, "Rex::User::NetBSD" : { "file" : "lib/Rex/User/NetBSD.pm", "version" : "v1.6.0" }, "Rex::User::OpenBSD" : { "file" : "lib/Rex/User/OpenBSD.pm", "version" : "v1.6.0" }, "Rex::User::OpenWrt" : { "file" : "lib/Rex/User/OpenWrt.pm", "version" : "v1.6.0" }, "Rex::User::SunOS" : { "file" : "lib/Rex/User/SunOS.pm", "version" : "v1.6.0" }, "Rex::Value" : { "file" : "lib/Rex/Value.pm", "version" : "v1.6.0" }, "Rex::Virtualization" : { "file" : "lib/Rex/Virtualization.pm", "version" : "v1.6.0" }, "Rex::Virtualization::Base" : { "file" : "lib/Rex/Virtualization/Base.pm", "version" : "v1.6.0" }, "Rex::Virtualization::Docker" : { "file" : "lib/Rex/Virtualization/Docker.pm", "version" : "v1.6.0" }, "Rex::Virtualization::Docker::create" : { "file" : "lib/Rex/Virtualization/Docker/create.pm", "version" : "v1.6.0" }, "Rex::Virtualization::Docker::daemon" : { "file" : "lib/Rex/Virtualization/Docker/daemon.pm", "version" : "v1.6.0" }, "Rex::Virtualization::Docker::delete" : { "file" : "lib/Rex/Virtualization/Docker/delete.pm", "version" : "v1.6.0" }, "Rex::Virtualization::Docker::destroy" : { "file" : "lib/Rex/Virtualization/Docker/destroy.pm", "version" : "v1.6.0" }, "Rex::Virtualization::Docker::guestinfo" : { "file" : "lib/Rex/Virtualization/Docker/guestinfo.pm", "version" : "v1.6.0" }, "Rex::Virtualization::Docker::images" : { "file" : "lib/Rex/Virtualization/Docker/images.pm", "version" : "v1.6.0" }, "Rex::Virtualization::Docker::import" : { "file" : "lib/Rex/Virtualization/Docker/import.pm", "version" : "v1.6.0" }, "Rex::Virtualization::Docker::info" : { "file" : "lib/Rex/Virtualization/Docker/info.pm", "version" : "v1.6.0" }, "Rex::Virtualization::Docker::list" : { "file" : "lib/Rex/Virtualization/Docker/list.pm", "version" : "v1.6.0" }, "Rex::Virtualization::Docker::reboot" : { "file" : "lib/Rex/Virtualization/Docker/reboot.pm", "version" : "v1.6.0" }, "Rex::Virtualization::Docker::shutdown" : { "file" : "lib/Rex/Virtualization/Docker/shutdown.pm", "version" : "v1.6.0" }, "Rex::Virtualization::Docker::start" : { "file" : "lib/Rex/Virtualization/Docker/start.pm", "version" : "v1.6.0" }, "Rex::Virtualization::Docker::status" : { "file" : "lib/Rex/Virtualization/Docker/status.pm", "version" : "v1.6.0" }, "Rex::Virtualization::LibVirt" : { "file" : "lib/Rex/Virtualization/LibVirt.pm", "version" : "v1.6.0" }, "Rex::Virtualization::LibVirt::blklist" : { "file" : "lib/Rex/Virtualization/LibVirt/blklist.pm", "version" : "v1.6.0" }, "Rex::Virtualization::LibVirt::clone" : { "file" : "lib/Rex/Virtualization/LibVirt/clone.pm", "version" : "v1.6.0" }, "Rex::Virtualization::LibVirt::create" : { "file" : "lib/Rex/Virtualization/LibVirt/create.pm", "version" : "v1.6.0" }, "Rex::Virtualization::LibVirt::delete" : { "file" : "lib/Rex/Virtualization/LibVirt/delete.pm", "version" : "v1.6.0" }, "Rex::Virtualization::LibVirt::destroy" : { "file" : "lib/Rex/Virtualization/LibVirt/destroy.pm", "version" : "v1.6.0" }, "Rex::Virtualization::LibVirt::dumpxml" : { "file" : "lib/Rex/Virtualization/LibVirt/dumpxml.pm", "version" : "v1.6.0" }, "Rex::Virtualization::LibVirt::guestinfo" : { "file" : "lib/Rex/Virtualization/LibVirt/guestinfo.pm", "version" : "v1.6.0" }, "Rex::Virtualization::LibVirt::hypervisor" : { "file" : "lib/Rex/Virtualization/LibVirt/hypervisor.pm", "version" : "v1.6.0" }, "Rex::Virtualization::LibVirt::iflist" : { "file" : "lib/Rex/Virtualization/LibVirt/iflist.pm", "version" : "v1.6.0" }, "Rex::Virtualization::LibVirt::import" : { "file" : "lib/Rex/Virtualization/LibVirt/import.pm", "version" : "v1.6.0" }, "Rex::Virtualization::LibVirt::info" : { "file" : "lib/Rex/Virtualization/LibVirt/info.pm", "version" : "v1.6.0" }, "Rex::Virtualization::LibVirt::list" : { "file" : "lib/Rex/Virtualization/LibVirt/list.pm", "version" : "v1.6.0" }, "Rex::Virtualization::LibVirt::option" : { "file" : "lib/Rex/Virtualization/LibVirt/option.pm", "version" : "v1.6.0" }, "Rex::Virtualization::LibVirt::reboot" : { "file" : "lib/Rex/Virtualization/LibVirt/reboot.pm", "version" : "v1.6.0" }, "Rex::Virtualization::LibVirt::shutdown" : { "file" : "lib/Rex/Virtualization/LibVirt/shutdown.pm", "version" : "v1.6.0" }, "Rex::Virtualization::LibVirt::start" : { "file" : "lib/Rex/Virtualization/LibVirt/start.pm", "version" : "v1.6.0" }, "Rex::Virtualization::LibVirt::status" : { "file" : "lib/Rex/Virtualization/LibVirt/status.pm", "version" : "v1.6.0" }, "Rex::Virtualization::LibVirt::vncdisplay" : { "file" : "lib/Rex/Virtualization/LibVirt/vncdisplay.pm", "version" : "v1.6.0" }, "Rex::Virtualization::Lxc" : { "file" : "lib/Rex/Virtualization/Lxc.pm", "version" : "v1.6.0" }, "Rex::Virtualization::Lxc::attach" : { "file" : "lib/Rex/Virtualization/Lxc/attach.pm", "version" : "v1.6.0" }, "Rex::Virtualization::Lxc::copy" : { "file" : "lib/Rex/Virtualization/Lxc/copy.pm", "version" : "v1.6.0" }, "Rex::Virtualization::Lxc::create" : { "file" : "lib/Rex/Virtualization/Lxc/create.pm", "version" : "v1.6.0" }, "Rex::Virtualization::Lxc::destroy" : { "file" : "lib/Rex/Virtualization/Lxc/destroy.pm", "version" : "v1.6.0" }, "Rex::Virtualization::Lxc::info" : { "file" : "lib/Rex/Virtualization/Lxc/info.pm", "version" : "v1.6.0" }, "Rex::Virtualization::Lxc::list" : { "file" : "lib/Rex/Virtualization/Lxc/list.pm", "version" : "v1.6.0" }, "Rex::Virtualization::Lxc::start" : { "file" : "lib/Rex/Virtualization/Lxc/start.pm", "version" : "v1.6.0" }, "Rex::Virtualization::Lxc::stop" : { "file" : "lib/Rex/Virtualization/Lxc/stop.pm", "version" : "v1.6.0" }, "Rex::Virtualization::VBox" : { "file" : "lib/Rex/Virtualization/VBox.pm", "version" : "v1.6.0" }, "Rex::Virtualization::VBox::bridge" : { "file" : "lib/Rex/Virtualization/VBox/bridge.pm", "version" : "v1.6.0" }, "Rex::Virtualization::VBox::create" : { "file" : "lib/Rex/Virtualization/VBox/create.pm", "version" : "v1.6.0" }, "Rex::Virtualization::VBox::delete" : { "file" : "lib/Rex/Virtualization/VBox/delete.pm", "version" : "v1.6.0" }, "Rex::Virtualization::VBox::destroy" : { "file" : "lib/Rex/Virtualization/VBox/destroy.pm", "version" : "v1.6.0" }, "Rex::Virtualization::VBox::forward_port" : { "file" : "lib/Rex/Virtualization/VBox/forward_port.pm", "version" : "v1.6.0" }, "Rex::Virtualization::VBox::guestinfo" : { "file" : "lib/Rex/Virtualization/VBox/guestinfo.pm", "version" : "v1.6.0" }, "Rex::Virtualization::VBox::import" : { "file" : "lib/Rex/Virtualization/VBox/import.pm", "version" : "v1.6.0" }, "Rex::Virtualization::VBox::info" : { "file" : "lib/Rex/Virtualization/VBox/info.pm", "version" : "v1.6.0" }, "Rex::Virtualization::VBox::list" : { "file" : "lib/Rex/Virtualization/VBox/list.pm", "version" : "v1.6.0" }, "Rex::Virtualization::VBox::option" : { "file" : "lib/Rex/Virtualization/VBox/option.pm", "version" : "v1.6.0" }, "Rex::Virtualization::VBox::reboot" : { "file" : "lib/Rex/Virtualization/VBox/reboot.pm", "version" : "v1.6.0" }, "Rex::Virtualization::VBox::share_folder" : { "file" : "lib/Rex/Virtualization/VBox/share_folder.pm", "version" : "v1.6.0" }, "Rex::Virtualization::VBox::shutdown" : { "file" : "lib/Rex/Virtualization/VBox/shutdown.pm", "version" : "v1.6.0" }, "Rex::Virtualization::VBox::start" : { "file" : "lib/Rex/Virtualization/VBox/start.pm", "version" : "v1.6.0" }, "Rex::Virtualization::VBox::status" : { "file" : "lib/Rex/Virtualization/VBox/status.pm", "version" : "v1.6.0" } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/RexOps/Rex/issues" }, "homepage" : "http://www.rexify.org", "repository" : { "type" : "git", "url" : "https://github.com/RexOps/Rex.git", "web" : "https://github.com/RexOps/Rex" }, "x_IRC" : "irc://irc.freenode.net/rex", "x_twitter" : "https://twitter.com/RexOps" }, "version" : "1.6.0", "x_serialization_backend" : "Cpanel::JSON::XS version 3.0239" } rexify100755001751001751 10124013232707066 13112 0ustar00janjan000000000000Rex-1.6.0/bin#!perl # # (c) Jan Gehring # # vim: set ts=3 sw=3 tw=0: # vim: set expandtab: use strict; use warnings; our $VERSION = '1.6.0'; # 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 [ []] []\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=\t\tProject where the Rexfile should be registered.\n"; print STDERR "\t\t--name=\t\t\tThe name for the Rexfile.\n"; print STDERR "\t\t--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=\t\tProject where the Rexfile should be registered.\n"; print STDERR "\t\t--job=\t\t\tThe name of the job to execute.\n"; print STDERR "\t\t--hosts=''\tA comma seperated list of servers.\n"; print STDERR "\n"; print STDERR "General options:"; print STDERR "\n"; print STDERR "\t--server=\t\tThe URL to JobControl server.\n"; print STDERR "\t--username=\t\tThe username to use for login to JobControl server.\n"; print STDERR "\t--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/^(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 suite your needs.\n"; print STDERR "You can edit $dir/lib/$ARGV[0].pm to define tasks.\n"; print STDERR "\n\nIf you have any questions or wishes\n\n\tjust join #rex on freenode\n\nor post them here:\n\n\thttps://github.com/RexOps/Rex/issues\n\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 => ""; # set your password set 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 => ''; set 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 }} 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 hosts.ex100644001751001751 25513232707066 12773 0ustar00janjan000000000000Rex-1.6.0/t# Host Database # 127.0.0.1 localhost 255.255.255.255 broadcasthost ::1 localhost 192.168.2.23 mango.rexify.org mango 192.168.2.222 syrr.rexify.org syrr no_tty.t100644001751001751 77313232707066 13003 0ustar00janjan000000000000Rex-1.6.0/tuse Test::More tests => 3; use Rex::Commands::Run; use Rex::Config; $::QUIET = 1; SKIP: { skip 'don\'t know how to test this right on windows', 3 if $^O =~ m/^MSWin/; Rex::Config->set_no_tty(0); my $s = run("ls -l /jllkjlkj"); like( $s, qr/No such file/, "with tty" ); Rex::Config->set_no_tty(1); $s = run("ls -l /jllkjlkj"); unlike( $s, qr/No such file/, "with no tty" ); Rex::Config->set_no_tty(0); $s = run("ls -l /jllkjlkj"); like( $s, qr/No such file/, "again with tty" ); } test.xml100644001751001751 200213232707066 13006 0ustar00janjan000000000000Rex-1.6.0/t logger.t100644001751001751 345313232707066 12764 0ustar00janjan000000000000Rex-1.6.0/t#!/usr/bin/env perl use strict; use warnings; use Test::More tests => 8; use Rex::Helper::Path; no warnings 'once'; $::QUIET = 1; my $logfile = Rex::Helper::Path::get_tmp_file(); # log to a file ok( !-e $logfile, 'Logfile does not exist' ); Rex::Config->set_log_filename($logfile); $Rex::Logger::format = '%l - %s'; { # only info messages are logged Rex::Logger::debug('debug1'); Rex::Logger::info('info1'); my $logcheck = qq~INFO - info1\n~; my $content = _get_log(); is( $content, $logcheck, "only info messages are logged" ); } { $Rex::Logger::silent = 1; Rex::Logger::debug('debug1'); Rex::Logger::info('info1'); my $logcheck = qq~INFO - info1\n~; my $content = _get_log(); is( $content, $logcheck, "no logging added while silent" ); } { $Rex::Logger::silent = 0; $Rex::Logger::debug = 1; Rex::Logger::debug('debug1'); Rex::Logger::info('info2'); my $logcheck = qq~INFO - info1 DEBUG - debug1 INFO - info2 ~; my $content = _get_log(); is( $content, $logcheck, "all messages are logged" ); } { $Rex::Logger::format = '%D - %l - %s'; my $date_regex = '\d{4}-\d{2}-\d{2} (?:\d+:){2}\d+'; Rex::Logger::debug('debug2'); Rex::Logger::info('info3'); my $logcheck = qq~INFO - info1 DEBUG - debug1 INFO - info2 $date_regex - DEBUG - debug2 $date_regex - INFO - info3 ~; my $content = _get_log(); like( $content, qr/$logcheck/, "all messages are logged - with date" ); } Rex::Logger->shutdown; ok( -e $logfile, 'Logfile still available' ); unlink $logfile; ok( !-e $logfile, 'Logfile unlinked' ); my $masq_s = Rex::Logger::masq( "This is a password: %s", "pass" ); is( $masq_s, "This is a password: **********", "Log-Masquerading" ); sub _get_log { local $/; open my $fh, '<', $logfile or die $!; my $loglines = <$fh>; close $fh; return $loglines; } test.ini100644001751001751 60213232707066 12751 0ustar00janjan000000000000Rex-1.6.0/t; my group file [frontends] fe01 fe02 fe03 fe04 fe05 # the backends [backends] be01 be02 ;be03 be04 [db] db[01..02] [cassandra] [01..02]-cassandra [voldemort] [111..133/11]-voldemort [kiokudb] [1,3,7,01]-kiokudb [riak] [1..3,5,9..21/3]-riak [redis] @backends redis01 redis02 [memcache < redis] memcache01 memcache02 user=root password=foob4r sudo=true services=apache,memcache config.t100644001751001751 220513232707066 12744 0ustar00janjan000000000000Rex-1.6.0/tuse strict; use warnings; use Test::More tests => 9; use Rex::Config; Rex::Config->set( "test", "foobar" ); is( Rex::Config->get("test"), "foobar", "setting scalars" ); Rex::Config->set( "test_a", [qw/one two three/] ); is( Rex::Config->get("test_a")->[1], "two", "setting arrayRef" ); is_deeply( Rex::Config->get('test_a'), [qw/one two three/], "compare complete arrayRef", ); Rex::Config->set( "test_a", [qw/four/] ); ok( Rex::Config->get("test_a")->[-1] eq "four" && Rex::Config->get("test_a")->[0] eq "one", "adding more to arrayRef" ); is_deeply( Rex::Config->get('test_a'), [qw/one two three four/], "compare complete arrayRef", ); Rex::Config->set( "test_h", { name => "john" } ); is( Rex::Config->get("test_h")->{"name"}, "john", "setting hashRef" ); is_deeply( Rex::Config->get('test_h'), { name => 'john' }, 'check test_h' ); Rex::Config->set( "test_h", { surname => "doe" } ); ok( Rex::Config->get("test_h")->{"surname"} eq "doe" && Rex::Config->get("test_h")->{"name"} eq "john", "adding more to hashRef" ); is_deeply( Rex::Config->get('test_h'), { name => 'john', surname => 'doe' }, 'check test_h' ); 1; report.t100644001751001751 272013232707066 13014 0ustar00janjan000000000000Rex-1.6.0/tuse strict; use warnings; use Test::More tests => 3; use YAML; use Rex::Commands; use Rex::Commands::File; use Rex::Commands::Fs; use Rex::Report::YAML; { no warnings 'once'; $::QUIET = 1; } if ( $^O =~ m/^MSWin/ ) { system("rd /Q /S tmp\\report"); } else { system("rm -rf tmp/report"); } my $report = Rex::Report->create; isa_ok( $report, "Rex::Report::Base", "created report class" ); mkdir "tmp"; Rex::Report->destroy; report( -on => "YAML" ); set( report_path => "tmp/report" ); my $report_num = 1; Rex::Report::YAML->set_report_name( sub { return $report_num; } ); task( "test", sub { file( "test_report.txt", content => "this is a test" ); } ); Rex::TaskList->create()->get_task("test")->run(""); my @files = list_files("tmp/report/_local_"); my $content = eval { local ( @ARGV, $/ ) = ("tmp/report/_local_/$files[0]"); <>; }; my $ref = Load($content); is( $ref->{'file[test_report.txt]'}->{changed}, 1, "a new file was created." ); $report_num += 1; Rex::TaskList->create()->get_task("test")->run(""); @files = sort { $a =~ s/\.yml//; $b =~ s/\.yml//; $a <=> $b } list_files("tmp/report/_local_/"); $content = eval { local ( @ARGV, $/ ) = ("tmp/report/_local_/$files[1].yml"); <>; }; $ref = Load($content); is( $ref->{'file[test_report.txt]'}->{changed}, 0, "the file was not changed" ); unlink "test_report.txt"; if ( $^O =~ m/^MSWin/ ) { system("rd /Q /S tmp\\report"); } else { system("rm -rf tmp/report"); } shared.t100644001751001751 405513232707066 12752 0ustar00janjan000000000000Rex-1.6.0/tuse strict; use warnings; BEGIN { use Test::More tests => 18; use Test::Deep; use Time::HiRes; use Rex::Shared::Var; share(qw($scalar @array %hash)); } $scalar = "scalar"; is( $scalar, "scalar", "scalar test" ); is( shift @array, undef, "shift from empty shared array" ); is( pop @array, undef, "pop from empty shared array" ); @array = qw(one two three four); is( join( "-", @array ), "one-two-three-four", "array test" ); push( @array, "five" ); is( $array[-1], "five", "array push" ); is( shift @array, "one", "shift from shared array" ); is_deeply( \@array, [qw(two three four five)], "shared array after shift" ); is( pop @array, "five", "pop from shared array" ); is_deeply( \@array, [qw(two three four)], "shared array after pop" ); unshift( @array, "six" ); is( $array[0], "six", "unshift to shared array" ); push( @array, qw(seven eight) ); is_deeply( [ @array[ -2, -1 ] ], [qw(seven eight)], "push list to shared array" ); unshift( @array, qw(nine ten) ); is_deeply( [ @array[ 0, 1 ] ], [qw(nine ten)], "unshift list to shared array" ); %hash = ( name => "joe", surename => "doe", multi => { key1 => "foo", arr1 => [qw/bar baz/], } ); is( $hash{name}, "joe", "hash test, key 1" ); is( $hash{surename}, "doe", "hash test, key 2" ); is( $hash{multi}->{key1}, "foo", "multidimension, key1" ); is( $hash{multi}->{arr1}->[0], "bar", "multidimension, arr1 - key0" ); is( $hash{multi}->{arr1}->[1], "baz", "multidimension, arr1 - key1" ); { # Sleeping in a test is not ideal. But can't replace Time::HiRes::usleep() # with POSIX::pause() because kill doesn't send signals on win32. See # 'perldoc -f kill', 'perldoc perlport' and # https://github.com/RexOps/Rex/pull/774 @array = (0); my @pids; for my $i ( 0 .. 5 ) { my $pid = fork(); if ( $pid == 0 ) { # child Time::HiRes::usleep 100_000; # .1 seconds push @array, 1; exit 0; } push @pids, $pid; } waitpid $_, 0 for @pids; cmp_deeply \@array, [qw/0 1 1 1 1 1 1/], 'race condition avoided'; } lib000755001751001751 013232707066 11456 5ustar00janjan000000000000Rex-1.6.0Rex.pm100644001751001751 5661413232707066 12746 0ustar00janjan000000000000Rex-1.6.0/lib# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: =encoding UTF-8 =head1 NAME Rex - Remote Execution =head1 DESCRIPTION Rex is a command line tool which executes commands on remote servers. Define tasks in Perl and execute them on remote servers or groups of servers. Rex can be used to: =over 4 =item * Deploy web applications to servers sequentially or in parallel. =item * Automate common tasks. =item * Provision servers using Rex's builtin tools. =back You can find examples and howtos on L =head1 GETTING HELP =over 4 =item * Web Site: L =item * IRC: irc.freenode.net #rex =item * Bug Tracker: L =item * Twitter: L =back =head1 SYNOPSIS # In a Rexfile: use Rex -feature => [qw/1.3/]; user "root"; password "ch4ngem3"; desc "Show Unix version"; task "uname", sub { say run "uname -a"; }; 1; # On the command line: bash# rex -H server[01..10] uname See L for more information about how to use rex on the command line. See L for a list of all commands you can use. =head1 CLASS METHODS =cut package Rex; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION # development version if this variable is not set if ( !$Rex::VERSION ) { $Rex::VERSION = "9999.99.99_99"; } BEGIN { use Rex::Logger; use Rex::Interface::Cache; use Data::Dumper; use Rex::Interface::Connection; use Cwd qw(getcwd); use Rex::Config; use Rex::Helper::Array; use Rex::Report; use Rex::Notify; use Rex::Require; use File::Basename; use File::Spec; eval { Net::SSH2->require; }; } our ( @EXPORT, @CONNECTION_STACK, $GLOBAL_SUDO, $MODULE_PATHS, $WITH_EXIT_STATUS, @FEATURE_FLAGS ); $WITH_EXIT_STATUS = 1; # since 0.50 activated by default @FEATURE_FLAGS = (); my $cur_dir; BEGIN { sub generate_inc { my @additional = @_; my @rex_inc = (); # this must be the first, special handling for rex modules which uses Module.pm and not # __module__.pm as their initial file. (rex pre 0.40 or something) push @rex_inc, sub { my $mod_to_load = $_[1]; return search_module_path( $mod_to_load, 1 ); }; # this adds the current directory to the lib search path. # this must come before all other paths, because custom libraries can be project dependant # see: #1108 push @rex_inc, add_cwd_to_inc(); push @rex_inc, add_libstruct_to_inc($_) for @additional; # we have to add the Rexfile's path to @INC FIX: #1170 push @rex_inc, @additional; # add home directory/.rex/recipes to the search path, so that recipes can be managed # at a central location. my $home_dir = _home_dir(); if ( -d "$home_dir/.rex/recipes" ) { push( @INC, "$home_dir/.rex/recipes" ); } # add the default search locations push @rex_inc, @INC; # this must be the last entry, special handling to load rex modules. push( @rex_inc, sub { my $mod_to_load = $_[1]; return search_module_path( $mod_to_load, 0 ); } ); return @rex_inc; } sub add_libstruct_to_inc { my ($path) = @_; my @ret = (); if ( -d File::Spec->catdir( $path, "lib" ) ) { push( @ret, File::Spec->catdir( $path, "lib" ) ); push( @ret, File::Spec->catdir( $path, "lib", "perl", "lib", "perl5" ) ); if ( $^O eq "linux" ) { push( @ret, File::Spec->catdir( $path, "lib", "perl", "lib", "perl5", "x86_64-linux" ) ); } if ( $^O =~ m/^MSWin/ ) { my ($special_win_path) = grep { m/\/MSWin32\-/ } @INC; if ( defined $special_win_path ) { my $mswin32_path = basename $special_win_path; push( @ret, File::Spec->catdir( $path, "lib", "perl", "lib", "perl5", $mswin32_path ) ); } } } return @ret; } sub add_cwd_to_inc { my $path = getcwd; return add_libstruct_to_inc($path); } sub _home_dir { if ( $^O =~ m/^MSWin/ ) { return $ENV{'USERPROFILE'}; } return $ENV{'HOME'} || ""; } my @new_inc = generate_inc(); @INC = @new_inc; } my $home = $ENV{'HOME'} || "/tmp"; if ( $^O =~ m/^MSWin/ ) { $home = $ENV{'USERPROFILE'}; } push( @INC, "$home/.rex/recipes" ); sub search_module_path { my ( $mod_to_load, $pre ) = @_; $mod_to_load =~ s/\.pm//g; my @search_in; if ($pre) { @search_in = map { ("$_/$mod_to_load.pm") } grep { -d } @INC; } else { @search_in = map { ( "$_/$mod_to_load/__module__.pm", "$_/$mod_to_load/Module.pm" ) } grep { -d } @INC; } for my $file (@search_in) { my $o = -f $file; my $fh_t; if ( $^O =~ m/^MSWin/i && !$o ) { # this is a windows workaround for if(-f ) on symlinks $o = open( my $fh_t, "<", $file ); } if ($o) { close $fh_t if $fh_t; my ($path) = ( $file =~ m/^(.*)\/.+?$/ ); if ( $path !~ m/\// ) { $path = $cur_dir . "/$path"; } # module found, register path $MODULE_PATHS->{$mod_to_load} = { path => $path }; my $mod_package_name = $mod_to_load; $mod_package_name =~ s/\//::/g; $MODULE_PATHS->{$mod_package_name} = { path => $path }; if ($pre) { return; } open( my $fh, "<", $file ); return $fh; } } } sub get_module_path { my ($module) = @_; if ( exists $MODULE_PATHS->{$module} ) { return $MODULE_PATHS->{$module}->{path}; } } sub push_connection { if ( !ref $_[0]->{server} ) { $_[0]->{server} = Rex::Group::Entry::Server->new( name => $_[0]->{server} ); } push @CONNECTION_STACK, $_[0]; return $_[0]; } sub pop_connection { pop @CONNECTION_STACK; Rex::Logger::debug( "Connections in queue: " . scalar(@CONNECTION_STACK) ); } sub reconnect_lost_connections { if ( @CONNECTION_STACK > 0 ) { Rex::Logger::debug("Need to reinitialize connections."); for (@CONNECTION_STACK) { $_->{conn}->reconnect; } } } # ... no words my @__modif_caller; sub unset_modified_caller { @__modif_caller = (); } sub modified_caller { my (@caller) = @_; if (@caller) { @__modif_caller = @caller; } else { return @__modif_caller; } } =head2 get_current_connection This function is deprecated since 0.28! See Rex::Commands::connection. Returns the current connection as a hashRef. =over 4 =item server The server name =item ssh 1 if it is a ssh connection, 0 if not. =back =cut sub get_current_connection { # if no connection available, use local connect unless (@CONNECTION_STACK) { my $conn = Rex::Interface::Connection->create("Local"); Rex::push_connection( { conn => $conn, ssh => $conn->get_connection_object, cache => Rex::Interface::Cache->create(), reporter => Rex::Report->create(), notify => Rex::Notify->new(), } ); } $CONNECTION_STACK[-1]; } sub get_current_connection_object { return Rex::get_current_connection()->{conn}; } =head2 is_ssh Returns 1 if the current connection is a ssh connection. 0 if not. =cut sub is_ssh { if ( $CONNECTION_STACK[-1] ) { my $ref = ref( $CONNECTION_STACK[-1]->{"conn"} ); if ( $ref =~ m/SSH/ ) { return $CONNECTION_STACK[-1]->{"conn"}->get_connection_object(); } } return 0; } =head2 is_local Returns 1 if the current connection is local. Otherwise 0. =cut sub is_local { if ( $CONNECTION_STACK[-1] ) { my $ref = ref( $CONNECTION_STACK[-1]->{"conn"} ); if ( $ref =~ m/Local/ ) { return $CONNECTION_STACK[-1]->{"conn"}->get_connection_object(); } } return 0; } =head2 is_sudo Returns 1 if the current operation is executed within sudo. =cut sub is_sudo { if ( exists $CONNECTION_STACK[-1]->{server}->{auth}->{sudo} && $CONNECTION_STACK[-1]->{server}->{auth}->{sudo} == 1 ) { return 1; } elsif ( exists $CONNECTION_STACK[-1]->{server}->{auth}->{sudo} && $CONNECTION_STACK[-1]->{server}->{auth}->{sudo} == 0 ) { return 0; } if ($GLOBAL_SUDO) { return 1; } if ( $CONNECTION_STACK[-1] ) { return $CONNECTION_STACK[-1]->{conn}->get_current_use_sudo; } return 0; } sub global_sudo { my ($on) = @_; $GLOBAL_SUDO = $on; # turn cache on Rex::Config->set_use_cache(1); } =head2 get_sftp Returns the sftp object for the current ssh connection. =cut sub get_sftp { if ( $CONNECTION_STACK[-1] ) { return $CONNECTION_STACK[-1]->{"conn"}->get_fs_connection_object(); } return 0; } sub get_cache { if ( $CONNECTION_STACK[-1] ) { return $CONNECTION_STACK[-1]->{"cache"}; } return Rex::Interface::Cache->create(); } =head2 connect Use this function to create a connection if you use Rex as a library. use Rex; use Rex::Commands::Run; use Rex::Commands::Fs; Rex::connect( server => "remotehost", user => "root", password => "f00b4r", private_key => "/path/to/private/key/file", public_key => "/path/to/public/key/file", ); if(is_file("/foo/bar")) { print "Do something...\n"; } my $output = run("uptime"); =cut sub connect { my ($param) = {@_}; my $server = $param->{server}; my $port = $param->{port} || 22; my $timeout = $param->{timeout} || 5; my $user = $param->{"user"}; my $pass = $param->{"password"}; my $cached_conn = $param->{"cached_connection"}; if ( !$cached_conn ) { my $conn = Rex::Interface::Connection->create(Rex::Config::get_connection_type); $conn->connect( user => $user, password => $pass, server => $server, port => $port, timeout => $timeout, %{$param}, ); unless ( $conn->is_connected ) { die("Connection error or refused."); } # push a remote connection my $rex_conn = Rex::push_connection( { conn => $conn, ssh => $conn->get_connection_object, server => $server, cache => Rex::Interface::Cache->create(), reporter => Rex::Report->create( Rex::Config->get_report_type ), notify => Rex::Notify->new(), } ); # auth unsuccessfull unless ( $conn->is_authenticated ) { Rex::Logger::info( "Wrong username or password. Or wrong key.", "warn" ); # after jobs die("Wrong username or password. Or wrong key."); } return $rex_conn; } else { Rex::push_connection($cached_conn); return $cached_conn; } } sub deprecated { my ( $func, $version, @msg ) = @_; if ($func) { Rex::Logger::info("The call to $func is deprecated."); } if (@msg) { for (@msg) { Rex::Logger::info($_); } } Rex::Logger::info(""); Rex::Logger::info( "Please rewrite your code. This function will disappear in (R)?ex version $version." ); Rex::Logger::info( "If you need assistance please join #rex on irc.freenode.net or our google group." ); } sub has_feature_version { my ($version) = @_; my @version_flags = grep { m/^\d+\./ } @FEATURE_FLAGS; for my $v (@version_flags) { if ( $version <= $v ) { return 1; } } return 0; } sub has_feature_version_lower { my ($version) = @_; my @version_flags = grep { m/^\d+\./ } @FEATURE_FLAGS; for my $v (@version_flags) { if ( $version > $v ) { return 1; } } return 0; } sub import { my ( $class, $what, $addition1 ) = @_; srand(); if ( $addition1 && ref $addition1 eq "ARRAY" ) { push @FEATURE_FLAGS, $what, @{$addition1}; } elsif ($addition1) { push @FEATURE_FLAGS, $what, $addition1; } $what ||= ""; my ( $register_to, $file, $line ) = caller; # use Net::OpenSSH if present (default without feature flag) Rex::Config->set_use_net_openssh_if_present(1); if ( $what eq "-minimal" ) { require Rex::Commands; Rex::Commands->import( register_in => $register_to ); require Rex::Helper::Rexfile::ParamLookup; Rex::Helper::Rexfile::ParamLookup->import( register_in => $register_to ); } if ( $what eq "-base" || $what eq "base" || $what eq "-feature" ) { require Rex::Commands; Rex::Commands->import( register_in => $register_to ); require Rex::Commands::Run; Rex::Commands::Run->import( register_in => $register_to ); require Rex::Commands::Fs; Rex::Commands::Fs->import( register_in => $register_to ); require Rex::Commands::File; Rex::Commands::File->import( register_in => $register_to ); require Rex::Commands::Cron; Rex::Commands::Cron->import( register_in => $register_to ); require Rex::Commands::Host; Rex::Commands::Host->import( register_in => $register_to ); require Rex::Commands::Download; Rex::Commands::Download->import( register_in => $register_to ); require Rex::Commands::Upload; Rex::Commands::Upload->import( register_in => $register_to ); require Rex::Commands::Gather; Rex::Commands::Gather->import( register_in => $register_to ); require Rex::Commands::Kernel; Rex::Commands::Kernel->import( register_in => $register_to ); require Rex::Commands::Pkg; Rex::Commands::Pkg->import( register_in => $register_to ); require Rex::Commands::Service; Rex::Commands::Service->import( register_in => $register_to ); require Rex::Commands::Sysctl; Rex::Commands::Sysctl->import( register_in => $register_to ); require Rex::Commands::Tail; Rex::Commands::Tail->import( register_in => $register_to ); require Rex::Commands::Process; Rex::Commands::Process->import( register_in => $register_to ); require Rex::Commands::Sync; Rex::Commands::Sync->import( register_in => $register_to ); require Rex::Commands::Notify; Rex::Commands::Notify->import( register_in => $register_to ); require Rex::Commands::User; Rex::Commands::User->import( register_in => $register_to ); require Rex::Helper::Rexfile::ParamLookup; Rex::Helper::Rexfile::ParamLookup->import( register_in => $register_to ); require Rex::Resource::firewall; Rex::Resource::firewall->import( register_in => $register_to ); } if ( $what eq "-feature" || $what eq "feature" ) { if ( !ref($addition1) ) { $addition1 = [$addition1]; } for my $add ( @{$addition1} ) { my $found_feature = 0; if ( $add =~ m/^(\d+\.\d+)$/ ) { my $vers = $1; my ( $major, $minor, $patch, $dev_release ) = $Rex::VERSION =~ m/^(\d+)\.(\d+)\.(\d+)_?(\d+)?$/; my ( $c_major, $c_minor ) = split( /\./, $vers ); if ( defined $dev_release && $c_major == $major && $c_minor > $minor ) { Rex::Logger::info( "This is development release $Rex::VERSION of Rex. Enabling experimental feature flag for $vers.", "warn" ); } elsif ( ( $c_major > $major ) || ( $c_major >= $major && $c_minor > $minor ) ) { Rex::Logger::info( "This Rexfile tries to enable features that are not supported with your version. Please update.", "error" ); exit 1; } } if ( $add =~ m/^\d+\.\d+$/ && $add >= 1.4 ) { Rex::Logger::debug("Enabling task_chaining_cmdline_args feature"); Rex::Config->set_task_chaining_cmdline_args(1); $found_feature = 1; } if ( $add =~ m/^\d+\.\d+$/ && $add >= 1.3 ) { Rex::Logger::debug("Activating new template engine."); Rex::Config->set_use_template_ng(1); $found_feature = 1; } if ( $add =~ m/^\d+\.\d+$/ && $add >= 1.0 ) { Rex::Logger::debug("Disabling usage of a tty"); Rex::Config->set_no_tty(1); $found_feature = 1; } if ( $add =~ m/^\d+\.\d+$/ && $add >= 0.56 ) { Rex::Logger::debug("Activating autodie."); Rex::Config->set_autodie(1); $found_feature = 1; } if ( $add =~ m/^\d+\.\d+$/ && $add >= 0.55 ) { Rex::Logger::debug("Using Net::OpenSSH if present."); Rex::Config->set_use_net_openssh_if_present(1); $found_feature = 1; } if ( $add =~ m/^\d+\.\d+$/ && $add >= 0.54 ) { Rex::Logger::debug("Add service check."); Rex::Config->set_check_service_exists(1); Rex::Logger::debug("Setting set() to not append data."); Rex::Config->set_set_no_append(1); $found_feature = 1; } if ( $add =~ m/^\d+\.\d+$/ && $add >= 0.53 ) { Rex::Logger::debug("Registering CMDB as template variables."); Rex::Config->set_register_cmdb_template(1); $found_feature = 1; } if ( $add =~ m/^\d+\.\d+$/ && $add >= 0.51 ) { Rex::Logger::debug("activating featureset >= 0.51"); Rex::Config->set_task_call_by_method(1); require Rex::Constants; Rex::Constants->import( register_in => $register_to ); require Rex::CMDB; Rex::CMDB->import( register_in => $register_to ); Rex::Commands::set( cmdb => { type => "YAML", path => [ "cmdb/{operatingsystem}/{hostname}.yml", "cmdb/{operatingsystem}/default.yml", "cmdb/{environment}/{hostname}.yml", "cmdb/{environment}/default.yml", "cmdb/{hostname}.yml", "cmdb/default.yml", ], } ); $found_feature = 1; } if ( $add =~ m/^\d+\.\d+$/ && $add >= 0.40 ) { Rex::Logger::debug("activating featureset >= 0.40"); $Rex::Template::BE_LOCAL = 1; $Rex::WITH_EXIT_STATUS = 1; $found_feature = 1; } if ( $add =~ m/^\d+\.\d+$/ && $add >= 0.35 ) { Rex::Logger::debug("activating featureset >= 0.35"); $Rex::Commands::REGISTER_SUB_HASH_PARAMETER = 1; $found_feature = 1; } # remove default task auth if ( $add =~ m/^\d+\.\d+$/ && $add >= 0.31 ) { Rex::Logger::debug("activating featureset >= 0.31"); Rex::TaskList->create()->set_default_auth(0); $found_feature = 1; } if ( $add eq "no_autodie" ) { Rex::Logger::debug("disabling autodie"); Rex::Config->set_autodie(0); $found_feature = 1; } if ( $add eq "rex_kvm_agent" ) { Rex::Logger::debug( "Activating experimental support for rex-kvm-agent."); Rex::Config->set_use_rex_kvm_agent(1); $found_feature = 1; } if ( $add eq "template_ng" ) { Rex::Logger::debug("Activating new template engine."); Rex::Config->set_use_template_ng(1); $found_feature = 1; } if ( $add eq "no_template_ng" ) { Rex::Logger::debug("Deactivating new template engine."); Rex::Config->set_use_template_ng(0); $found_feature = 1; } if ( $add eq "register_cmdb_top_scope" ) { Rex::Logger::debug("Registering CMDB as template variables."); Rex::Config->set_register_cmdb_template(1); $found_feature = 1; } if ( $add eq "no_local_template_vars" ) { Rex::Logger::debug("activating featureset no_local_template_vars"); $Rex::Template::BE_LOCAL = 0; $found_feature = 1; } if ( $add eq "exit_status" ) { Rex::Logger::debug("activating featureset exit_status"); $Rex::WITH_EXIT_STATUS = 1; $found_feature = 1; } if ( $add eq "sudo_without_sh" ) { Rex::Logger::debug( "using sudo without sh. this might break some things."); Rex::Config->set_sudo_without_sh(1); $found_feature = 1; } if ( $add eq "sudo_without_locales" ) { Rex::Logger::debug( "Using sudo without locales. this _will_ break things!"); Rex::Config->set_sudo_without_locales(1); $found_feature = 1; } if ( $add eq "tty" ) { Rex::Logger::debug("Enabling pty usage for ssh"); Rex::Config->set_no_tty(0); $found_feature = 1; } if ( $add eq "no_tty" ) { Rex::Logger::debug("Disabling pty usage for ssh"); Rex::Config->set_no_tty(1); $found_feature = 1; } if ( $add eq "empty_groups" ) { Rex::Logger::debug("Enabling usage of empty groups"); Rex::Config->set_allow_empty_groups(1); $found_feature = 1; } if ( $add eq "use_server_auth" ) { Rex::Logger::debug("Enabling use_server_auth"); Rex::Config->set_use_server_auth(1); $found_feature = 1; } if ( $add eq "exec_and_sleep" ) { Rex::Logger::debug("Enabling exec_and_sleep"); Rex::Config->set_sleep_hack(1); $found_feature = 1; } if ( $add eq "disable_strict_host_key_checking" ) { Rex::Logger::debug("Disabling strict host key checking for openssh"); Rex::Config->set_openssh_opt( StrictHostKeyChecking => "no" ); $found_feature = 1; } #if($add eq "reporting" || $add eq "report" || exists $ENV{REX_REPORT_TYPE}) { # Rex::Logger::debug("Enabling reporting"); Rex::Config->set_do_reporting(1); # $found_feature = 1; #} if ( $add eq "source_profile" ) { Rex::Logger::debug("Enabling source_profile"); Rex::Config->set_source_profile(1); $found_feature = 1; } if ( $add eq "source_global_profile" ) { Rex::Logger::debug("Enabling source_global_profile"); Rex::Config->set_source_global_profile(1); $found_feature = 1; } if ( $add eq "no_path_cleanup" ) { Rex::Logger::debug("Enabling no_path_cleanup"); Rex::Config->set_no_path_cleanup(1); $found_feature = 1; } if ( $add eq "exec_autodie" ) { Rex::Logger::debug("Enabling exec_autodie"); Rex::Config->set_exec_autodie(1); $found_feature = 1; } if ( $add eq "no_cache" ) { Rex::Logger::debug("disable caching"); Rex::Config->set_use_cache(0); $found_feature = 1; } if ( $add eq "verbose_run" ) { Rex::Logger::debug("Enabling verbose_run feature"); Rex::Config->set_verbose_run(1); $found_feature = 1; } if ( $add eq "disable_taskname_warning" ) { Rex::Logger::debug("Enabling disable_taskname_warning feature"); Rex::Config->set_disable_taskname_warning(1); $found_feature = 1; } if ( $add eq "no_task_chaining_cmdline_args" ) { Rex::Logger::debug("Disabling task_chaining_cmdline_args feature"); Rex::Config->set_task_chaining_cmdline_args(0); $found_feature = 1; } if ( $add eq "task_chaining_cmdline_args" ) { Rex::Logger::debug("Enabling task_chaining_cmdline_args feature"); Rex::Config->set_task_chaining_cmdline_args(1); $found_feature = 1; } if ( $found_feature == 0 ) { Rex::Logger::info( "You tried to load a feature ($add) that doesn't exists in your Rex version. Please update.", "warn" ); exit 1; } } } if ( exists $ENV{REX_REPORT_TYPE} ) { Rex::Logger::debug("Enabling reporting"); Rex::Config->set_do_reporting(1); } if ( exists $ENV{REX_SUDO} && $ENV{REX_SUDO} ) { Rex::global_sudo(1); } # we are always strict strict->import; } =head1 CONTRIBUTORS Many thanks to the contributors for their work. Please see L file for a complete list. =head1 LICENSE Rex is a free software, licensed under: The Apache License, Version 2.0, January 2004 =cut 1; package.t100644001751001751 163613232707066 13101 0ustar00janjan000000000000Rex-1.6.0/tuse strict; use warnings; use Test::More tests => 4; use Rex::Pkg::Base; my $pkg = Rex::Pkg::Base->new; my @plist1 = ( { name => 'vim', version => '1.0' }, { name => 'mc', version => '2.0' }, { name => 'rex', version => '0.51.0' }, ); my @plist2 = ( { name => 'vim', version => '1.0' }, { name => 'rex', version => '0.52.0' }, { name => 'libssh2-1', version => '0.32.1' }, ); my @mods = $pkg->diff_package_list( \@plist1, \@plist2 ); my $found_vim = grep { $_->{name} eq "vim" } @mods; is( $found_vim, 0, "vim was not modified" ); my ($found_rex) = grep { $_->{name} eq "rex" } @mods; is( $found_rex->{action}, "updated", "rex was updated" ); my ($found_libssh2) = grep { $_->{name} eq "libssh2-1" } @mods; is( $found_libssh2->{action}, "installed", "libssh2-1 was installed" ); my ($found_mc) = grep { $_->{name} eq "mc" } @mods; is( $found_mc->{action}, "removed", "mc was removed" ); 1; summary.t100644001751001751 723213232707066 13201 0ustar00janjan000000000000Rex-1.6.0/tuse strict; use warnings; use Test::More tests => 2; use Test::Deep; use Rex::Config; use Rex::Commands; use Rex::Commands::Run; use Rex::Transaction; $::QUIET = 1; subtest "distributor => 'Base'" => sub { subtest 'exec_autodie => 0' => sub { Rex::Config->set_exec_autodie(0); Rex::Config->set_distributor('Base'); test_summary( task0 => { server => '', task => 'task0', exit_code => 1 }, task1 => { server => '', task => 'task1', exit_code => 0 }, task2 => { server => '', task => 'task2', exit_code => 0 }, task3 => { server => '', task => 'task3', exit_code => 1 }, ); }; subtest 'exec_autodie => 1' => sub { Rex::Config->set_exec_autodie(1); Rex::Config->set_distributor('Base'); test_summary( task0 => { server => '', task => 'task0', exit_code => 1 }, task1 => { server => '', task => 'task1', exit_code => ( $^O =~ m/^(MSWin|freebsd|darwin|netbsd|openbsd)/ ? 1 : 2 ) }, task2 => { server => '', task => 'task2', exit_code => 0 }, task3 => { server => '', task => 'task3', exit_code => 1 }, ); }; }; SKIP: { skip "Parallel::ForkManager is not installed", 1 if parallel_forkmanager_not_installed(); subtest "distributor => 'Parallel_ForkManager'" => sub { subtest 'exec_autodie => 0' => sub { Rex::Config->set_exec_autodie(0); Rex::Config->set_distributor('Parallel_ForkManager'); test_summary( task0 => { server => '', task => 'task0', exit_code => 1 }, task1 => { server => '', task => 'task1', exit_code => 0 }, task2 => { server => '', task => 'task2', exit_code => 0 }, task3 => { server => '', task => 'task3', exit_code => 1 }, ); }; subtest 'exec_autodie => 1' => sub { Rex::Config->set_exec_autodie(1); Rex::Config->set_distributor('Parallel_ForkManager'); test_summary( task0 => { server => '', task => 'task0', exit_code => 1 }, task1 => { server => '', task => 'task1', exit_code => ( $^O =~ m/^(MSWin|freebsd|darwin|netbsd|openbsd)/ ? 1 : 2 ) }, task2 => { server => '', task => 'task2', exit_code => 0 }, task3 => { server => '', task => 'task3', exit_code => 1 }, ); }; } } sub create_tasks { desc "desc 0"; task "task0" => sub { die "bork0"; }; desc "desc 1"; task "task1" => sub { my $cmd = $^O =~ /MSWin32/ ? "dir" : "ls"; run "$cmd asdfxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; }; desc "desc 2"; task "task2" => sub { my $cmd = $^O =~ /MSWin32/ ? "dir" : "ls"; run $cmd; }; desc "desc 3"; task "task3" => sub { transaction { do_task qw/task0/; }; }; } sub test_summary { my (%expected) = @_; my @expected_summary; $Rex::TaskList::task_list = undef; create_tasks(); for my $task_name ( Rex::TaskList->create->get_tasks ) { Rex::TaskList->run($task_name); my @summary = Rex::TaskList->create->get_summary; # for the tests we remove the error message. for (@summary) { delete $_->{error_message}; } push @expected_summary, $expected{$task_name}; my $test_description = $expected{$task_name}->{exit_code} == 0 ? "$task_name succeeded" : "$task_name failed"; cmp_deeply \@summary, \@expected_summary, $test_description; } my $distributor = Rex::Config->get_distributor; no warnings; @Rex::TaskList::Base::SUMMARY = (); } sub parallel_forkmanager_not_installed { eval { require Parallel::ForkManager }; return 1 if $@; return 0; } include.t100644001751001751 101213232707066 13115 0ustar00janjan000000000000Rex-1.6.0/tuse strict; use warnings; use lib 't/lib'; use Test::More tests => 2; use Test::Deep; use Rex::Commands; use Rex::Commands::Run; use t::tasks::cowboy; $::QUIET = 1; my $task_list = Rex::TaskList->create; my @task_names = $task_list->get_tasks; cmp_deeply \@task_names, [qw/t:tasks:cowboy:roundup/], "found visible task"; my @all_task_names = sort $task_list->get_all_tasks(qr/.*/); cmp_deeply \@all_task_names, [qw/t:tasks:alien:negotiate t:tasks:cowboy:roundup/], "found hidden task"; done_testing(); hosts.ex2100644001751001751 26213232707066 13053 0ustar00janjan000000000000Rex-1.6.0/t127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 1.2.3.4 rexify.org do_task.t100644001751001751 106413232707066 13125 0ustar00janjan000000000000Rex-1.6.0/tuse strict; use warnings; use Rex::Commands; use Test::More tests => 4; eval { Rex::Commands::do_task("non_existing_task"); }; my $result = $@; isnt( $result, undef, 'exception for do_task non-existing task' ); like( $result, qr/Task non_existing_task not found\./, 'do_task non-existing task' ); eval { Rex::Commands::do_task( ["non_existing_task"] ); }; $result = $@; isnt( $result, undef, 'exception for do_task non-existing task as arrayref' ); like( $result, qr/Task non_existing_task not found\./, 'do_task non-existing task as arrayref' ); runlist.t100644001751001751 177313232707066 13210 0ustar00janjan000000000000Rex-1.6.0/tuse strict; use warnings; use Test::More; use Rex::Args; use Rex::RunList; use Rex::Commands; $Rex::Logger::silent = 1; Rex::Config->set_task_chaining_cmdline_args(1); task task1 => sub { }; task task2 => sub { }; task task3 => sub { }; task task4 => sub { }; @ARGV = qw(task1 arg1 arg2 --name=thename --num=5 task2 arg task3 --hey=what); my $run_list = Rex::RunList->instance; $run_list->parse_opts(@ARGV); my @tasks = $run_list->tasks; is scalar @tasks, 3, "run list has 3 tasks"; is ref $_, 'Rex::Task', "object isa Rex::Task" for @tasks; note "opts"; is_deeply { $tasks[0]->get_opts }, { name => 'thename', num => 5 }, "task0 opts"; is_deeply { $tasks[1]->get_opts }, {}, "task1 opts"; is_deeply { $tasks[2]->get_opts }, { hey => 'what' }, "task2 opts"; note "args"; is_deeply [ $tasks[0]->get_args ], [qw/arg1 arg2/], "task0 args"; is_deeply [ $tasks[1]->get_args ], [qw/arg/], "task1 args"; is_deeply [ $tasks[2]->get_args ], [qw//], "task2 args"; $run_list->run_tasks; done_testing; can_run.t100644001751001751 154013232707066 13125 0ustar00janjan000000000000Rex-1.6.0/tuse strict; use warnings; use Test::More tests => 4; use Rex::Commands::Run; { my $command_to_check = $^O =~ /^MSWin/ ? 'where' : 'which'; my $result = can_run($command_to_check); ok( $result, 'Found checker command' ); } { my $command_to_check = "I'm pretty sure this command doesn't exist anywhere"; my $result = can_run($command_to_check); ok( !$result, 'Non-existing command not found' ); } { my @commands_to_check = $^O =~ /^MSWin/ ? 'where' : 'which'; push @commands_to_check, 'non-existing command'; my $result = can_run(@commands_to_check); ok( $result, 'Multiple commands - existing first' ); } { my @commands_to_check = $^O =~ /^MSWin/ ? 'where' : 'which'; unshift @commands_to_check, 'non-existing command'; my $result = can_run(@commands_to_check); ok( $result, 'Multiple commands - non-existing first' ); } Makefile.PL100644001751001751 1016713232707066 13047 0ustar00janjan000000000000Rex-1.6.0# This Makefile.PL for Rex was generated by # Dist::Zilla::Plugin::MakeMaker::Awesome 0.39. # Don't edit it but the dist.ini and plugins used to construct it. use strict; use warnings; use 5.008008; use ExtUtils::MakeMaker; die 'OS unsupported' if ( $^O eq 'MSWin32' && scalar((Win32::GetOSVersion())[1]) < 6 ); my %WriteMakefileArgs = ( "ABSTRACT" => "Remote Execution", "AUTHOR" => "Jan Gehring ", "BUILD_REQUIRES" => { "Test::Pod" => 0 }, "CONFIGURE_REQUIRES" => { "ExtUtils::MakeMaker" => 0 }, "DISTNAME" => "Rex", "EXE_FILES" => [ "bin/rex", "bin/rexify" ], "LICENSE" => "apache", "MIN_PERL_VERSION" => "5.008008", "NAME" => "Rex", "PREREQ_PM" => { "AWS::Signature4" => 0, "Carp" => 0, "Cwd" => 0, "Data::Dumper" => 0, "Data::Validate::IP" => 0, "Devel::Caller" => 0, "Digest::HMAC_SHA1" => 0, "Digest::MD5" => 0, "Exporter" => 0, "Fcntl" => 0, "File::Basename" => 0, "File::Spec" => 0, "File::Spec::Unix" => 0, "File::Spec::Win32" => 0, "FindBin" => 0, "HTTP::Request" => 0, "HTTP::Request::Common" => 0, "Hash::Merge" => 0, "IO::File" => 0, "IO::Select" => 0, "IO::Socket" => 0, "IO::String" => 0, "IPC::Open3" => 0, "JSON::MaybeXS" => 0, "LWP::UserAgent" => 0, "List::MoreUtils" => 0, "List::Util" => 0, "MIME::Base64" => 0, "Net::OpenSSH::ShellQuoter" => 0, "POSIX" => 0, "Scalar::Util" => 0, "Sort::Naturally" => 0, "Storable" => 0, "Symbol" => 0, "Term::ReadKey" => 0, "Test::Builder::Module" => 0, "Text::Glob" => 0, "Text::Wrap" => 0, "Time::HiRes" => 0, "UNIVERSAL" => 0, "URI" => 0, "URI::QueryParam" => 0, "XML::Simple" => 0, "YAML" => 0, "attributes" => 0, "base" => 0, "constant" => 0, "lib" => 0, "overload" => 0, "strict" => 0, "vars" => 0, "version" => 0, "warnings" => 0 }, "TEST_REQUIRES" => { "File::Temp" => 0, "Parallel::ForkManager" => 0, "String::Escape" => 0, "Test::Deep" => 0, "Test::More" => 0, "Test::UseAllModules" => 0 }, "VERSION" => "1.6.0", "test" => { "TESTS" => "t/*.t t/commands/*.t t/issue/*.t" } ); my %FallbackPrereqs = ( "AWS::Signature4" => 0, "Carp" => 0, "Cwd" => 0, "Data::Dumper" => 0, "Data::Validate::IP" => 0, "Devel::Caller" => 0, "Digest::HMAC_SHA1" => 0, "Digest::MD5" => 0, "Exporter" => 0, "Fcntl" => 0, "File::Basename" => 0, "File::Spec" => 0, "File::Spec::Unix" => 0, "File::Spec::Win32" => 0, "File::Temp" => 0, "FindBin" => 0, "HTTP::Request" => 0, "HTTP::Request::Common" => 0, "Hash::Merge" => 0, "IO::File" => 0, "IO::Select" => 0, "IO::Socket" => 0, "IO::String" => 0, "IPC::Open3" => 0, "JSON::MaybeXS" => 0, "LWP::UserAgent" => 0, "List::MoreUtils" => 0, "List::Util" => 0, "MIME::Base64" => 0, "Net::OpenSSH::ShellQuoter" => 0, "POSIX" => 0, "Parallel::ForkManager" => 0, "Scalar::Util" => 0, "Sort::Naturally" => 0, "Storable" => 0, "String::Escape" => 0, "Symbol" => 0, "Term::ReadKey" => 0, "Test::Builder::Module" => 0, "Test::Deep" => 0, "Test::More" => 0, "Test::Pod" => 0, "Test::UseAllModules" => 0, "Text::Glob" => 0, "Text::Wrap" => 0, "Time::HiRes" => 0, "UNIVERSAL" => 0, "URI" => 0, "URI::QueryParam" => 0, "XML::Simple" => 0, "YAML" => 0, "attributes" => 0, "base" => 0, "constant" => 0, "lib" => 0, "overload" => 0, "strict" => 0, "vars" => 0, "version" => 0, "warnings" => 0 ); unless ( eval { ExtUtils::MakeMaker->VERSION(6.63_03) } ) { delete $WriteMakefileArgs{TEST_REQUIRES}; delete $WriteMakefileArgs{BUILD_REQUIRES}; $WriteMakefileArgs{PREREQ_PM} = \%FallbackPrereqs; } delete $WriteMakefileArgs{CONFIGURE_REQUIRES} unless eval { ExtUtils::MakeMaker->VERSION(6.52) }; if ( $^O !~ /MSWin/i ) { $WriteMakefileArgs{PREREQ_PM}{'IO::Pty'} = '0'; $WriteMakefileArgs{PREREQ_PM}{'Net::OpenSSH'} = '0'; $WriteMakefileArgs{PREREQ_PM}{'Net::SFTP::Foreign'} = '0'; } if ( $^O =~ /MSWin/i ) { $WriteMakefileArgs{PREREQ_PM}{'Net::SSH2'} = '0'; } WriteMakefile(%WriteMakefileArgs); CONTRIBUTORS100644001751001751 272513232707066 12736 0ustar00janjan000000000000Rex-1.6.0# alphabetical order Alex Mestiashvili alex1line Alexandr Ciornii Ali Polatel alx542 Anders Ossowicki Andrej Zverev Andrew Beverley Andrew Solomon Arnold Bechtoldt bollwarm Boris Däppen Brian Manning Cameron Daniel Сергей Романов (complefor) Chris Steigmeier Christophe Wolfhugel Cuong Manh Le Daniel Baeurer Daniel Dico David Golovan Denis Silakov Dmitry Kopytov Dominik Danter Dominik Schulz eduardoj Eivin Giske Skaaren Elmer Quintanilla Eric Johnson Erik Huelsmann fanyeren (范野人) Ferenc Erki Fran Rodriguez Franky Van Liedekerke Gilles Gaudin, for writing a french howto Graham Todd H. Daniel Cesario Harm Müller Hiroaki Nakamura Hiroki Matsuo Ilya Evseev James D Bearden Jean Charles Passard Jean-Marie Renouard Jeen Lee Jens Berthold Joachim Bargsten John Karr Jon Gentle Jonathan Delgado Joris DE POOTER Jose Luis Martinez Jose Luis Perez Diez Kasim Tuman Keedi Kim Ken Crowell Kent Fredric Kirill Babikhin Laird Liu Mario Domgoergen Mitch Broadhead Nathan Abu Naveed Massjouni necrophcodr Nicolas Leclercq Niklas Larsson Nikolay Fetisov Nils Domrose okaoka Oleg Hardt Paco Esteban Patrick Lauer Pavel Timofeev perlancar Peter H. Ezetta Peter Manthey petersonchen Pierrick DINTRAT Piotr Karbowski Prajithp Rao Chenlin (Chenryn) Rapenne Solène RenatoCRON Renee Bäcker Robert Abraham Samuele Tognini Sascha Askani Sascha Guenther Simon Bertrang Stephane Benoit Sven Dowideit Tamas Molnar Tianon Gravi Tokuhiro Matsuno Tomohiro Hosaka Walery Wysotsky Yanick Champouxtemplate.t100644001751001751 504513232707066 13317 0ustar00janjan000000000000Rex-1.6.0/tuse strict; use warnings; use Test::More tests => 18; use Rex::Config; use Rex::Commands; use Rex::Template; my $t = Rex::Template->new; my $content = 'one two three'; is( $t->parse( $content, {} ), "one two three", "just text" ); $content = 'Hello this is <%= $::name %>'; is( $t->parse( $content, { name => "foo" } ), "Hello this is foo", "simple variable" ); $content = '<% if($::logged_in) { %> Logged in! <% } else { %> Logged out! <% } %>'; my $content_ok = " Logged in! "; is( $t->parse( $content, { logged_in => 1 } ), $content_ok, "if condition" ); $content = 'Hello this is <%= $::name %>'; is( Rex::Config->get_template_function()->( $content, { name => "baz" } ), "Hello this is baz", "get template function" ); is( $t->parse( $content, name => "bar" ), "Hello this is bar", "simple variable without hashRef" ); $Rex::Template::BE_LOCAL = 1; $Rex::Template::BE_LOCAL = 1; $content = 'Hello this is <%= $foo %>'; is( $t->parse( $content, { foo => "baz" } ), "Hello this is baz", "local vars" ); $content = '<%= join(",", @{ $arr }) %>'; is( $t->parse( $content, { arr => [qw/one two three/] } ), "one,two,three", "local var with array" ); # # old variable style # $content = 'one two three'; is( $t->parse( $content, {} ), "one two three", "just text" ); $content = 'Hello this is <%= $::name %>'; is( $t->parse( $content, { name => "foo" } ), "Hello this is foo", "simple variable" ); $content = '<% if($::logged_in) { %> Logged in! <% } else { %> Logged out! <% } %>'; $content_ok = " Logged in! "; is( $t->parse( $content, { logged_in => 1 } ), $content_ok, "if condition" ); $content = 'Hello this is <%= $::name %>'; is( Rex::Config->get_template_function()->( $content, { name => "baz" } ), "Hello this is baz", "get template function" ); is( $t->parse( $content, name => "bar" ), "Hello this is bar", "simple variable without hashRef" ); $content = 'Hello this is <%= $::foo %> <%= $::veth1_0_ip %>'; is( $t->parse( $content, { foo => "baz", "veth1.0_ip" => "10.1.2.3" } ), "Hello this is baz 10.1.2.3", "template with invalid key name" ); my $v = { "foo" => { val => "val1", name => "foo" }, "foo_bar" => { val => "val2", name => "foo_bar" }, "k-ey" => { val => "val3", name => "k_ey" }, "veth0.1" => { val => "val4", name => "veth0_1" }, "2nd\\key" => { val => "val5", name => "2nd_key" }, }; for my $key ( keys %{$v} ) { my $var_name = Rex::Template::_normalize_var_name($key); is( $var_name, $v->{$key}->{name}, "$var_name is equal to " . $v->{$key}->{name} ); } commands.t100644001751001751 407613232707066 13310 0ustar00janjan000000000000Rex-1.6.0/tuse strict; use warnings; use Test::More tests => 18; use Rex::Commands; delete $ENV{REX_USER}; user("test"); is( Rex::Config->get_user, "test", "setting user" ); password("test"); is( Rex::Config->get_password, "test", "setting password" ); sudo_password("test"); is( Rex::Config->get_sudo_password, "test", "setting password" ); timeout(5); is( Rex::Config->get_timeout, 5, "setting timeout" ); max_connect_retries(5); is( Rex::Config->get_max_connect_fails, 5, "setting max connect retries" ); is( length( get_random( 5, 'a' .. 'z' ) ), 5, "get random string" ); public_key("/tmp/pub.key"); is( Rex::Config->get_public_key, "/tmp/pub.key", "set public key" ); private_key("/tmp/priv.key"); is( Rex::Config->get_private_key, "/tmp/priv.key", "set private key" ); pass_auth(); ok( Rex::Config->get_password_auth, "password auth" ); parallelism(5); is( Rex::Config->get_parallelism, 5, "set parallelism" ); parallelism(1); path( "/bin", "/sbin" ); is_deeply( [ Rex::Config->get_path ], [qw!/bin /sbin!], "set path" ); set( "foo", "bar" ); is( get("foo"), "bar", "set/get" ); my @ret = Rex::Commands::evaluate_hostname("test[01..04]"); is_deeply( \@ret, [qw/test01 test02 test03 test04/], "evaluate hostname" ); @ret = Rex::Commands::evaluate_hostname("test[01..04].rexify.org"); is_deeply( \@ret, [qw/test01.rexify.org test02.rexify.org test03.rexify.org test04.rexify.org/], "evaluate hostname / with domain" ); @ret = Rex::Commands::evaluate_hostname("test[1..4]"); is_deeply( \@ret, [qw/test1 test2 test3 test4/], "evaluate hostname without zeros" ); @ret = Rex::Commands::evaluate_hostname("test[1..4].rexify.org"); is_deeply( \@ret, [qw/test1.rexify.org test2.rexify.org test3.rexify.org test4.rexify.org/], "evaluate hostname with domainname / without zero" ); @ret = Rex::Commands::evaluate_hostname("10.5.9.[8..11]"); is_deeply( \@ret, [qw/10.5.9.8 10.5.9.9 10.5.9.10 10.5.9.11/], "evaluate ip" ); @ret = Rex::Commands::evaluate_hostname("[1..3].host.domain"); is_deeply( \@ret, [qw/1.host.domain 2.host.domain 3.host.domain/], "evaluate leading range" ); resource.t100644001751001751 325513232707066 13334 0ustar00janjan000000000000Rex-1.6.0/tuse Test::More tests => 11; use Rex -base; use Rex::Resource; use Rex::Resource::Common; $::QUIET = 1; resource( "testres", sub { my $name = resource_name; my $file = param_lookup "file", "/etc/passwd"; is( $name, "foo", "testres name is foo" ); is( $file, "/etc/passwd", "testres got default file param" ); my $x = template( \'<%= $file %>' ); is( $x, "/etc/passwd", "got default parameter in template" ); emit changed; } ); resource( "testres2", sub { my $name = resource_name; my $file = param_lookup "file", "/etc/passwd"; is( $name, "bar", "testres2 name is bar" ); is( $file, "/etc/shadow", "testres2 got custom param" ); my $x = template( \'<%= $file %>' ); is( $x, "/etc/shadow", "got custom parameter in template" ); testres3( "baz", file => "/etc/foo" ); $x = template( \'after: <%= $file %>' ); is( $x, "after: /etc/shadow", "got custom parameter in template after nested resource call" ); } ); resource( "testres3", sub { my $name = resource_name; my $file = param_lookup "file", "/etc/passwd"; is( $name, "baz", "testres3 name is baz" ); is( $file, "/etc/foo", "testres3 got custom param" ); my $x = template( \'<%= $file %>' ); is( $x, "/etc/foo", "got custom parameter in template (nested resource)" ); } ); task( "test1", sub { my $file = param_lookup "file", "/etc/groups"; testres("foo"); testres2( "bar", file => "/etc/shadow" ); my $x = template( \'<%= $file %>' ); is( $x, "/etc/securetty", "task got custom parameter in template" ); } ); test1( { file => "/etc/securetty" } ); done_testing(); fs_files.t100644001751001751 602213232707066 13272 0ustar00janjan000000000000Rex-1.6.0/t#!/usr/bin/env perl use strict; use warnings; use Test::More tests => 13; use Rex::Helper::Path; use Rex::Commands::File; my @lines = ( "first line", "second line", "test" ); my $filename = Rex::Helper::Path::get_tmp_file(); file( $filename, content => join "\n", @lines ); ok -e $filename, 'file was created'; { # standard Rex::FS::File usage - read file my $fh = Rex::Interface::File->create('Local'); $fh->open( '<', $filename ); my $file_object = Rex::FS::File->new( fh => $fh ); isa_ok $file_object, 'Rex::FS::File', 'new with fh was successful'; my @read_lines = $file_object->read_all; is_deeply \@read_lines, \@lines, 'read lines from fh'; } { # standard Rex::FS::File usage - write file my $fh = Rex::Interface::File->create('Local'); $fh->open( '>', $filename ); my $file_object = Rex::FS::File->new( fh => $fh ); isa_ok $file_object, 'Rex::FS::File', 'new with fh (write mode) was successful'; $file_object->write(qw/this is a test/); $file_object->close; my $read_fh = Rex::Interface::File->create('Local'); $read_fh->open( '<', $filename ); my $read_object = Rex::FS::File->new( fh => $read_fh ); my @read_lines = $read_object->read_all; is_deeply \@read_lines, [qw/this is a test/], 'read lines from fh'; } { # new Rex::FS::File usage - read file file( $filename, content => join "\n", @lines ); my $file_object = Rex::FS::File->new( filename => $filename ); isa_ok $file_object, 'Rex::FS::File', 'new with filename was successful'; my @read_lines = $file_object->read_all; is_deeply \@read_lines, \@lines, 'read lines from filename'; } { # new Rex::FS::File usage - write file my $file_object = Rex::FS::File->new( filename => $filename, mode => '>' ); isa_ok $file_object, 'Rex::FS::File', 'new with filename with mode ">" was successful'; $file_object->write(qw/this is a test/); $file_object->close; my $read_fh = Rex::Interface::File->create('Local'); $read_fh->open( '<', $filename ); my $read_object = Rex::FS::File->new( fh => $read_fh ); my @read_lines = $read_object->read_all; is_deeply \@read_lines, [qw/this is a test/], 'read lines from fh'; } { # new Rex::FS::File usage - read file - explicit read mode file( $filename, content => join "\n", @lines ); my $file_object = Rex::FS::File->new( filename => $filename, mode => '<' ); isa_ok $file_object, 'Rex::FS::File', 'new with filename and explicit read mode was successful'; my @read_lines = $file_object->read_all; is_deeply \@read_lines, \@lines, 'read lines from filename (explicit read mode)'; } { # new Rex::FS::File usage - write file - mode "w" my $file_object = Rex::FS::File->new( filename => $filename, mode => 'w' ); isa_ok $file_object, 'Rex::FS::File', 'new with filename with mode "w" was successful'; $file_object->write(qw/this is a test/); $file_object->close; my $read_object = Rex::FS::File->new( filename => $filename, mode => 'r' ); my @read_lines = $read_object->read_all; is_deeply \@read_lines, [qw/this is a test/], 'read lines from fh'; } base_virt.t100644001751001751 56113232707066 13440 0ustar00janjan000000000000Rex-1.6.0/tuse Test::More tests => 2; use Rex::Virtualization; my $vm_obj = Rex::Virtualization->create("VBox"); ok( ref($vm_obj) eq "Rex::Virtualization::VBox", "created vm object with param" ); Rex::Config->set( virtualization => "LibVirt" ); $vm_obj = Rex::Virtualization->create(); ok( ref($vm_obj) eq "Rex::Virtualization::LibVirt", "created vm object with config" ); md5test.bin100644001751001751 113232707066 13301 0ustar00janjan000000000000Rex-1.6.0/tissue_539.t100644001751001751 276313232707066 13240 0ustar00janjan000000000000Rex-1.6.0/tuse Test::More tests => 16; use Rex::Hardware::Network::Linux; use Rex::Helper::Hash; my @in = eval { local (@ARGV) = ("t/ip.out_issue_539"); <>; }; my $info = Rex::Hardware::Network::Linux::_parse_ip(@in); is( $info->{eth0}->{broadcast}, "192.168.178.255", "eth0 primary / broadcast" ); is( $info->{eth0}->{ip}, "192.168.178.81", "eth0 primary / ip" ); is( $info->{eth0}->{netmask}, "255.255.255.0", "eth0 primary / netmask" ); is( $info->{eth0}->{mac}, "08:00:27:4b:b8:48", "eth0 primary / mac" ); is( $info->{eth0_1}->{broadcast}, "192.168.99.255", "eth0 secondary / broadcast" ); is( $info->{eth0_1}->{ip}, "192.168.99.37", "eth0 secondary / ip" ); is( $info->{eth0_1}->{netmask}, "255.255.255.0", "eth0 secondary / netmask" ); is( $info->{eth0_1}->{mac}, "08:00:27:4b:b8:48", "eth0 secondary / mac" ); my $f = {}; hash_flatten( $info, $f, "_" ); is( $f->{eth0_mac}, "08:00:27:4b:b8:48", "eth0 primary / flatten / mac" ); is( $f->{eth0_ip}, "192.168.178.81", "eth0 primary / flatten / ip" ); is( $f->{eth0_netmask}, "255.255.255.0", "eth0 primary / flatten / netmask" ); is( $f->{eth0_broadcast}, "192.168.178.255", "eth0 primary / flatten / broadcast" ); is( $f->{eth0_1_mac}, "08:00:27:4b:b8:48", "eth0 secondary / flatten / mac" ); is( $f->{eth0_1_ip}, "192.168.99.37", "eth0 secondary / flatten / ip" ); is( $f->{eth0_1_netmask}, "255.255.255.0", "eth0 secondary / flatten / netmask" ); is( $f->{eth0_1_broadcast}, "192.168.99.255", "eth0 secondary / flatten / broadcast" ); issue000755001751001751 013232707066 12303 5ustar00janjan000000000000Rex-1.6.0/t949.t100644001751001751 111413232707066 13152 0ustar00janjan000000000000Rex-1.6.0/t/issueuse strict; use warnings; use Test::More tests => 2; use Rex::Commands; use Rex::Virtualization; eval { my $v = Rex::Virtualization->create(); 1; } or do { like $@, qr/^No virtualization provider set.\nPlease use `set virtualization => 'YourProvider'/, "Got right error message if no provider is set."; }; set virtualization => "Goo"; eval { my $v = Rex::Virtualization->create(); 1; } or do { like $@, qr/^Failed loading given virtualization module\.\nTried to load \/, "Got right error message if module loading failed."; }; 948.t100644001751001751 307013232707066 13154 0ustar00janjan000000000000Rex-1.6.0/t/issueuse strict; use warnings; use Rex::Virtualization; use Test::More tests => 6; use Data::Dumper; use Rex::Helper::Run; use Rex::Commands::File; use Rex::Commands::Fs; use Rex::Commands::Run; $::QUIET = 1; my $image_format = "raw"; sub get_image_format { return $image_format; } my $count_file = 0; my $count_exec = 0; no warnings 'redefine'; # TODO implement mocking sub Rex::Commands::File::file { my ( $name, %params ) = @_; my $fmt = get_image_format(); like $params{content}, qr||, "Found file content for $fmt format."; $count_file++; } sub Rex::Commands::Run::can_run { return 1; } sub Rex::Commands::Fs::unlink { my ($file) = @_; } sub Rex::Helper::Run::i_run { my ($exec) = @_; if ( $exec =~ m/virsh.*capabilities/ ) { return eval { local (@ARGV) = ("t/issue/948/capabilities.xml"); <>; }; } if ( $exec =~ m/^qemu\-img create/ ) { my $fmt = get_image_format(); like $exec, qr/^qemu\-img create \-f $fmt/, "qemu-img created a raw image."; $count_exec++; } return ""; } use warnings; my $v = Rex::Virtualization->create("LibVirt"); $v->execute( "create", "test01", storage => [ { file => "/mnt/data/libvirt/images/vm01.img", dev => "vda", } ] ); $image_format = "qcow2"; $v->execute( "create", "test01", storage => [ { file => "/mnt/data/libvirt/images/vm01.img", dev => "vda", driver_type => "qcow2", } ] ); is( $count_exec, 2, "Executed qemu-img 2 times." ); is( $count_file, 2, "Created virsh file 2 times." ); 513.t100644001751001751 57413232707066 13126 0ustar00janjan000000000000Rex-1.6.0/t/issueuse strict; use warnings; use Rex::CLI; use Test::More tests => 2; my $ok = 0; eval { Rex::CLI::load_rexfile("t/issue/513_t1.rex"); $ok = 1; }; is( $ok, 1, "Rexfile with false return value was loaded successfull." ); $ok = 0; eval { Rex::CLI::load_rexfile("t/issue/513_t2.rex"); $ok = 1; }; is( $ok, 1, "Rexfile with true return value was loaded successfull." ); 934.t100644001751001751 105013232707066 13143 0ustar00janjan000000000000Rex-1.6.0/t/issueuse strict; use warnings; use Test::More tests => 1; use Rex::Args; use Rex::RunList; use Rex::Commands; use Rex::CLI; use Rex::Transaction; use Rex::Args; BEGIN { use Rex::Shared::Var; share qw(%opt_t1 %opt_t2 %opt_t3); } $::QUIET = 1; $::rexfile = "noop"; task task1 => sub { }; before_task_start task1 => sub { my %args = Rex::Args->get; is( $args{name}, "thename", "got taskopt" ); }; @ARGV = qw(task1 --name=thename --num=5 --hey=what); my $run_list = Rex::RunList->instance; $run_list->parse_opts(@ARGV); $run_list->run_tasks; 860.t100644001751001751 332213232707066 13145 0ustar00janjan000000000000Rex-1.6.0/t/issueuse strict; use warnings; use Test::More tests => 6; use Rex::Args; use Rex::RunList; use Rex::Commands; use Rex::CLI; use Rex::Transaction; BEGIN { use Rex::Shared::Var; share qw(%opt_t1 %opt_t2 %opt_t3); } $::QUIET = 1; $::rexfile = "noop"; task task1 => sub { }; task task2 => sub { }; task task3 => sub { }; before task1 => sub { %opt_t1 = Rex::Args->get; }; before task2 => sub { %opt_t2 = Rex::Args->get; }; before task3 => sub { %opt_t3 = Rex::Args->get; }; @ARGV = qw(task1 task2 task3 --name=thename --num=5 --hey=what); my $run_list = Rex::RunList->instance; $run_list->parse_opts(@ARGV); $run_list->run_tasks; is_deeply( { name => "thename", task2 => 1, num => 5, task1 => 1, hey => "what", task3 => 1, }, \%opt_t1, "got task1 parameter with pre 1.4 compatibility" ); is_deeply( { name => "thename", task2 => 1, num => 5, task1 => 1, hey => "what", task3 => 1, }, \%opt_t2, "got task2 parameter with pre 1.4 compatibility" ); is_deeply( { name => "thename", task2 => 1, num => 5, task1 => 1, hey => "what", task3 => 1, }, \%opt_t3, "got task3 parameter with pre 1.4 compatibility" ); Rex::Config->set_task_chaining_cmdline_args(1); @ARGV = qw(task1 arg1 arg2 --name=thename --num=5 task2 arg task3 --hey=what); $run_list = Rex::RunList->instance; $run_list->parse_opts(@ARGV); $run_list->run_tasks; is_deeply( { name => "thename", num => 5, }, \%opt_t1, "got task1 parameter with 1.4 compatibility" ); is_deeply( {}, \%opt_t2, "got task2 parameter with 1.4 compatibility" ); is_deeply( { hey => "what", }, \%opt_t3, "got task3 parameter with 1.4 compatibility" ); url_encode.t100644001751001751 66413232707066 13605 0ustar00janjan000000000000Rex-1.6.0/tuse Test::More tests => 1; use Rex::Helper::Encode; my $input = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789#+~*`´!\"§\$%&/()=?\\|<>,.-_'^°"; my $output = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789%23%2B%7E%2A%60%C2%B4%21%22%C2%A7%24%25%26%2F%28%29%3D%3F%5C%7C%3C%3E%2C%2E%2D_%27%5E%C2%B0"; is( Rex::Helper::Encode::url_encode($input), $output, "encode everything except a-z0-9_" ); config-ssh.t100644001751001751 405113232707066 13540 0ustar00janjan000000000000Rex-1.6.0/tuse strict; use warnings; use File::Temp; use Test::More tests => 21; use Rex::Config; my $ssh_cfg1 = < 1 ); ok( open( my $FH1, '>', $tempdir . '/cfg1' ), 'Opened cfg1' ); print $FH1 $ssh_cfg1; ok( close($FH1), 'Closed cfg1' ); %Rex::Config::SSH_CONFIG_FOR = (); Rex::Config::read_ssh_config_file( $tempdir . '/cfg1' ); my $c = \%Rex::Config::SSH_CONFIG_FOR; is( $c->{'web'}->{'user'}, 'root' ); isnt( $c->{'web'}->{'user'}, 'root ' ); is( $c->{'web'}->{'hostname'}, '192.168.1.1' ); is( $c->{'frontend1'}->{'user'}, 'bogey' ); is( $c->{'frontend1'}->{'hostname'}, 'fe80::1' ); is( $c->{'frontend1'}->{'port'}, 35221 ); ok( open( my $FH2, '>', $tempdir . '/cfg2' ), 'Opened cfg2' ); print $FH2 $ssh_cfg2; ok( close($FH2), 'Closed cfg2' ); %Rex::Config::SSH_CONFIG_FOR = (); Rex::Config::read_ssh_config_file( $tempdir . '/cfg2' ); is( $c->{'frontend2'}->{'user'}, '123' ); is( $c->{'frontend2'}->{'hostname'}, 'this.is.a.domain.tld' ); is( $c->{'frontend2'}->{'port'}, 1005 ); is( $c->{'some'}->{'port'}, '3306' ); is( $c->{'other'}->{'port'}, '3306' ); is( $c->{'hosts'}->{'port'}, '3306' ); my @lines = eval { local (@ARGV) = ("t/ssh_config.1"); <>; }; my %data = Rex::Config::_parse_ssh_config(@lines); ok( exists $data{"*"}, "Host * exists" ); ok( exists $data{"*"}->{stricthostkeychecking}, "Host * / StrictHostKeyChecking exists" ); ok( $data{"*"}->{stricthostkeychecking} eq "no", "Host * / StrictHostKeyChecking and contains 'no'" ); ok( exists $data{"*"}->{userknownhostsfile}, "Host * / UserKnownHostsFile exists" ); ok( $data{"*"}->{userknownhostsfile} eq "/dev/null", "Host * / UserKnownHostsFile and contains '/dev/null'" ); 1; task_hosts.t100644001751001751 106013232707066 13657 0ustar00janjan000000000000Rex-1.6.0/tpackage main; use Test::More tests => 2; use Rex::Commands; desc("Test"); task( "test", "server01", "server02", sub { } ); desc("Test 2"); task( "test2", "fe[01..10]", sub { } ); desc("Test 3"); task( "test3", "fe06", "server02", sub { } ); my @tasks = Rex::TaskList->create()->get_tasks_for("server01"); is_deeply( \@tasks, ["test"], "tasks has one element: 'test'" ); @tasks = Rex::TaskList->create()->get_tasks_for("fe06"); is_deeply( \@tasks, [ "test2", "test3" ], "tasks has two elements: 'test2' and 'test3'" ); dmi.obsd.out100644001751001751 1270213232707066 13565 0ustar00janjan000000000000Rex-1.6.0/t# dmidecode 2.10 SMBIOS 2.3 present. 20 structures occupying 896 bytes. Table at 0x000F6120. Handle 0x0000, DMI type 0, 20 bytes BIOS Information Vendor: Parallels Software International Inc. Version: 6.0.12094.676533 Release Date: 10/26/2007 Address: 0xF0000 Runtime Size: 64 kB ROM Size: 64 kB Characteristics: ISA is supported PCI is supported PNP is supported APM is supported VLB is supported Boot from CD is supported 8042 keyboard services are supported (int 9h) Serial services are supported (int 14h) Printer services are supported (int 17h) CGA/mono video services are supported (int 10h) ACPI is supported Handle 0x0001, DMI type 1, 25 bytes System Information Manufacturer: Parallels Software International Inc. Product Name: Parallels Virtual Platform Version: None Serial Number: Parallels-A2 00 64 F5 C3 9B 49 34 9E B7 F5 4D 34 B7 E2 40 UUID: A20064F5-C39B-4934-9EB7-F54D34B7E240 Wake-up Type: Power Switch Handle 0x0002, DMI type 2, 8 bytes Base Board Information Manufacturer: Parallels Software International Inc. Product Name: Parallels Virtual Platform Version: None Serial Number: None Handle 0x0003, DMI type 3, 18 bytes Chassis Information Manufacturer: Parallels Software International Inc. Type: Unknown Lock: Not Present Version: Serial Number: Asset Tag: Boot-up State: Safe Power Supply State: Safe Thermal State: Safe Security Status: None OEM Information: 0x00000000 Handle 0x0004, DMI type 4, 35 bytes Processor Information Socket Designation: CPU Socket #0 Type: Central Processor Family: Unknown Manufacturer: GenuineIntel ID: E5 06 01 00 FF FB EB BF Version: Not Specified Voltage: 3.3 V External Clock: 466 MHz Max Speed: 2800 MHz Current Speed: 2800 MHz Status: Populated, Enabled Upgrade: Other L1 Cache Handle: Not Provided L2 Cache Handle: Not Provided L3 Cache Handle: Not Provided Serial Number: Not Specified Asset Tag: Not Specified Part Number: Not Specified Handle 0x0005, DMI type 9, 13 bytes System Slot Information Designation: ISA slot 1 Type: 16-bit ISA Current Usage: Unknown Length: Unknown Characteristics: 3.3 V is provided Handle 0x0006, DMI type 9, 13 bytes System Slot Information Designation: PCI slot 1 Type: 32-bit PCI Current Usage: Unknown Length: Unknown ID: 1 Characteristics: 3.3 V is provided Handle 0x0007, DMI type 9, 13 bytes System Slot Information Designation: PCI slot 2 Type: 32-bit PCI Current Usage: Unknown Length: Unknown ID: 2 Characteristics: 3.3 V is provided Handle 0x0008, DMI type 9, 13 bytes System Slot Information Designation: PCI slot 3 Type: 32-bit PCI Current Usage: Unknown Length: Unknown ID: 3 Characteristics: 3.3 V is provided Handle 0x0009, DMI type 9, 13 bytes System Slot Information Designation: PCI slot 4 Type: 32-bit PCI Current Usage: Unknown Length: Unknown ID: 4 Characteristics: 3.3 V is provided Handle 0x000A, DMI type 9, 13 bytes System Slot Information Designation: PCI slot 5 Type: 32-bit PCI Current Usage: Unknown Length: Unknown ID: 5 Characteristics: 3.3 V is provided Handle 0x000B, DMI type 10, 8 bytes On Board Device 1 Information Type: Video Status: Disabled Description: Parallels Video Adapter On Board Device 2 Information Type: Sound Status: Disabled Description: Parallels Sound Adapter Handle 0x000C, DMI type 16, 15 bytes Physical Memory Array Location: System Board Or Motherboard Use: System Memory Error Correction Type: None Maximum Capacity: 8 GB Error Information Handle: Not Provided Number Of Devices: 4 Handle 0x000D, DMI type 17, 27 bytes Memory Device Array Handle: 0x000C Error Information Handle: Not Provided Total Width: 32 bits Data Width: 32 bits Size: 512 MB Form Factor: DIMM Set: None Locator: DIMM #0 Bank Locator: BANK #0 Type: DRAM Type Detail: EDO Speed: 667 MHz Manufacturer: Not Specified Serial Number: Not Specified Asset Tag: Not Specified Part Number: Not Specified Handle 0x000E, DMI type 17, 27 bytes Memory Device Array Handle: 0x000C Error Information Handle: Not Provided Total Width: 32 bits Data Width: 32 bits Size: No Module Installed Form Factor: DIMM Set: None Locator: DIMM #1 Bank Locator: BANK #1 Type: DRAM Type Detail: EDO Speed: 667 MHz Manufacturer: Not Specified Serial Number: Not Specified Asset Tag: Not Specified Part Number: Not Specified Handle 0x000F, DMI type 17, 27 bytes Memory Device Array Handle: 0x000C Error Information Handle: Not Provided Total Width: 32 bits Data Width: 32 bits Size: No Module Installed Form Factor: DIMM Set: None Locator: DIMM #2 Bank Locator: BANK #2 Type: DRAM Type Detail: EDO Speed: 667 MHz Manufacturer: Not Specified Serial Number: Not Specified Asset Tag: Not Specified Part Number: Not Specified Handle 0x0010, DMI type 17, 27 bytes Memory Device Array Handle: 0x000C Error Information Handle: Not Provided Total Width: 32 bits Data Width: 32 bits Size: No Module Installed Form Factor: DIMM Set: None Locator: DIMM #3 Bank Locator: BANK #3 Type: DRAM Type Detail: EDO Speed: 667 MHz Manufacturer: Not Specified Serial Number: Not Specified Asset Tag: Not Specified Part Number: Not Specified Handle 0x0011, DMI type 19, 15 bytes Memory Array Mapped Address Starting Address: 0x00000000000 Ending Address: 0x0001FFFFFFF Range Size: 512 MB Physical Array Handle: 0x000C Partition Width: 0 Handle 0x0012, DMI type 32, 20 bytes System Boot Information Status: No errors detected Handle 0x0013, DMI type 127, 4 bytes End Of Table dmi.fbsd.out100644001751001751 1270213232707066 13554 0ustar00janjan000000000000Rex-1.6.0/t# dmidecode 2.10 SMBIOS 2.3 present. 20 structures occupying 896 bytes. Table at 0x000F6120. Handle 0x0000, DMI type 0, 20 bytes BIOS Information Vendor: Parallels Software International Inc. Version: 6.0.12094.676533 Release Date: 10/26/2007 Address: 0xF0000 Runtime Size: 64 kB ROM Size: 64 kB Characteristics: ISA is supported PCI is supported PNP is supported APM is supported VLB is supported Boot from CD is supported 8042 keyboard services are supported (int 9h) Serial services are supported (int 14h) Printer services are supported (int 17h) CGA/mono video services are supported (int 10h) ACPI is supported Handle 0x0001, DMI type 1, 25 bytes System Information Manufacturer: Parallels Software International Inc. Product Name: Parallels Virtual Platform Version: None Serial Number: Parallels-16 BE 64 A3 DA 80 4A 1F B9 12 1A 49 1C 47 6A DA UUID: 16BE64A3-DA80-4A1F-B912-1A491C476ADA Wake-up Type: Power Switch Handle 0x0002, DMI type 2, 8 bytes Base Board Information Manufacturer: Parallels Software International Inc. Product Name: Parallels Virtual Platform Version: None Serial Number: None Handle 0x0003, DMI type 3, 18 bytes Chassis Information Manufacturer: Parallels Software International Inc. Type: Unknown Lock: Not Present Version: Serial Number: Asset Tag: Boot-up State: Safe Power Supply State: Safe Thermal State: Safe Security Status: None OEM Information: 0x00000000 Handle 0x0004, DMI type 4, 35 bytes Processor Information Socket Designation: CPU Socket #0 Type: Central Processor Family: Unknown Manufacturer: GenuineIntel ID: E5 06 01 00 FF FB EB BF Version: Not Specified Voltage: 3.3 V External Clock: 466 MHz Max Speed: 2800 MHz Current Speed: 2800 MHz Status: Populated, Enabled Upgrade: Other L1 Cache Handle: Not Provided L2 Cache Handle: Not Provided L3 Cache Handle: Not Provided Serial Number: Not Specified Asset Tag: Not Specified Part Number: Not Specified Handle 0x0005, DMI type 9, 13 bytes System Slot Information Designation: ISA slot 1 Type: 16-bit ISA Current Usage: Unknown Length: Unknown Characteristics: 3.3 V is provided Handle 0x0006, DMI type 9, 13 bytes System Slot Information Designation: PCI slot 1 Type: 32-bit PCI Current Usage: Unknown Length: Unknown ID: 1 Characteristics: 3.3 V is provided Handle 0x0007, DMI type 9, 13 bytes System Slot Information Designation: PCI slot 2 Type: 32-bit PCI Current Usage: Unknown Length: Unknown ID: 2 Characteristics: 3.3 V is provided Handle 0x0008, DMI type 9, 13 bytes System Slot Information Designation: PCI slot 3 Type: 32-bit PCI Current Usage: Unknown Length: Unknown ID: 3 Characteristics: 3.3 V is provided Handle 0x0009, DMI type 9, 13 bytes System Slot Information Designation: PCI slot 4 Type: 32-bit PCI Current Usage: Unknown Length: Unknown ID: 4 Characteristics: 3.3 V is provided Handle 0x000A, DMI type 9, 13 bytes System Slot Information Designation: PCI slot 5 Type: 32-bit PCI Current Usage: Unknown Length: Unknown ID: 5 Characteristics: 3.3 V is provided Handle 0x000B, DMI type 10, 8 bytes On Board Device 1 Information Type: Video Status: Disabled Description: Parallels Video Adapter On Board Device 2 Information Type: Sound Status: Disabled Description: Parallels Sound Adapter Handle 0x000C, DMI type 16, 15 bytes Physical Memory Array Location: System Board Or Motherboard Use: System Memory Error Correction Type: None Maximum Capacity: 8 GB Error Information Handle: Not Provided Number Of Devices: 4 Handle 0x000D, DMI type 17, 27 bytes Memory Device Array Handle: 0x000C Error Information Handle: Not Provided Total Width: 32 bits Data Width: 32 bits Size: 512 MB Form Factor: DIMM Set: None Locator: DIMM #0 Bank Locator: BANK #0 Type: DRAM Type Detail: EDO Speed: 667 MHz Manufacturer: Not Specified Serial Number: Not Specified Asset Tag: Not Specified Part Number: Not Specified Handle 0x000E, DMI type 17, 27 bytes Memory Device Array Handle: 0x000C Error Information Handle: Not Provided Total Width: 32 bits Data Width: 32 bits Size: No Module Installed Form Factor: DIMM Set: None Locator: DIMM #1 Bank Locator: BANK #1 Type: DRAM Type Detail: EDO Speed: 667 MHz Manufacturer: Not Specified Serial Number: Not Specified Asset Tag: Not Specified Part Number: Not Specified Handle 0x000F, DMI type 17, 27 bytes Memory Device Array Handle: 0x000C Error Information Handle: Not Provided Total Width: 32 bits Data Width: 32 bits Size: No Module Installed Form Factor: DIMM Set: None Locator: DIMM #2 Bank Locator: BANK #2 Type: DRAM Type Detail: EDO Speed: 667 MHz Manufacturer: Not Specified Serial Number: Not Specified Asset Tag: Not Specified Part Number: Not Specified Handle 0x0010, DMI type 17, 27 bytes Memory Device Array Handle: 0x000C Error Information Handle: Not Provided Total Width: 32 bits Data Width: 32 bits Size: No Module Installed Form Factor: DIMM Set: None Locator: DIMM #3 Bank Locator: BANK #3 Type: DRAM Type Detail: EDO Speed: 667 MHz Manufacturer: Not Specified Serial Number: Not Specified Asset Tag: Not Specified Part Number: Not Specified Handle 0x0011, DMI type 19, 15 bytes Memory Array Mapped Address Starting Address: 0x00000000000 Ending Address: 0x0001FFFFFFF Range Size: 512 MB Physical Array Handle: 0x000C Partition Width: 0 Handle 0x0012, DMI type 32, 20 bytes System Boot Information Status: No errors detected Handle 0x0013, DMI type 127, 4 bytes End Of Table ssh_config.1100644001751001751 7713232707066 13463 0ustar00janjan000000000000Rex-1.6.0/tHost * StrictHostKeyChecking no UserKnownHostsFile=/dev/null cmdb000755001751001751 013232707066 12060 5ustar00janjan000000000000Rex-1.6.0/tfoo.yml100644001751001751 1213232707066 13457 0ustar00janjan000000000000Rex-1.6.0/t/cmdbname: foo 1008.t100644001751001751 364013232707066 13223 0ustar00janjan000000000000Rex-1.6.0/t/issueuse strict; use warnings; use Rex::Helper::IP; use Test::More tests => 16; my $ok = 0; my $ipv4 = "192.168.178.22"; my $ipv4_port = "192.168.178.22:2222"; my $ipv4_port_s = "192.168.178.22/2222"; my $ipv6 = "fe80::a00:27ff:fe36:9377"; my $ipv6_port = "fe80::a00:27ff:fe36:9377/3333"; my $hostname = "blah01"; my $hostname_port = "blah01:4444"; my $hostname_port_s = "blah01/4444"; my ( $s, $p ); ( $s, $p ) = Rex::Helper::IP::get_server_and_port($ipv4); is( $s, "192.168.178.22", "got v4 ip" ); is( $p, undef, "got no port from v4 ip" ); ( $s, $p ) = ( undef, undef ); ( $s, $p ) = Rex::Helper::IP::get_server_and_port($ipv4_port); is( $s, "192.168.178.22", "got v4 ip with port" ); is( $p, 2222, "got port from v4 ip with colon" ); ( $s, $p ) = ( undef, undef ); ( $s, $p ) = Rex::Helper::IP::get_server_and_port($ipv4_port_s); is( $s, "192.168.178.22", "got v4 ip with port with slash" ); is( $p, 2222, "got port from v4 ip with slash" ); ( $s, $p ) = ( undef, undef ); ( $s, $p ) = Rex::Helper::IP::get_server_and_port($ipv6); is( $s, "fe80::a00:27ff:fe36:9377", "got v6 ip" ); is( $p, undef, "got no port from v6 ip" ); ( $s, $p ) = ( undef, undef ); ( $s, $p ) = Rex::Helper::IP::get_server_and_port($ipv6_port); is( $s, "fe80::a00:27ff:fe36:9377", "got v6 ip with port" ); is( $p, 3333, "got port from v6 ip with colon" ); ( $s, $p ) = Rex::Helper::IP::get_server_and_port($hostname); is( $s, "blah01", "got hostname" ); is( $p, undef, "got no port from hostname" ); ( $s, $p ) = ( undef, undef ); ( $s, $p ) = Rex::Helper::IP::get_server_and_port($hostname_port); is( $s, "blah01", "got host with port" ); is( $p, 4444, "got port from hostname with colon" ); ( $s, $p ) = ( undef, undef ); ( $s, $p ) = Rex::Helper::IP::get_server_and_port($hostname_port_s); is( $s, "blah01", "got host with port with slash" ); is( $p, 4444, "got port from host with slash" ); Rex000755001751001751 013232707066 12214 5ustar00janjan000000000000Rex-1.6.0/libCLI.pm100644001751001751 5007513232707066 13350 0ustar00janjan000000000000Rex-1.6.0/lib/Rex# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::CLI; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use FindBin; use File::Basename qw(basename dirname); use Time::HiRes qw(gettimeofday tv_interval); use Cwd qw(getcwd); use List::Util qw(max); use Text::Wrap; use Term::ReadKey; use Sort::Naturally; use Rex; use Rex::Args; use Rex::Config; use Rex::Group; use Rex::Batch; use Rex::TaskList; use Rex::Logger; use YAML; use Data::Dumper; my $no_color = 0; eval "use Term::ANSIColor"; if ($@) { $no_color = 1; } if ( $^O =~ m/MSWin/ ) { eval "use Win32::Console::ANSI"; if ($@) { $no_color = 1; } } # preload some modules use Rex -base; $|++; my ( %opts, @help, @exit ); if ( $#ARGV < 0 ) { @ARGV = qw(-h); } sub new { my $that = shift; my $proto = ref($that) || $that; my $self = {@_}; bless( $self, $proto ); return $self; } sub __run__ { my ( $self, %more_args ) = @_; Rex::Args->parse_rex_opts; %opts = Rex::Args->getopts; if ( $opts{'Q'} ) { my ( $stdout, $stderr ); open( my $newout, '>', \$stdout ); select $newout; close(STDERR); } if ( $opts{'m'} ) { $no_color = 1; $Rex::Logger::no_color = 1; } if ( $opts{'d'} ) { $Rex::Logger::debug = $opts{'d'}; $Rex::Logger::silent = 0; } Rex::Config->set_use_cache(1); if ( $opts{"c"} ) { # Rex::Config->set_use_cache(1); # since 0.46 just a pseudo option # cache is enabled by default } elsif ( $opts{"C"} ) { Rex::Config->set_use_cache(0); } Rex::Logger::debug("This is Rex version: $Rex::VERSION"); Rex::Logger::debug("Command Line Parameters"); for my $param ( keys %opts ) { Rex::Logger::debug( "\t$param = " . $opts{$param} ); } if ( $opts{'h'} ) { $self->__help__; } elsif ( $opts{'v'} && !$opts{'T'} ) { $self->__version__; } if ( $opts{'q'} ) { $::QUIET = 1; if ( $opts{'w'} ) { $::QUIET = 2; } } $::rexfile = "Rexfile"; if ( $opts{'f'} ) { Rex::Logger::debug( "Using Rexfile: " . $opts{'f'} ); $::rexfile = $opts{'f'}; } else { if ( ( !-e $::rexfile ) && ( $ARGV[0] && $ARGV[0] =~ /:/ ) ) { #if there is no Rexfile, and the user asks for a longer path task, see if we can use it as the Rexfile #eg: rex -H $host Misc:Example:prepare --bar=baz $::rexfile = $ARGV[0]; $::rexfile =~ s/:[^:]*$//; $::rexfile =~ s{:}{/}g; $::rexfile = 'Rex/' . $::rexfile . '.pm'; } } FORCE_SERVER: { if ( $opts{'H'} ) { if ( $opts{'H'} =~ m/^perl:(.*)/ ) { my $host_eval = eval($1); if ( ref($host_eval) eq "ARRAY" ) { $::FORCE_SERVER = join( " ", @{$host_eval} ); } else { die("Perl Code have to return an array reference."); } } else { $::FORCE_SERVER = $opts{'H'}; } } } if ( $opts{'z'} ) { my $host_eval = eval { `$opts{'z'}`; }; if ( $host_eval =~ m/\S/xms ) { $::FORCE_SERVER = join( " ", split /\n|,|;/, $host_eval ); } else { $::FORCE_SERVER = $opts{'H'}; } } if ( $opts{'z'} ) { my $host_eval = eval { `$opts{'z'}`; }; if ( $host_eval =~ m/\S/xms ) { $::FORCE_SERVER = join( " ", split /\n|,|;/, $host_eval ); } else { Rex::Logger::info("You must give a valid command."); } } if ( $opts{'o'} ) { Rex::Output->require; Rex::Output->get( $opts{'o'} ); } handle_lock_file($::rexfile); Rex::Config->set_environment( $opts{"E"} ) if ( $opts{"E"} ); if ( $opts{'g'} || $opts{'G'} ) { #$::FORCE_SERVER = "\0" . $opts{'g'}; $opts{'g'} ||= $opts{'G'}; if ( ref $opts{'g'} ne "ARRAY" ) { $::FORCE_SERVER = [ $opts{'g'} ]; } else { $::FORCE_SERVER = $opts{'g'}; } } load_server_ini_file($::rexfile); load_rexfile($::rexfile); #### check if some parameters should be overwritten from the command line CHECK_OVERWRITE: { my $pass_auth = 0; if ( $opts{'u'} ) { Rex::Commands::user( $opts{'u'} ); for my $task ( Rex::TaskList->create()->get_tasks ) { Rex::TaskList->create()->get_task($task)->set_user( $opts{'u'} ); } } if ( $opts{'p'} ) { Rex::Commands::password( $opts{'p'} ); unless ( $opts{'P'} ) { $pass_auth = 1; } for my $task ( Rex::TaskList->create()->get_tasks ) { Rex::TaskList->create()->get_task($task)->set_password( $opts{'p'} ); } } if ( $opts{'P'} ) { Rex::Commands::private_key( $opts{'P'} ); for my $task ( Rex::TaskList->create()->get_tasks ) { Rex::TaskList->create()->get_task($task) ->set_auth( "private_key", $opts{'P'} ); } } if ( $opts{'K'} ) { Rex::Commands::public_key( $opts{'K'} ); for my $task ( Rex::TaskList->create()->get_tasks ) { Rex::TaskList->create()->get_task($task) ->set_auth( "public_key", $opts{'K'} ); } } if ($pass_auth) { pass_auth; } } Rex::Logger::debug("Initializing Logger from parameters found in $::rexfile"); if ( $opts{'T'} && $opts{'m'} ) { # create machine readable tasklist my @tasks = Rex::TaskList->create()->get_tasks; for my $task (@tasks) { my $desc = Rex::TaskList->create()->get_desc($task); $desc =~ s/'/\\'/gms; print "'$task'" . " = '$desc'\n"; } } elsif ( $opts{'T'} && $opts{'y'} ) { my @tasks = Rex::TaskList->create()->get_tasks; my @envs = Rex::Commands->get_environments(); my %groups = Rex::Group->get_groups; my %real_groups; for my $group ( keys %groups ) { my @servers = map { $_->get_servers } Rex::Group->get_group_object($group)->get_servers; $real_groups{$group} = \@servers; } print YAML::Dump( { tasks => \@tasks, envs => \@envs, groups => \%real_groups, } ); } elsif ( $opts{'T'} ) { _handle_T(%opts); Rex::global_sudo(0); Rex::Logger::debug("Removing lockfile") if ( !exists $opts{'F'} ); CORE::unlink("$::rexfile.lock") if ( !exists $opts{'F'} ); CORE::exit 0; } # turn sudo on with cli option s is used if ( exists $opts{'s'} ) { sudo("on"); } if ( exists $opts{'S'} ) { sudo_password( $opts{'S'} ); } if ( exists $opts{'t'} ) { parallelism( $opts{'t'} ); } if ( $opts{'e'} ) { Rex::Logger::debug("Executing command line code"); Rex::Logger::debug( "\t" . $opts{'e'} ); # execute the given code my $code = "sub { \n"; $code .= $opts{'e'} . "\n"; $code .= "}"; $code = eval($code); if ($@) { Rex::Logger::info( "Error in eval line: $@\n", "warn" ); exit 1; } if ( exists $opts{'t'} ) { parallelism( $opts{'t'} ); } my $pass_auth = 0; if ( $opts{'u'} ) { Rex::Commands::user( $opts{'u'} ); } if ( $opts{'p'} ) { Rex::Commands::password( $opts{'p'} ); unless ( $opts{'P'} ) { $pass_auth = 1; } } if ( $opts{'P'} ) { Rex::Commands::private_key( $opts{'P'} ); } if ( $opts{'K'} ) { Rex::Commands::public_key( $opts{'K'} ); } if ($pass_auth) { pass_auth; } my @params = (); if ( $opts{'H'} ) { push @params, split( /\s+/, $opts{'H'} ); } push @params, $code; push @params, "eval-line-desc"; push @params, {}; Rex::TaskList->create()->create_task( "eval-line", @params ); Rex::Commands::do_task("eval-line"); exit_rex(); } elsif ( $opts{'M'} ) { Rex::Logger::debug( "Loading Rex-Module: " . $opts{'M'} ); my $mod = $opts{'M'}; $mod =~ s{::}{/}g; $mod .= ".pm"; require $mod; } my $run_list = Rex::RunList->instance; if ( $opts{'b'} ) { my $batch = $opts{'b'}; Rex::Logger::debug("Running batch: $batch"); $run_list->add_task($_) for Rex::Batch->get_batch($batch); } $run_list->parse_opts(@ARGV); eval { $run_list->run_tasks }; if ($@) { # this is always the child Rex::Logger::info( "Error running task/batch: $@", "warn" ); CORE::exit(0); } exit_rex(); } sub _print_color { my ( $msg, $color ) = @_; $color = 'green' if !defined($color); if ($no_color) { print $msg; } else { print colored( [$color], $msg ); } } sub __help__ { my $fmt = " %-6s %s\n"; print "usage: \n"; print " rex [] [-H ] [-G ] []\n"; print " rex -T[m|y|v] []\n"; print "\n"; printf $fmt, "-b", "Run batch"; printf $fmt, "-e", "Run the given code fragment"; printf $fmt, "-E", "Execute a task on the given environment"; printf $fmt, "-G|-g", "Execute a task on the given server groups"; printf $fmt, "-H", "Execute a task on the given hosts (space delimited)"; printf $fmt, "-z", "Execute a task on hosts from this command's output"; print "\n"; printf $fmt, "-K", "Public key file for the ssh connection"; printf $fmt, "-P", "Private key file for the ssh connection"; printf $fmt, "-p", "Password for the ssh connection"; printf $fmt, "-u", "Username for the ssh connection"; print "\n"; printf $fmt, "-d", "Show debug output"; printf $fmt, "-ddd", "Show more debug output (includes profiling output)"; printf $fmt, "-m", "Monochrome output: no colors"; printf $fmt, "-o", "Output format"; printf $fmt, "-q", "Quiet mode: no log output"; printf $fmt, "-qw", "Quiet mode: only output warnings and errors"; printf $fmt, "-Q", "Really quiet: output nothing"; print "\n"; printf $fmt, "-T", "List tasks"; printf $fmt, "-Tm", "List tasks in machine-readable format"; printf $fmt, "-Tv", "List tasks verbosely"; printf $fmt, "-Ty", "List tasks in YAML format"; print "\n"; printf $fmt, "-c", "Turn cache ON"; printf $fmt, "-C", "Turn cache OFF"; printf $fmt, "-f", "Use this file instead of Rexfile"; printf $fmt, "-F", "Force: disregard lock file"; printf $fmt, "-h", "Display this help message"; printf $fmt, "-M", "Load this module instead of Rexfile"; printf $fmt, "-O", "Pass additional options, like CMDB path"; printf $fmt, "-s", "Use sudo for every command"; printf $fmt, "-S", "Password for sudo"; printf $fmt, "-t", "Number of threads to use (aka 'parallelism' param)"; printf $fmt, "-v", "Display (R)?ex version"; print "\n"; for my $code (@help) { &$code(); } CORE::exit 0; } sub add_help { my ( $self, $code ) = @_; push( @help, $code ); } sub add_exit { my ( $self, $code ) = @_; push( @exit, $code ); } sub __version__ { print "(R)?ex " . $Rex::VERSION . "\n"; CORE::exit 0; } sub _handle_T { my %opts = @_; my ($cols) = Term::ReadKey::GetTerminalSize(*STDOUT); $Text::Wrap::columns = $cols || 80; _list_tasks(); _list_batches(); _list_envs(); _list_groups(); } sub _list_tasks { Rex::Logger::debug("Listing Tasks"); my @tasks = Rex::TaskList->create()->get_tasks; if ( defined $ARGV[0] ) { @tasks = grep { $_ =~ /^$ARGV[0]/ } @tasks; # Warn if the user passed args to '-T' and no matching task names were found Rex::Logger::info( "No tasks matching '$ARGV[0]' found.", "error" ) unless @tasks; } return unless @tasks; # fancy sorting of tasks -- put tasks from Rexfile first my @root_tasks = grep { !/:/ } @tasks; my @other_tasks = grep { /:/ } @tasks; @tasks = ( sort(@root_tasks), sort(@other_tasks) ); _print_color( "Tasks\n", "yellow" ); my $max_task_len = max map { length } @tasks; my $fmt = " %-" . $max_task_len . "s %s\n"; my $last_namespace = _namespace( $tasks[0] ); for my $task (@tasks) { print "\n" if $last_namespace ne _namespace($task); $last_namespace = _namespace($task); my $description = Rex::TaskList->create()->get_desc($task); my $output = sprintf $fmt, $task, $description; my $indent = " " x $max_task_len . " "; print wrap( "", $indent, $output ); if ( $opts{'v'} ) { my @servers = sort @{ Rex::TaskList->create()->get_task($task)->server }; _print_color( " Servers: " . join( ", ", @servers ) . "\n" ); } } } sub _namespace { my ($full_task_name) = @_; return "" unless $full_task_name =~ /:/; my ($namespace) = split /:/, $full_task_name; return $namespace; } sub _list_batches { Rex::Logger::debug("Listing Batches"); my @batchs = sort Rex::Batch->get_batchs; return unless Rex::Batch->get_batchs; _print_color( "Batches\n", 'yellow' ); my $max_batch_len = max map { length } @batchs; my $fmt = " %-" . $max_batch_len . "s %s\n"; for my $batch ( sort @batchs ) { my $description = Rex::Batch->get_desc($batch); my $output = sprintf $fmt, $batch, $description; my $indent = " " x $max_batch_len . " "; print wrap( "", $indent, $output ); if ( $opts{'v'} ) { my @tasks = Rex::Batch->get_batch($batch); _print_color( " " . join( " ", @tasks ) . "\n" ); } } } sub _list_envs { Rex::Logger::debug("Listing Envs"); my @envs = map { Rex::Commands->get_environment($_) } sort Rex::Commands->get_environments(); return unless @envs; _print_color( "Environments\n", "yellow" ) if scalar @envs; my $max_env_len = max map { length $_->{name} } @envs; my $fmt = " %-" . $max_env_len . "s %s\n"; for my $e ( sort @envs ) { my $output = sprintf $fmt, $e->{name}, $e->{description}; my $indent = " " x $max_env_len . " "; print wrap( "", $indent, $output ); } } sub _list_groups { Rex::Logger::debug("Listing Groups"); my %groups = Rex::Group->get_groups; my @group_names = sort keys %groups; return unless @group_names; _print_color( "Server Groups\n", "yellow" ); my $max_group_len = max map { length } @group_names; my $fmt = " %-" . $max_group_len . "s %s\n"; for my $group_name (@group_names) { my $hosts = join( ", ", sort @{ $groups{$group_name} } ); my $output = sprintf $fmt, $group_name, $hosts; my $indent = " " x $max_group_len . " "; print wrap( "", $indent, $output ); } } sub summarize { my ($signal) = @_; my %opts = Rex::Args->getopts; return if $opts{'T'}; my @summary = Rex::TaskList->create()->get_summary(); return unless @summary; # no tasks ran -- nothing to summarize my @failures = grep { $_->{exit_code} != 0 } @summary; if ( !@failures ) { Rex::Logger::info("All tasks successful on all hosts"); return; } Rex::Logger::info( @failures . " out of " . @summary . " task(s) failed:", "error" ); foreach ( sort { ncmp( $a->{task}, $b->{task} ) || ncmp( $a->{server}, $b->{server} ) } @failures ) { Rex::Logger::info( "\t$_->{task} failed on $_->{server}", "error" ); if ( $_->{error_message} ) { for my $line ( split( $/, $_->{error_message} ) ) { Rex::Logger::info( "\t\t$line", "error" ); } } } } sub handle_lock_file { my $rexfile = shift; if ( $^O !~ m/^MSWin/ ) { if ( -f "$rexfile.lock" && !exists $opts{'F'} ) { Rex::Logger::debug("Found $rexfile.lock"); my $pid = eval { local ( @ARGV, $/ ) = ("$rexfile.lock"); <>; }; system( "ps aux | awk -F' ' ' { print \$2 } ' | grep $pid >/dev/null 2>&1"); if ( $? == 0 ) { Rex::Logger::info("Rexfile is in use by $pid."); CORE::exit 1; } else { Rex::Logger::debug("Found stale lock file. Removing it."); Rex::global_sudo(0); CORE::unlink("$rexfile.lock"); } } Rex::Logger::debug("Creating lock-file ($rexfile.lock)"); open( my $f, ">", "$rexfile.lock" ) or die($!); print $f $$; close($f); } else { Rex::Logger::debug("Running on windows. Disabled lock file support."); } } sub load_server_ini_file { my $rexfile = shift; # load server ini file my $env = environment; my $ini_dir = dirname($rexfile); my $server_ini_file = "$ini_dir/server.$env.ini"; $server_ini_file = "$ini_dir/server.ini" unless -f $server_ini_file; if ( -f $server_ini_file && Rex::Group::Lookup::INI->is_loadable ) { Rex::Logger::debug("Loading $server_ini_file"); Rex::Group::Lookup::INI::groups_file($server_ini_file); } } sub load_rexfile { my $rexfile = shift; Rex::Logger::debug("Loading $rexfile"); if ( !-f $rexfile ) { if ( !exists $opts{'e'} ) { Rex::Logger::info( "No Rexfile found.", "warn" ); Rex::Logger::info( "Create a file named 'Rexfile' in this directory,", "warn" ); Rex::Logger::info( "or specify the file you want to use with:", "warn" ); Rex::Logger::info( " rex -f file_to_use task_to_run", "warn" ); } return; } my $rexfile_dir = dirname $rexfile; my @new_inc = Rex::generate_inc($rexfile_dir); @INC = @new_inc; # load Rexfile eval { # add a true return value at the end of $rexfile. # we need to do this because perl want a "true" value # at the end of a file that is loaded. unshift @INC, sub { my $load_file = $_[1]; if ( $load_file eq "__Rexfile__.pm" ) { open( my $fh, "<", $rexfile ) or die("Error can't open $rexfile: $!"); my @content = <$fh>; close($fh); chomp @content; my $i = 0; my $found_end = 0; # some rexfile has a __DATA__ or __END__ section # and we need to add the true value before those sections. for my $line (@content) { if ( $line =~ m/^__(DATA|END)__$/ ) { splice( @content, $i, 0, "42;" ); $found_end++; last; } $i++; } # we didn't found __DATA__ or __END__ so we just add # it at the end. if ( $found_end == 0 ) { push @content, "42;"; } # we can't remove this load from @INC because on perl 5.8 # this causes a crash #shift @INC; # remove this loader from @INC # we can't directly return a scalar reference because perl 5.8 # needs a filehandle. so we create a virtual filehandle... my $c = join( "\n", @content ); open( my $rex_fh, "<", \$c ); return $rex_fh; } }; my ( $stdout, $stderr, $default_stderr ); open $default_stderr, ">&", STDERR; # we close STDERR here because we don't want to see the # normal perl error message on the screen. Instead we print # the error message in the catch-if below. local *STDERR; open( STDERR, ">>", \$stderr ); # we can't use $rexfile here, because if the variable contains dots # the perl interpreter try to load the file directly without using @INC # so we just fake a module name. require __Rexfile__; # update %INC so that we can later use it to find the rexfile $INC{"__Rexfile__.pm"} = $rexfile; # reopen STDERR open STDERR, ">&", $default_stderr; if ($stderr) { my @lines = split( $/, $stderr ); Rex::Logger::info( "You have some code warnings:", 'warn' ); Rex::Logger::info( "\t$_", 'warn' ) for @lines; } 1; }; if ($@) { my $e = $@; chomp $e; # remove the strange path to the Rexfile which exists because # we load the Rexfile via our custom code block. $e =~ s|/loader/[^/]+/||smg; my @lines = split( $/, $e ); Rex::Logger::info( "Compile time errors:", 'error' ); Rex::Logger::info( "\t$_", 'error' ) for @lines; exit 1; } } sub exit_rex { my ( $exit_code_override, $signal ) = @_; summarize($signal) if !$signal; Rex::global_sudo(0); Rex::Logger::debug("Removing lockfile") if !exists $opts{'F'}; unlink("$::rexfile.lock") if !exists $opts{'F'}; select STDOUT; if ( !$signal && $opts{'o'} && defined( Rex::Output->get ) ) { Rex::Output->get->write(); IPC::Shareable->clean_up_all(); } for my $exit_hook (@exit) { $exit_hook->( $exit_code_override, $signal ); } if ($Rex::WITH_EXIT_STATUS) { CORE::exit($exit_code_override) if defined $exit_code_override; my @exit_codes = Rex::TaskList->create()->get_exit_codes(); for my $exit_code (@exit_codes) { $exit_code = $exit_code >> 8 if $exit_code > 255; CORE::exit($exit_code) if $exit_code != 0; } } CORE::exit(0); } # we capture CTRL+C so we can cleanup vars files # and give modules the chance to also do cleanup $SIG{INT} = sub { exit_rex( 1, "INT" ); }; 1; Pkg.pm100644001751001751 315313232707066 13435 0ustar00janjan000000000000Rex-1.6.0/lib/Rex# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Pkg; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Rex::Config; use Rex::Commands::Gather; use Rex::Hardware; use Rex::Hardware::Host; use Rex::Logger; use Data::Dumper; my %PKG_PROVIDER; sub register_package_provider { my ( $class, $service_name, $service_class ) = @_; $PKG_PROVIDER{"\L$service_name"} = $service_class; return 1; } sub get { my ($self) = @_; my %_host = %{ Rex::Hardware::Host->get() }; my $host = {%_host}; my $pkg_provider_for = Rex::Config->get("package_provider") || {}; #if(lc($host->{"operatingsystem"}) eq "centos" || lc($host->{"operatingsystem"}) eq "redhat") { if ( is_redhat() ) { $host->{"operatingsystem"} = "Redhat"; } if ( is_debian() ) { $host->{"operatingsystem"} = "Debian"; } my $class = "Rex::Pkg::" . $host->{"operatingsystem"}; my $provider; if ( ref($pkg_provider_for) && exists $pkg_provider_for->{ $host->{"operatingsystem"} } ) { $provider = $pkg_provider_for->{ $host->{"operatingsystem"} }; $class .= "::$provider"; } elsif ( exists $PKG_PROVIDER{$pkg_provider_for} ) { $class = $PKG_PROVIDER{$pkg_provider_for}; } Rex::Logger::debug("Using $class for package management"); eval "use $class"; if ($@) { if ($provider) { Rex::Logger::info( "Provider not supported (" . $provider . ")" ); } else { Rex::Logger::info( "OS not supported (" . $host->{"operatingsystem"} . ")" ); } die("OS/Provider not supported"); } return $class->new; } 1; Box.pm100644001751001751 160313232707066 13442 0ustar00janjan000000000000Rex-1.6.0/lib/Rex# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Box; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Rex::Config; use Rex::Logger; my %BOX_PROVIDER; sub register_box_provider { my ( $class, $service_name, $service_class ) = @_; $BOX_PROVIDER{"\L$service_name"} = $service_class; return 1; } sub create { my ( $class, @opts ) = @_; my $type = Rex::Config->get("box_type") || "VBox"; my $options = Rex::Config->get("box_options") || {}; my $klass = "Rex::Box::${type}"; if ( exists $BOX_PROVIDER{$type} ) { $klass = $BOX_PROVIDER{$type}; } Rex::Logger::debug("Using $klass as box provider"); eval "use $klass;"; if ($@) { Rex::Logger::info("Box Class $klass not found."); die("Box Class $klass not found."); } return $klass->new( @opts, options => $options ); } 1; ifconfig.out6100644001751001751 251413232707066 13720 0ustar00janjan000000000000Rex-1.6.0/teth0: flags=4163 mtu 1500 inet 192.168.112.182 netmask 255.255.255.0 broadcast 192.168.112.255 inet6 fe80::5054:ff:fe37:a8e1 prefixlen 64 scopeid 0x20 ether 52:54:00:37:a8:e1 txqueuelen 1000 (Ethernet) RX packets 18300 bytes 94489008 (90.1 MiB) RX errors 0 dropped 3 overruns 0 frame 0 TX packets 14582 bytes 1308678 (1.2 MiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 eth0:1: flags=4163 mtu 1500 inet 1.2.3.4 netmask 255.255.0.0 broadcast 1.2.255.255 inet6 fe80::5054:ff:fe37:a8e1 prefixlen 64 scopeid 0x20 ether 52:54:00:37:a8:e1 txqueuelen 1000 (Ethernet) RX packets 18300 bytes 94489008 (90.1 MiB) RX errors 0 dropped 3 overruns 0 frame 0 TX packets 14582 bytes 1308678 (1.2 MiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 lo: flags=73 mtu 65536 inet 127.0.0.1 netmask 255.0.0.0 inet6 ::1 prefixlen 128 scopeid 0x10 loop txqueuelen 0 (Lokale Schleife) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 ifconfig.out2100644001751001751 66413232707066 13700 0ustar00janjan000000000000Rex-1.6.0/tvif1.0 Link encap:Ethernet HWaddr fe:ff:ff:ff:ff:ff inet6 addr: fe80::fcff:ffff:feff:ffff/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:426013098 errors:0 dropped:0 overruns:0 frame:0 TX packets:407288230 errors:0 dropped:12 overruns:0 carrier:0 collisions:0 txqueuelen:32 RX bytes:519084830261 (483.4 GiB) TX bytes:172943515025 (161.0 GiB) ifconfig.out3100644001751001751 56513232707066 13701 0ustar00janjan000000000000Rex-1.6.0/teth0 Link encap:Ethernet HWaddr 00:21:cc:5f:7c:6c UP BROADCAST MULTICAST MTU:1500 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) Interrupt:20 Memory:f3900000-f3920000 dmi.linux.out100644001751001751 1270213232707066 13775 0ustar00janjan000000000000Rex-1.6.0/t# dmidecode 2.11 SMBIOS 2.3 present. 20 structures occupying 896 bytes. Table at 0x000F6120. Handle 0x0000, DMI type 0, 20 bytes BIOS Information Vendor: Parallels Software International Inc. Version: 7.0.15107.796624 Release Date: 10/26/2007 Address: 0xF0000 Runtime Size: 64 kB ROM Size: 64 kB Characteristics: ISA is supported PCI is supported PNP is supported APM is supported VLB is supported Boot from CD is supported 8042 keyboard services are supported (int 9h) Serial services are supported (int 14h) Printer services are supported (int 17h) CGA/mono video services are supported (int 10h) ACPI is supported Handle 0x0001, DMI type 1, 25 bytes System Information Manufacturer: Parallels Software International Inc. Product Name: Parallels Virtual Platform Version: None Serial Number: Parallels-EB 27 9A D5 DE 07 40 9E 9D AB F9 C0 3E 8D 49 DE UUID: EB279AD5-DE07-409E-9DAB-F9C03E8D49DE Wake-up Type: Power Switch Handle 0x0002, DMI type 2, 8 bytes Base Board Information Manufacturer: Parallels Software International Inc. Product Name: Parallels Virtual Platform Version: None Serial Number: None Handle 0x0003, DMI type 3, 18 bytes Chassis Information Manufacturer: Parallels Software International Inc. Type: Unknown Lock: Not Present Version: Serial Number: Asset Tag: Boot-up State: Safe Power Supply State: Safe Thermal State: Safe Security Status: None OEM Information: 0x00000000 Handle 0x0004, DMI type 4, 35 bytes Processor Information Socket Designation: CPU Socket #0 Type: Central Processor Family: Unknown Manufacturer: GenuineIntel ID: E5 06 01 00 FF FB EB BF Version: Not Specified Voltage: 3.3 V External Clock: 466 MHz Max Speed: 2800 MHz Current Speed: 2800 MHz Status: Populated, Enabled Upgrade: Other L1 Cache Handle: Not Provided L2 Cache Handle: Not Provided L3 Cache Handle: Not Provided Serial Number: Not Specified Asset Tag: Not Specified Part Number: Not Specified Handle 0x0005, DMI type 9, 13 bytes System Slot Information Designation: ISA slot 1 Type: 16-bit ISA Current Usage: Unknown Length: Unknown Characteristics: 3.3 V is provided Handle 0x0006, DMI type 9, 13 bytes System Slot Information Designation: PCI slot 1 Type: 32-bit PCI Current Usage: Unknown Length: Unknown ID: 1 Characteristics: 3.3 V is provided Handle 0x0007, DMI type 9, 13 bytes System Slot Information Designation: PCI slot 2 Type: 32-bit PCI Current Usage: Unknown Length: Unknown ID: 2 Characteristics: 3.3 V is provided Handle 0x0008, DMI type 9, 13 bytes System Slot Information Designation: PCI slot 3 Type: 32-bit PCI Current Usage: Unknown Length: Unknown ID: 3 Characteristics: 3.3 V is provided Handle 0x0009, DMI type 9, 13 bytes System Slot Information Designation: PCI slot 4 Type: 32-bit PCI Current Usage: Unknown Length: Unknown ID: 4 Characteristics: 3.3 V is provided Handle 0x000A, DMI type 9, 13 bytes System Slot Information Designation: PCI slot 5 Type: 32-bit PCI Current Usage: Unknown Length: Unknown ID: 5 Characteristics: 3.3 V is provided Handle 0x000B, DMI type 10, 8 bytes On Board Device 1 Information Type: Video Status: Disabled Description: Parallels Video Adapter On Board Device 2 Information Type: Sound Status: Disabled Description: Parallels Sound Adapter Handle 0x000C, DMI type 16, 15 bytes Physical Memory Array Location: System Board Or Motherboard Use: System Memory Error Correction Type: None Maximum Capacity: 8 GB Error Information Handle: Not Provided Number Of Devices: 4 Handle 0x000D, DMI type 17, 27 bytes Memory Device Array Handle: 0x000C Error Information Handle: Not Provided Total Width: 32 bits Data Width: 32 bits Size: 512 MB Form Factor: DIMM Set: None Locator: DIMM #0 Bank Locator: BANK #0 Type: DRAM Type Detail: EDO Speed: 667 MHz Manufacturer: Not Specified Serial Number: Not Specified Asset Tag: Not Specified Part Number: Not Specified Handle 0x000E, DMI type 17, 27 bytes Memory Device Array Handle: 0x000C Error Information Handle: Not Provided Total Width: 32 bits Data Width: 32 bits Size: No Module Installed Form Factor: DIMM Set: None Locator: DIMM #1 Bank Locator: BANK #1 Type: DRAM Type Detail: EDO Speed: 667 MHz Manufacturer: Not Specified Serial Number: Not Specified Asset Tag: Not Specified Part Number: Not Specified Handle 0x000F, DMI type 17, 27 bytes Memory Device Array Handle: 0x000C Error Information Handle: Not Provided Total Width: 32 bits Data Width: 32 bits Size: No Module Installed Form Factor: DIMM Set: None Locator: DIMM #2 Bank Locator: BANK #2 Type: DRAM Type Detail: EDO Speed: 667 MHz Manufacturer: Not Specified Serial Number: Not Specified Asset Tag: Not Specified Part Number: Not Specified Handle 0x0010, DMI type 17, 27 bytes Memory Device Array Handle: 0x000C Error Information Handle: Not Provided Total Width: 32 bits Data Width: 32 bits Size: No Module Installed Form Factor: DIMM Set: None Locator: DIMM #3 Bank Locator: BANK #3 Type: DRAM Type Detail: EDO Speed: 667 MHz Manufacturer: Not Specified Serial Number: Not Specified Asset Tag: Not Specified Part Number: Not Specified Handle 0x0011, DMI type 19, 15 bytes Memory Array Mapped Address Starting Address: 0x00000000000 Ending Address: 0x0001FFFFFFF Range Size: 512 MB Physical Array Handle: 0x000C Partition Width: 2 Handle 0x0012, DMI type 32, 20 bytes System Boot Information Status: No errors detected Handle 0x0013, DMI type 127, 4 bytes End Of Table ifconfig.out1100644001751001751 76113232707066 13675 0ustar00janjan000000000000Rex-1.6.0/teth0 Link encap:Ethernet HWaddr 00:16:3e:7f:fc:3a inet addr:10.18.1.107 Bcast:10.18.1.255 Mask:255.255.255.0 inet6 addr: fe80::216:3eff:fe7f:fc3a/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:7581854 errors:0 dropped:0 overruns:0 frame:0 TX packets:6074960 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:725754206 (725.7 MB) TX bytes:1594218416 (1.5 GB) ifconfig.out7100644001751001751 67313232707066 13705 0ustar00janjan000000000000Rex-1.6.0/tppp0 Link encap:Point-to-Point Protocol inet addr:123.117.251.17 P-t-P:234.165.249.179 Mask:255.255.255.255 UP POINTOPOINT RUNNING NOARP MULTICAST MTU:1454 Metric:1 RX packets:4143329786 errors:0 dropped:0 overruns:0 frame:0 TX packets:754829057 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:3 RX bytes:1171440390 (1.0 GiB) TX bytes:121847007 (116.2 MiB) ifconfig.out5100644001751001751 100013232707066 13704 0ustar00janjan000000000000Rex-1.6.0/teth1 Link encap:Ethernet HWaddr 00:50:56:86:48:E6 inet addr:10.64.82.10 Bcast:10.64.82.31 Mask:255.255.255.224 inet6 addr: fe80::250:56ff:fe86:48e6/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:1723537000 errors:0 dropped:0 overruns:0 frame:0 TX packets:1490804633 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:1276409391132 (1.1 TiB) TX bytes:1122763800358 (1.0 TiB) helper_hash.t100644001751001751 201713232707066 13762 0ustar00janjan000000000000Rex-1.6.0/tuse strict; use warnings; use Test::More tests => 6; use Rex::Helper::Hash; my %h = ( name => "FooBar", age => 99, data => { foo => "bar", blah => "fasel", more => { a => "eins", b => "zwei", c => "drei", d => { germany => "Berlin", france => "Paris", }, }, emails => [ 'm@m.m', 'a@a.a', { n1 => "nested_1", n2 => "nested_2", }, [ 'eins', 'zwei', 'drei', ], ], }, blub => [qw/eins zwei drei/], ); my $nh = {}; hash_flatten( \%h, $nh, "_" ); is( $nh->{"age"}, 99, "testing flattened hash" ); is( $nh->{"data_more_d_france"}, "Paris", "testing flattened hash - nested" ); is( $nh->{"blub_0"}, "eins", "testing flattened array" ); is( $nh->{"data_emails_0"}, 'm@m.m', "testing flattened array - nested" ); is( $nh->{"data_emails_1"}, 'a@a.a', "testing flattened array - nested (2)" ); is( $nh->{"data_emails_2_n1"}, 'nested_1', "testing flattened hash nested in array" ); helper_path.t100644001751001751 373513232707066 14003 0ustar00janjan000000000000Rex-1.6.0/tuse strict; use warnings; use Test::More tests => 8; use File::Basename; use Cwd 'getcwd'; use Rex::Helper::Path; my $rexfile = "Rexfile"; my $file = Rex::Helper::File::Spec->join( "files", "foo.txt" ); my $path = Rex::Helper::Path::get_file_path( $file, "main", $rexfile ); my $expected = $file; is( $path, $expected, "got file path if called from Rexfile" ); my $cwd = getcwd; $file = Rex::Helper::File::Spec->join( $cwd, "ChangeLog" ); $path = Rex::Helper::Path::get_file_path( $file, "main", $rexfile ); $expected = $file; is( $path, $file, "got file path if called from Rexfile - absolute path" ); $rexfile = Rex::Helper::File::Spec->join( "this", "is", "Rexfile" ); $file = Rex::Helper::File::Spec->join( "files", "foo.txt" ); $path = Rex::Helper::Path::get_file_path( $file, "main", $rexfile ); $expected = Rex::Helper::File::Spec->join( "this", "is", "files", "foo.txt" ); is( $path, $expected, "got file path if called Rexfile from other directory" ); $rexfile = Rex::Helper::File::Spec->join( Rex::Helper::File::Spec->rootdir(), "this", "is", "Rexfile" ); $file = Rex::Helper::File::Spec->join( "files", "foo.txt" ); $path = Rex::Helper::Path::get_file_path( $file, "main", $rexfile ); $expected = Rex::Helper::File::Spec->join( Rex::Helper::File::Spec->rootdir(), "this", "is", "files", "foo.txt" ); is( $path, $expected, "got file path if called Rexfile from other directory (absolute)" ); my $module_path = Rex::Helper::File::Spec->join( "lib", "File", "Foo", "__module__.pm" ); $path = Rex::Helper::Path::get_file_path( $file, "File::Foo", $module_path ); $expected = Rex::Helper::File::Spec->join( "lib", "File", "Foo", "files", "foo.txt" ); is( $path, $expected, "got file path for File::Foo module" ); $path = Rex::Helper::Path::get_tmp_file(); my ( $filename, $directory, $suffix ) = fileparse( $path, '.tmp' ); ok( defined $filename, 'Got temp filename' ); is( $suffix, '.tmp', 'Got filename with .tmp suffix' ); ok( defined $directory, 'Got temp directory' ); template_ng.t100644001751001751 574313232707066 14010 0ustar00janjan000000000000Rex-1.6.0/tuse strict; use warnings; use Test::More tests => 21; use Rex::Template::NG; use Rex::Commands; use Rex::Config; my $t = Rex::Template::NG->new; my $content = 'one two three'; is( $t->parse( $content, {} ), "one two three", "just text" ); $content = '{ 1 }'; my $content_ok = '{ 1 }'; is( $t->parse( $content, {} ), $content_ok, "curly braces" ); $content = 'Hello this is <%= $::name %>'; is( $t->parse( $content, { name => "foo" } ), "Hello this is foo", "simple variable" ); $content = 'Hello this is <%=$::name%>'; is( $t->parse( $content, { name => "bar" } ), "Hello this is bar", "simple variable - no spaces around tag" ); $content = '<% if($::logged_in) { %> Logged in! <% } else { %> Logged out! <% } %>'; $content_ok = " Logged in! "; is( $t->parse( $content, { logged_in => 1 } ), $content_ok, "if condition" ); $content = 'Hello this is <%= $::name %>'; is( Rex::Config->get_template_function()->( $content, { name => "baz" } ), "Hello this is baz", "get template function" ); is( $t->parse( $content, name => "bar" ), "Hello this is bar", "simple variable without hashRef" ); $Rex::Template::BE_LOCAL = 1; $Rex::Template::BE_LOCAL = 1; $content = 'Hello this is <%= $foo %>'; is( $t->parse( $content, { foo => "baz" } ), "Hello this is baz", "local vars" ); $content = '<%= join(",", @{ $arr }) %>'; is( $t->parse( $content, { arr => [qw/one two three/] } ), "one,two,three", "local var with array" ); $content = 'This is a false value: <%= $value %>'; $content_ok = 'This is a false value: 0'; is( $t->parse( $content, value => 0 ), $content_ok, 'false value passed' ); # # old variable style # $content = 'one two three'; is( $t->parse( $content, {} ), "one two three", "just text" ); $content = 'Hello this is <%= $::name %>'; is( $t->parse( $content, { name => "foo" } ), "Hello this is foo", "simple variable" ); $content = '<% if($::logged_in) { %> Logged in! <% } else { %> Logged out! <% } %>'; $content_ok = " Logged in! "; is( $t->parse( $content, { logged_in => 1 } ), $content_ok, "if condition" ); $content = 'Hello this is <%= $::name %>'; is( Rex::Config->get_template_function()->( $content, { name => "baz" } ), "Hello this is baz", "get template function" ); is( $t->parse( $content, name => "bar" ), "Hello this is bar", "simple variable without hashRef" ); $content = 'Hello this is <%= $::foo %> <%= $::veth1_0_ip %>'; is( $t->parse( $content, { foo => "baz", "veth1.0_ip" => "10.1.2.3" } ), "Hello this is baz 10.1.2.3", "template with invalid key name" ); my $v = { "foo" => { val => "val1", name => "foo" }, "foo_bar" => { val => "val2", name => "foo_bar" }, "k-ey" => { val => "val3", name => "k_ey" }, "veth0.1" => { val => "val4", name => "veth0_1" }, "2nd\\key" => { val => "val5", name => "2nd_key" }, }; for my $key ( keys %{$v} ) { my $var_name = Rex::Template::_normalize_var_name($key); is( $var_name, $v->{$key}->{name}, "$var_name is equal to " . $v->{$key}->{name} ); } ifconfig.out4100644001751001751 75713232707066 13705 0ustar00janjan000000000000Rex-1.6.0/twlan0 Link encap:Ethernet HWaddr 8c:a9:82:b0:82:04 inet addr:10.50.20.173 Bcast:10.50.255.255 Mask:255.255.0.0 inet6 addr: fe80::8ea9:82ff:feb0:8204/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:183505 errors:0 dropped:0 overruns:0 frame:0 TX packets:76048 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:227762379 (227.7 MB) TX bytes:11297856 (11.2 MB) Test.pm100644001751001751 160213232707066 13630 0ustar00janjan000000000000Rex-1.6.0/lib/Rex# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Test; use strict; use warnings; use Rex -base; use Data::Dumper; use Rex::Commands::Box; require Rex::CLI; our $VERSION = '1.6.0'; # VERSION BEGIN { use Rex::Shared::Var; share qw(@exit); } Rex::CLI->add_exit( sub { if ( scalar @exit > 0 ) { CORE::exit(1); } } ); sub push_exit { push @exit, shift; } desc 'Run tests specified with --test=testfile (default: t/*.t)'; task run => make { Rex::Logger::info("Running integration tests..."); my $parameters = shift; my @files; LOCAL { @files = defined $parameters->{test} ? glob( $parameters->{test} ) : glob('t/*.t'); }; for my $file (@files) { Rex::Logger::info("Running test: $file."); do "./$file"; Rex::Logger::info( "Error running $file: $@", "error" ) if $@; } }; 1; Cron.pm100644001751001751 146613232707066 13622 0ustar00janjan000000000000Rex-1.6.0/lib/Rex# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Cron; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Rex::Commands::Gather; use List::Util qw'first'; sub create { my ($class) = @_; my $type = "Linux"; if ( operating_system_is("SunOS") ) { $type = "SunOS"; } my $exec = Rex::Interface::Exec->create; # here we're using first and not any, because in older perl versions # there is no any() function in List::Util. if ( operating_system_is( "FreeBSD", "OpenBSD", "NetBSD" ) && first { $exec->shell->name eq $_ } (qw/csh ksh tcsh/) ) { $type = "FreeBSD"; } my $klass = "Rex::Cron::$type"; eval "use $klass;"; if ($@) { die("Error creating cron class: $klass\n$@"); } return $klass->new; } 1; CMDB.pm100644001751001751 753113232707066 13425 0ustar00janjan000000000000Rex-1.6.0/lib/Rex# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: =head1 NAME Rex::CMDB - Function to access the CMDB (configuration management database) =head1 DESCRIPTION This module exports a function to access a CMDB via a common interface. =head1 SYNOPSIS use Rex::CMDB; set cmdb => { type => 'YAML', path => [ 'cmdb/{hostname}.yml', 'cmdb/default.yml', ], merge_behavior => 'LEFT_PRECEDENT', }; task "prepare", "server1", sub { my $virtual_host = cmdb("vhost"); my %all_information = cmdb; }; =head1 EXPORTED FUNCTIONS =cut package Rex::CMDB; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Rex::Commands; use Rex::Value; require Rex::Exporter; use base qw(Rex::Exporter); use vars qw(@EXPORT); @EXPORT = qw(cmdb); my $CMDB_PROVIDER; =head2 set cmdb CMDB is enabled by default, with Rex::CMDB::YAML as default provider. The path option specifies an ordered list of places to look for CMDB information. The path specification supports any Rex::Hardware variable as macros, when enclosed within curly braces. Macros are dynamically expanded during runtime. The default path settings is: [qw( cmdb/{operatingsystem}/{hostname}.yml cmdb/{operatingsystem}/default.yml cmdb/{environment}/{hostname}.yml cmdb/{environment}/default.yml cmdb/{hostname}.yml cmdb/default.yml )] Please note that the default environment is, well, "default". You can define additional CMDB paths via the `-O` command line option by using a semicolon-separated list of `cmdb_path=path` key-value pairs: rex -O 'cmdb_path=cmdb/{domain}.yml;cmdb_path=cmdb/{domain}/{hostname}.yml;' taskname Those additional paths will be prepended to the current list of CMDB paths (so the last one specified will get on top, and thus checked first). The CMDB module looks up the specified files in order and then returns the requested data. If multiple files specify the same data for a given case, then the first instance of the data will be returned by default. Rex uses Hash::Merge internally to merge the data found on different levels of the CMDB hierarchy. Any merge strategy supported by that module can be specified to override the default one. For example one of the built-in strategies: merge_behavior => 'LEFT_PRECEDENT' Or even custom ones: merge_behavior => { SCALAR => { ... }, ARRAY => { ... }, HASH => { ... }, } For full list of options, please see the documentation of Hash::Merge. =cut Rex::Config->register_set_handler( "cmdb" => sub { my ($option) = @_; my %args = Rex::Args->getopts; if ( exists $args{O} ) { for my $itm ( split( /;/, $args{O} ) ) { my ( $key, $val ) = split( /=/, $itm ); if ( $key eq "cmdb_path" ) { if ( ref $option->{path} eq "ARRAY" ) { unshift @{ $option->{path} }, $val; } else { $option->{path} = [$val]; } } } } $CMDB_PROVIDER = $option; } ); =head2 cmdb([$item, $server]) Function to query a CMDB. If this function is called without $item it should return a hash containing all the information for the requested server. If $item is given it should return only the value for $item. task "prepare", "server1", sub { my $virtual_host = cmdb("vhost"); my %all_information = cmdb; }; =cut sub cmdb { my ( $item, $server ) = @_; $server ||= connection->server; my $klass = $CMDB_PROVIDER->{type}; if ( !$klass ) { # no cmdb set return; } if ( $klass !~ m/::/ ) { $klass = "Rex::CMDB::$klass"; } eval "use $klass"; if ($@) { die("CMDB provider ($klass) not found: $@"); } my $cmdb = $klass->new( %{$CMDB_PROVIDER} ); return Rex::Value->new( value => ( $cmdb->get( $item, $server ) || undef ) ); } sub cmdb_active { return ( $CMDB_PROVIDER ? 1 : 0 ); } 1; Args.pm100644001751001751 771613232707066 13621 0ustar00janjan000000000000Rex-1.6.0/lib/Rex# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Args; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use vars qw(%rex_opts); use Rex::Logger; use Data::Dumper; our $CLEANUP = 1; sub args_spec { return ( C => {}, c => {}, q => {}, Q => {}, F => {}, T => {}, h => {}, v => {}, d => {}, s => {}, m => {}, y => {}, w => {}, S => { type => "string" }, E => { type => "string" }, o => { type => "string" }, f => { type => "string" }, M => { type => "string" }, b => { type => "string" }, e => { type => "string" }, H => { type => "string" }, u => { type => "string" }, p => { type => "string" }, P => { type => "string" }, K => { type => "string" }, G => { type => "string" }, g => { type => "string" }, z => { type => "string" }, O => { type => "string" }, t => { type => "string" }, ); } sub parse_rex_opts { my ($class) = @_; my %args = $class->args_spec; #### clean up @ARGV my $runner = 0; for (@ARGV) { if ( /^\-[A-Za-z]+/ && length($_) > 2 && $CLEANUP ) { my @args = map { "-$_" } split( //, substr( $_, 1 ) ); splice( @ARGV, $runner, 1, @args ); } $runner++; } #### parse rex options my @params = @ARGV; for my $p (@params) { # shift off @ARGV my $shift = shift @ARGV; if ( length($p) >= 2 && substr( $p, 0, 1 ) eq "-" ) { my $name_param = substr( $p, 1, 2 ); # found a parameter if ( exists $args{$name_param} ) { Rex::Logger::debug("Option found: $name_param ($p)"); my $type = "Single"; if ( exists $args{$name_param}->{type} ) { $type = $args{$name_param}->{type}; Rex::Logger::debug(" is a $type"); shift @params; # remove the next parameter, because it must be an option if ( !exists $ARGV[0] || ( length( $ARGV[0] ) == 2 && exists $args{ substr( $ARGV[0], 1, 2 ) } && substr( $ARGV[0], 0, 1 ) eq "-" ) ) { # this is a typed parameter without an option! Rex::Logger::debug(" but there is no parameter"); Rex::Logger::debug( Dumper( \@params ) ); print("No parameter for $name_param\n"); CORE::exit 1; } } elsif ( exists $args{$name_param}->{func} ) { Rex::Logger::debug(" is a function - executing now"); $args{$name_param}->{func}->(); } my $c = "Rex::Args::\u$type"; eval "use $c"; if ($@) { die("No Argumentclass $type found!"); } if ( exists $rex_opts{$name_param} && $type eq "Single" ) { $rex_opts{$name_param}++; } else { # multiple params defined, create an array if ( exists $rex_opts{$name_param} ) { if ( !ref $rex_opts{$name_param} ) { $rex_opts{$name_param} = [ $rex_opts{$name_param} ]; } push @{ $rex_opts{$name_param} }, $c->get; } else { $rex_opts{$name_param} = $c->get; } } } else { Rex::Logger::debug("Option not known: $name_param ($p)"); next; } } else { # unshift the last parameter unshift @ARGV, $shift; last; } } } sub getopts { return %rex_opts; } sub is_opt { my ( $class, $opt ) = @_; if ( exists $rex_opts{$opt} ) { return $rex_opts{$opt}; } } sub get { my ($class) = @_; my $task = Rex::TaskList->create->current_task; if ($task) { return $task->get_opts(); } else { return _read_old_way(); } } sub _read_old_way { #### parse task options my %task_opts; for my $p (@ARGV) { my ( $key, $val ) = split( /=/, $p, 2 ); $key =~ s/^--//; if ( defined $val ) { $task_opts{$key} = $val; next; } $task_opts{$key} = 1; } return %task_opts; } 1; User.pm100644001751001751 131113232707066 13624 0ustar00janjan000000000000Rex-1.6.0/lib/Rex# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::User; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Rex::Commands::Gather; use Rex::Logger; sub get { my $user_o = "Linux"; if (is_freebsd) { $user_o = "FreeBSD"; } elsif (is_netbsd) { $user_o = "NetBSD"; } elsif (is_openbsd) { $user_o = "OpenBSD"; } elsif ( operating_system_is("SunOS") ) { $user_o = "SunOS"; } elsif (is_openwrt) { $user_o = "OpenWrt"; } my $class = "Rex::User::" . $user_o; eval "use $class"; if ($@) { Rex::Logger::info("OS not supported"); die("OS not supported"); } return $class->new; } 1; Hook.pm100644001751001751 156713232707066 13623 0ustar00janjan000000000000Rex-1.6.0/lib/Rex# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Hook; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION require Exporter; use base qw(Exporter); use vars qw(@EXPORT); @EXPORT = qw(register_function_hooks); my $__hooks = {}; sub register_function_hooks { my ($hooks) = @_; for my $state ( keys %{$hooks} ) { for my $func ( keys %{ $hooks->{$state} } ) { if ( !exists $__hooks->{$state}->{$func} ) { $__hooks->{$state}->{$func} = []; } push @{ $__hooks->{$state}->{$func} }, $hooks->{$state}->{$func}; } } } sub run_hook { my ( $command, $state, @args ) = @_; if ( !exists $__hooks->{$state}->{$command} ) { return; } my $func_arr = $__hooks->{$state}->{$command}; for my $func ( @{$func_arr} ) { @args = $func->(@args); } return @args; } 1; Task.pm100644001751001751 5135713232707066 13647 0ustar00janjan000000000000Rex-1.6.0/lib/Rex# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: =head1 NAME Rex::Task - The Task Object =head1 DESCRIPTION The Task Object. Typically you only need this class if you want to manipulate tasks after their initial creation. =head1 SYNOPSIS use Rex::Task; # create a new task my $task = Rex::Task->new(name => "testtask"); $task->set_server("remoteserver"); $task->set_code(sub { say "Hello"; }); $task->modify("no_ssh", 1); # retrieve an existing task use Rex::TaskList; my $existing_task = Rex::TaskList->get_task('my_task'); =head1 METHODS =cut package Rex::Task; use strict; use warnings; use Data::Dumper; use Time::HiRes qw(time); our $VERSION = '1.6.0'; # VERSION use Rex::Logger; use Rex::TaskList; use Rex::Interface::Connection; use Rex::Interface::Executor; use Rex::Group::Entry::Server; use Rex::Profiler; use Rex::Hardware; use Rex::Interface::Cache; use Rex::Report; use Rex::Helper::Run; use Rex::Helper::Path; use Rex::Notify; use Carp; require Rex::Commands; require Rex::Args; =head2 new This is the constructor. $task = Rex::Task->new( func => sub { some_code_here }, server => [ @server ], desc => $description, no_ssh => $no_ssh, hidden => $hidden, auth => { user => $user, password => $password, private_key => $private_key, public_key => $public_key, }, before => [sub {}, sub {}, ...], after => [sub {}, sub {}, ...], around => [sub {}, sub {}, ...], before_task_start => [sub {}, sub {}, ...], after_task_finished => [sub {}, sub {}, ...], name => $task_name, executor => Rex::Interface::Executor->create, opts => {key1 => val1, key2 => val2, ...}, args => [arg1, arg2, ...], ); =cut sub new { my $that = shift; my $proto = ref($that) || $that; my $self = {@_}; bless( $self, $proto ); if ( !exists $self->{name} ) { die("You have to define a task name."); } $self->{no_ssh} ||= 0; $self->{func} ||= sub { }; $self->{executor} ||= Rex::Interface::Executor->create; $self->{opts} ||= {}; $self->{args} ||= []; $self->{connection} = undef; # set to true as default if ( !exists $self->{exit_on_connect_fail} ) { $self->{exit_on_connect_fail} = 1; } return $self; } =head2 connection Returns the current connection object. =cut sub connection { my ($self) = @_; if ( !exists $self->{connection} || !$self->{connection} ) { $self->{connection} = Rex::Interface::Connection->create( $self->get_connection_type ); } $self->{connection}; } sub set_connection { my ( $self, $conn ) = @_; $self->{connection} = $conn; } =head2 executor Returns the current executor object. =cut sub executor { my ($self) = @_; $self->{executor}->set_task($self); return $self->{executor}; } =head2 hidden Returns true if the task is hidden. (Should not be displayed on ,,rex -T''.) =cut sub hidden { my ($self) = @_; return $self->{hidden}; } =head2 server Returns the servers on which the task should be executed as an ArrayRef. =cut sub server { my ($self) = @_; my @server = @{ $self->{server} }; my @ret = (); if ( ref( $server[-1] ) eq "HASH" ) { Rex::deprecated( undef, "0.40", "Defining extra credentials within the task creation is deprecated.", "Please use set auth => task => 'taskname' instead." ); # use extra defined credentials my $data = pop(@server); $self->set_auth( "user", $data->{'user'} ); $self->set_auth( "password", $data->{'password'} ); if ( exists $data->{"private_key"} ) { $self->set_auth( "private_key", $data->{"private_key"} ); $self->set_auth( "public_key", $data->{"public_key"} ); } } if ( ref( $self->{server} ) eq "ARRAY" && scalar( @{ $self->{server} } ) > 0 ) { for my $srv ( @{ $self->{server} } ) { if ( ref($srv) eq "CODE" ) { push( @ret, &$srv() ); } else { if ( ref $srv && $srv->isa("Rex::Group::Entry::Server") ) { push( @ret, $srv->get_servers ); } else { push( @ret, $srv ); } } } } elsif ( ref( $self->{server} ) eq "CODE" ) { push( @ret, &{ $self->{server} }() ); } else { push( @ret, Rex::Group::Entry::Server->new( name => "" ) ); } return [@ret]; } =head2 set_server(@server) With this method you can set new servers on which the task should be executed on. =cut sub set_server { my ( $self, @server ) = @_; $self->{server} = \@server; } =head2 delete_server Delete every server registered to the task. =cut sub delete_server { my ($self) = @_; delete $self->{current_server}; delete $self->{server}; $self->rethink_connection; } =head2 current_server Returns the current server on which the tasks gets executed right now. =cut sub current_server { my ($self) = @_; return $self->{current_server} || Rex::Group::Entry::Server->new( name => "" ); } =head2 desc Returns the description of a task. =cut sub desc { my ($self) = @_; return $self->{desc}; } =head2 set_desc($description) Set the description of a task. =cut sub set_desc { my ( $self, $desc ) = @_; $self->{desc} = $desc; } =head2 is_remote Returns true (1) if the task will be executed remotely. =cut sub is_remote { my ($self) = @_; if ( exists $self->{current_server} ) { if ( $self->{current_server} ne '' ) { return 1; } } else { if ( exists $self->{server} && scalar( @{ $self->{server} } ) > 0 ) { return 1; } } return 0; } =head2 is_local Returns true (1) if the task gets executed on the local host. =cut sub is_local { my ($self) = @_; return $self->is_remote() == 0 ? 1 : 0; } =head2 is_http Returns true (1) if the task gets executed over http protocol. =cut sub is_http { my ($self) = @_; return ( $self->{"connection_type"} && lc( $self->{"connection_type"} ) eq "http" ); } =head2 is_https Returns true (1) if the task gets executed over https protocol. =cut sub is_https { my ($self) = @_; return ( $self->{"connection_type"} && lc( $self->{"connection_type"} ) eq "https" ); } =head2 is_openssh Returns true (1) if the task gets executed with openssh. =cut sub is_openssh { my ($self) = @_; return ( $self->{"connection_type"} && lc( $self->{"connection_type"} ) eq "openssh" ); } =head2 want_connect Returns true (1) if the task will establish a connection to a remote system. =cut sub want_connect { my ($self) = @_; return $self->{no_ssh} == 0 ? 1 : 0; } =head2 get_connection_type This method tries to guess the right connection type for the task and returns it. Current return values are below: =over 4 =item * SSH: connect to the remote server using Net::SSH2 =item * OpenSSH: connect to the remote server using Net::OpenSSH =item * Local: runs locally (without any connections) =item * HTTP: uses experimental HTTP connection =item * HTTPS: uses experimental HTTPS connection =item * Fake: populate the connection properties, but do not connect So you can use this type to iterate over a list of remote hosts, but don't let rex build a connection. For example if you want to use Sys::Virt or other modules. =back =cut sub get_connection_type { my ($self) = @_; if ( $self->is_http ) { return "HTTP"; } elsif ( $self->is_https ) { return "HTTPS"; } elsif ( $self->is_remote && $self->is_openssh && $self->want_connect ) { return "OpenSSH"; } elsif ( $self->is_remote && $self->want_connect ) { return Rex::Config->get_connection_type(); } elsif ( $self->is_remote ) { return "Fake"; } else { return "Local"; } } =head2 modify($key, $value) With this method you can modify values of the task. =cut sub modify { my ( $self, $key, $value ) = @_; if ( ref( $self->{$key} ) eq "ARRAY" ) { push( @{ $self->{$key} }, $value ); } else { $self->{$key} = $value; } $self->rethink_connection; } =head2 rethink_connection Deletes current connection object. =cut sub rethink_connection { my ($self) = @_; delete $self->{connection}; } =head2 user Returns the username the task will use. =cut sub user { my ($self) = @_; if ( exists $self->{auth} && $self->{auth}->{user} ) { return $self->{auth}->{user}; } } =head2 set_user($user) Set the username of a task. =cut sub set_user { my ( $self, $user ) = @_; $self->{auth}->{user} = $user; } =head2 password Returns the password that will be used. =cut sub password { my ($self) = @_; if ( exists $self->{auth} && $self->{auth}->{password} ) { return $self->{auth}->{password}; } } =head2 set_password($password) Set the password of the task. =cut sub set_password { my ( $self, $password ) = @_; $self->{auth}->{password} = $password; } =head2 name Returns the name of the task. =cut sub name { my ($self) = @_; return $self->{name}; } =head2 code Returns the code of the task. =cut sub code { my ($self) = @_; return $self->{func}; } =head2 set_code(\&code_ref) Set the code of the task. =cut sub set_code { my ( $self, $code ) = @_; $self->{func} = $code; } =head2 run_hook($server, $hook) This method is used internally to execute the specified hooks. =cut sub run_hook { my ( $self, $server, $hook, @more_args ) = @_; my $old_server; for my $code ( @{ $self->{$hook} } ) { if ( $hook eq "after" ) { # special case for after hooks $code->( $$server, ( $self->{"__was_authenticated"} || 0 ), { $self->get_opts }, @more_args ); } else { $old_server = $$server if $server; $code->( $$server, $server, { $self->get_opts }, @more_args ); if ( $old_server && $old_server ne $$server ) { $self->{current_server} = $$server; } } } } =head2 set_auth($key, $value) Set the authentication of the task. $task->set_auth("user", "foo"); $task->set_auth("password", "bar"); =cut sub set_auth { my ( $self, $key, $value ) = @_; if ( scalar(@_) > 3 ) { my $_d = shift; $self->{auth} = {@_}; } else { $self->{auth}->{$key} = $value; } } =head2 merge_auth($server) Merges the authentication information from $server into the task. Tasks authentication information have precedence. =cut sub merge_auth { my ( $self, $server ) = @_; # merge auth hashs # task auth as precedence my %auth = $server->merge_auth( $self->{auth} ); return \%auth; } =head2 get_sudo_password Returns the sudo password. =cut sub get_sudo_password { my ($self) = @_; my $server = $self->connection->server; my %auth = $server->merge_auth( $self->{auth} ); return $auth{sudo_password}; } =head2 parallelism Get the parallelism count of a task. =cut sub parallelism { my ($self) = @_; return $self->{parallelism}; } =head2 set_parallelism($count) Set the parallelism of the task. =cut sub set_parallelism { my ( $self, $para ) = @_; $self->{parallelism} = $para; } =head2 connect($server) Initiate the connection to $server. =cut sub connect { my ( $self, $server, %override ) = @_; if ( !ref $server ) { $server = Rex::Group::Entry::Server->new( name => $server ); } $self->{current_server} = $server; $self->run_hook( \$server, "before" ); # need to be called, in case of a run_task task call. # see #788 $self->rethink_connection; my $user = $self->user; #print Dumper($self); my $auth = $self->merge_auth($server); if ( exists $override{auth} ) { $auth = $override{auth}; $user = $auth->{user}; } my $rex_int_conf = Rex::Commands::get("rex_internals"); Rex::Logger::debug( Dumper($rex_int_conf) ); Rex::Logger::debug("Auth-Information inside Task:"); for my $key ( keys %{$auth} ) { my $data = $auth->{$key}; $data = Rex::Logger::masq( "%s", $data ) if $key eq 'password'; $data = Rex::Logger::masq( "%s", $data ) if $key eq 'sudo_password'; $data ||= ""; Rex::Logger::debug("$key => [[$data]]"); } $auth->{public_key} = resolv_path( $auth->{public_key}, 1 ) if ( $auth->{public_key} ); $auth->{private_key} = resolv_path( $auth->{private_key}, 1 ) if ( $auth->{private_key} ); my $profiler = Rex::Profiler->new; # task specific auth rules over all my %connect_hash = %{$auth}; $connect_hash{server} = $server; # need to get rid of this Rex::push_connection( { conn => $self->connection, ssh => $self->connection->get_connection_object, server => $server, cache => Rex::Interface::Cache->create(), task => [], profiler => $profiler, reporter => Rex::Report->create( Rex::Config->get_report_type ), notify => Rex::Notify->new(), } ); push @{ Rex::get_current_connection()->{task} }, $self; $profiler->start("connect"); eval { $self->connection->connect(%connect_hash); 1; } or do { if ( !defined Rex::Config->get_fallback_auth ) { croak $@; } }; $profiler->end("connect"); if ( !$self->connection->is_connected ) { Rex::pop_connection(); croak("Couldn't connect to $server."); } elsif ( !$self->connection->is_authenticated ) { Rex::pop_connection(); my $message = "Couldn't authenticate against $server. It may be caused by one or more of:\n"; $message .= " - wrong username, password, key or passphrase\n"; $message .= " - changed remote host key\n"; $message .= " - root is not permitted to login over SSH\n" if ( $connect_hash{user} eq 'root' ); if ( !exists $override{auth} ) { my $fallback_auth = Rex::Config->get_fallback_auth; if ( ref $fallback_auth eq "ARRAY" ) { my $ret_eval; for my $fallback_a ( @{$fallback_auth} ) { $ret_eval = eval { $self->connect( $server, auth => $fallback_a ); }; } return $ret_eval if $ret_eval; } } croak($message); } else { Rex::Logger::debug("Successfully authenticated on $server.") if ( $self->connection->get_connection_type ne "Local" ); $self->{"__was_authenticated"} = 1; } $self->run_hook( \$server, "around" ); return 1; } =head2 disconnect Disconnect from the current connection. =cut sub disconnect { my ( $self, $server ) = @_; $self->run_hook( \$server, "around", 1 ); $self->connection->disconnect; my %args = Rex::Args->getopts; if ( defined $args{'d'} && $args{'d'} > 2 ) { Rex::Commands::profiler()->report; } delete $self->{connection}; pop @{ Rex::get_current_connection()->{task} }; # need to get rid of this Rex::pop_connection(); $self->run_hook( \$server, "after" ); } =head2 get_data Dump task data. =cut sub get_data { my ($self) = @_; return { func => $self->{func}, server => $self->{server}, desc => $self->{desc}, no_ssh => $self->{no_ssh}, hidden => $self->{hidden}, auth => $self->{auth}, before => $self->{before}, after => $self->{after}, around => $self->{around}, name => $self->{name}, executor => $self->{executor}, connection_type => $self->{connection_type}, opts => $self->{opts}, args => $self->{args}, }; } =head2 run($server, %options) Run the task on C<$server>, with C<%options>. =cut sub run { return pre_40_run(@_) unless ref $_[0]; my ( $self, $server, %options ) = @_; $options{opts} ||= { $self->get_opts }; $options{args} ||= [ $self->get_args ]; $options{params} ||= $options{opts}; if ( !ref $server ) { $server = Rex::Group::Entry::Server->new( name => $server ); } if ( !$_[1] ) { # run is called without any server. # so just connect to any servers. return Rex::TaskList->create()->run( $self, %options ); } # this is a method call # so run the task # TODO: refactor complete task calling # direct call with function and normal task call my ( $in_transaction, $start_time ); $start_time = time; if ( $server ne "" ) { # this is _not_ a task call via function syntax. $in_transaction = $options{in_transaction}; eval { $self->connect($server) }; if ($@) { my $error = $@; $self->{"__was_authenticated"} = 0; $self->run_hook( \$server, "after" ); die $error; } if ( Rex::Args->is_opt("c") ) { # get and cache all os info if ( !Rex::get_cache()->load() ) { Rex::Logger::debug("No cache found, need to collect new data."); $server->gather_information; } } if ( !$server->test_perl ) { Rex::Logger::info( "There is no perl interpreter found on this system. " . "Some commands may not work. Sudo won't work.", "warn" ); sleep 3; } } else { # we need to push the connection information of the last task onto this task object # if we don't do this, the task doesn't have any information of the current connection when called like a function. # See: #1091 $self->set_connection( Rex::get_current_connection()->{task}->[-1]->connection ) if Rex::get_current_connection()->{task}->[-1]; push @{ Rex::get_current_connection()->{task} }, $self; } # execute code my @ret; my $wantarray = wantarray; eval { $self->set_opts( %{ $options{params} } ) if ref $options{params} eq "HASH"; if ($wantarray) { @ret = $self->executor->exec( $options{params}, $options{args} ); } else { $ret[0] = $self->executor->exec( $options{params}, $options{args} ); } my $notify = Rex::get_current_connection()->{notify}; $notify->run_postponed(); } or do { if ($@) { my $error = $@; Rex::get_current_connection()->{reporter} ->report_resource_failed( message => $error ); Rex::get_current_connection()->{reporter}->report_task_execution( failed => 1, start_time => $start_time, end_time => time, message => $error, ); Rex::get_current_connection()->{reporter}->write_report(); pop @{ Rex::get_current_connection()->{task} }; die($error); } }; if ( $server ne "" ) { if ( Rex::Args->is_opt("c") ) { # get and cache all os info Rex::get_cache()->save(); } Rex::get_current_connection()->{reporter}->report_task_execution( failed => 0, start_time => $start_time, end_time => time, ); Rex::get_current_connection()->{reporter}->write_report(); if ($in_transaction) { $self->run_hook( \$server, "around", 1 ); $self->run_hook( \$server, "after" ); } else { $self->disconnect($server); } } else { pop @{ Rex::get_current_connection()->{task} }; } if ($wantarray) { return @ret; } else { return $ret[0]; } } sub pre_40_run { my ( $class, $task_name, $server_overwrite, $params ) = @_; # static calls to this method are deprecated Rex::deprecated( "Rex::Task->run()", "0.40" ); my $tasklist = Rex::TaskList->create; my $task = $tasklist->get_task($task_name); $task->set_server($server_overwrite) if $server_overwrite; $tasklist->run( $task, params => $params ); } =head2 modify_task($task, $key => $value) Modify C<$task>, by setting C<$key> to C<$value>. =cut sub modify_task { my $class = shift; my $task = shift; my $key = shift; my $value = shift; Rex::TaskList->create()->get_task($task)->modify( $key => $value ); } =head2 is_task Returns true(1) if the passed object is a task. =cut sub is_task { my ( $class, $task ) = @_; return Rex::TaskList->create()->is_task($task); } =head2 get_tasks Returns list of tasks. =cut sub get_tasks { my ( $class, @tmp ) = @_; return Rex::TaskList->create()->get_tasks(@tmp); } =head2 get_desc Returns description of task. =cut sub get_desc { my ( $class, @tmp ) = @_; return Rex::TaskList->create()->get_desc(@tmp); } =head2 exit_on_connect_fail Returns true if rex should exit on connect failure. =cut sub exit_on_connect_fail { my ($self) = @_; return $self->{exit_on_connect_fail}; } =head2 set_exit_on_connect_fail Sets if rex should exit on connect failure. =cut sub set_exit_on_connect_fail { my ( $self, $exit ) = @_; $self->{exit_on_connect_fail} = $exit; } =head2 get_args Returns arguments of task. =cut sub get_args { my ($self) = @_; @{ $self->{args} || [] }; } =head2 get_opts Returns options of task. =cut sub get_opts { my ($self) = @_; %{ $self->{opts} || {} }; } =head2 set_args Sets arguments for task. =cut sub set_args { my ( $self, @args ) = @_; $self->{args} = \@args; } =head2 set_opt Sets an option for task. =cut sub set_opt { my ( $self, $key, $value ) = @_; $self->{opts}->{$key} = $value; } =head2 set_opts Sets options for task. =cut sub set_opts { my ( $self, %opts ) = @_; $self->{opts} = \%opts; } =head2 clone Clones a task. =cut sub clone { my $self = shift; return Rex::Task->new( %{ $self->get_data } ); } 1; ip.out_centos7100644001751001751 136313232707066 14121 0ustar00janjan000000000000Rex-1.6.0/t1: lo: mtu 65536 qdisc noqueue state UNKNOWN link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: eth0: mtu 1500 qdisc pfifo_fast state UP qlen 1000 link/ether 00:1c:42:fe:5a:b5 brd ff:ff:ff:ff:ff:ff inet 10.211.55.171/24 brd 10.211.55.255 scope global dynamic eth0 valid_lft 1783sec preferred_lft 1783sec inet6 fec0::fea9:21c:42ff:fefe:5ab5/64 scope site dynamic valid_lft 2591627sec preferred_lft 2591627sec inet6 fe80::21c:42ff:fefe:5ab5/64 scope link valid_lft forever preferred_lft forever read_buffers.t100644001751001751 156613232707066 14137 0ustar00janjan000000000000Rex-1.6.0/tuse strict; use warnings; use Test::More; if ( $^O =~ m/^MSWin/i ) { plan tests => 1; } else { plan tests => 2; } use Rex::Interface::Exec; my $tty = $^O =~ m/^MSWin/i ? 0 : 1; Rex::Config->set_no_tty($tty); my $exec = Rex::Interface::Exec->create; my $command = ( $^O =~ m/^MSWin/i && Rex::is_local() ) ? qq{perl -e "my \$count = 500_000; while ( \$count-- ) { if ( \$count % 2) { print STDERR 'x'} else { print STDOUT 'x'} }"} : qq{perl -e 'my \$count = 500_000; while ( \$count-- ) { if ( \$count % 2) { print STDERR "x"} else { print STDOUT "x"} }'}; alarm 30; $SIG{ALRM} = sub { BAIL_OUT 'Reading from buffer timed out'; }; my ( $out, $err ) = $exec->exec($command); alarm 0; if ( $^O =~ m/^MSWin/i ) { is length($out), 500_000, 'output length on Windows'; } else { is length($out), 250_000, 'STDOUT length'; is length($err), 250_000, 'STDERR length'; } param_lookup.t100644001751001751 150013232707066 14165 0ustar00janjan000000000000Rex-1.6.0/t package main; use Test::More tests => 8; use Rex -base; use Rex::Resource; $::QUIET = 1; task( "test1", sub { my $x = param_lookup( "name", "foo" ); my $tp = template( \'<%= $name %>' ); is( $x, "foo", "got default value" ); is( $tp, "foo", "got default value in template" ); } ); task( "test2", sub { my $x = param_lookup( "name", "foo" ); my $tp = template( \'<%= $name %>' ); is( $x, "rex", "got parameter value" ); is( $tp, "rex", "got parameter value in template" ); } ); task( "test3", sub { test1(); my $x = param_lookup( "name", "foo" ); my $tp = template( \'<%= $name %>' ); is( $x, "xer", "got parameter value" ); is( $tp, "xer", "got parameter value in template" ); } ); test1(); test2( { name => "rex" } ); test3( { name => "xer" } ); Cloud.pm100644001751001751 177613232707066 13773 0ustar00janjan000000000000Rex-1.6.0/lib/Rex# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Cloud; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION require Exporter; use base qw(Exporter); use vars qw(@EXPORT); use Rex::Logger; @EXPORT = qw(get_cloud_service); my %CLOUD_SERVICE; sub register_cloud_service { my ( $class, $service_name, $service_class ) = @_; $CLOUD_SERVICE{"\L$service_name"} = $service_class; return 1; } sub register_cloud_provider { my $class = shift; $class->register_cloud_service(@_); } sub get_cloud_service { my ($service) = @_; if ( exists $CLOUD_SERVICE{"\L$service"} ) { eval "use " . $CLOUD_SERVICE{"\L$service"}; my $mod = $CLOUD_SERVICE{"\L$service"}; return $mod->new; } else { eval "use Rex::Cloud::$service"; if ($@) { Rex::Logger::info("Cloud Service $service not supported."); Rex::Logger::info($@); return 0; } my $mod = "Rex::Cloud::$service"; return $mod->new; } } 1; Batch.pm100644001751001751 206713232707066 13740 0ustar00janjan000000000000Rex-1.6.0/lib/Rex# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Batch; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Rex::Logger; use Rex::TaskList; use vars qw(%batchs); sub create_batch { my $class = shift; my $batch_name = shift; my $batch_desc = pop; my @task_names = @_; my $task_list = Rex::TaskList->create; for my $task_name (@task_names) { die "ERROR: no task: $task_name" unless $task_list->is_task($task_name); } $batchs{$batch_name} = { desc => $batch_desc, task_names => \@task_names }; } sub get_batch { my $class = shift; my $batch_name = shift; return @{ $batchs{$batch_name}->{'task_names'} }; } sub get_desc { my $class = shift; my $batch_name = shift; return $batchs{$batch_name}->{'desc'}; } sub get_batchs { my $class = shift; my @a = sort { $a cmp $b } keys %batchs; } sub is_batch { my $class = shift; my $batch_name = shift; if ( defined $batchs{$batch_name} ) { return 1; } return 0; } 1; Group.pm100644001751001751 535213232707066 14013 0ustar00janjan000000000000Rex-1.6.0/lib/Rex# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Group; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Rex::Logger; use attributes; use Rex::Group::Entry::Server; use vars qw(%groups); use List::MoreUtils qw(uniq); use Data::Dumper; sub new { my $that = shift; my $proto = ref($that) || $that; my $self = {@_}; bless( $self, $proto ); for my $srv ( @{ $self->{servers} } ) { $srv->append_to_group( $self->{name} ); } return $self; } sub get_servers { my ($self) = @_; my @servers = map { ref( $_->to_s ) eq "CODE" ? &{ $_->to_s } : $_ } @{ $self->{servers} }; return uniq @servers; } sub set_auth { my ( $self, %data ) = @_; $self->{auth} = \%data; map { $_->set_auth( %{ $self->get_auth } ) } $self->get_servers; } sub get_auth { my ($self) = @_; return $self->{auth}; } ################################################################################ # STATIC FUNCTIONS ################################################################################ # Creates a new server group # Possible calls: # create_group(name => "g1", "srv1", "srv2"); # create_group(name => "g1", Rex::Group::Entry::Server->new(name => "srv1"), "srv2"); # create_group(name => "g1", "srv1" => { user => "other" }, "srv2"); sub create_group { my $class = shift; my $group_name = shift; my @server = uniq grep { defined } @_; my @server_obj; for ( my $i = 0 ; $i <= $#server ; $i++ ) { next if ref $server[$i] eq 'HASH'; # already processed by previous loop # if argument is already a Rex::Group::Entry::Server if ( ref $server[$i] && $server[$i]->isa("Rex::Group::Entry::Server") ) { push @server_obj, $server[$i]; next; } # if next argument is a HashRef, use it as options for the server my %options = ( $i < $#server and ref $server[ $i + 1 ] eq 'HASH' ) ? %{ $server[ $i + 1 ] } : (); my $obj = Rex::Group::Entry::Server->new( name => $server[$i], %options ); push @server_obj, $obj; } $groups{$group_name} = Rex::Group->new( servers => \@server_obj, name => $group_name ); } # returns the servers in the group sub get_group { my $class = shift; my $group_name = shift; if ( exists $groups{$group_name} ) { return $groups{$group_name}->get_servers; } return (); } sub is_group { my $class = shift; my $group_name = shift; if ( defined $groups{$group_name} ) { return 1; } return 0; } sub get_groups { my $class = shift; my %ret = (); for my $key ( keys %groups ) { $ret{$key} = [ $groups{$key}->get_servers ]; } return %ret; } sub get_group_object { my $class = shift; my $name = shift; return $groups{$name}; } 1; Value.pm100644001751001751 65713232707066 13756 0ustar00janjan000000000000Rex-1.6.0/lib/Rex# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: # # this is a simple helper class for the get() function package Rex::Value; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION sub new { my $that = shift; my $proto = ref($that) || $that; my $self = {@_}; bless( $self, $proto ); return $self; } sub value { my ($self) = @_; return $self->{value}; } 1; network_linux.t100644001751001751 1004213232707066 14425 0ustar00janjan000000000000Rex-1.6.0/tuse Test::More tests => 44; use Rex::Hardware::Network::Linux; use Rex::Helper::Hash; my @in = eval { local (@ARGV) = ("t/ifconfig.out1"); <>; }; my $info = Rex::Hardware::Network::Linux::_parse_ifconfig(@in); is( $info->{eth0}->{broadcast}, "10.18.1.255", "ex1 / broadcast" ); is( $info->{eth0}->{ip}, "10.18.1.107", "ex1 / ip" ); is( $info->{eth0}->{netmask}, "255.255.255.0", "ex1 / netmask" ); is( $info->{eth0}->{mac}, "00:16:3e:7f:fc:3a", "ex1 / mac" ); my $f = {}; hash_flatten( $info, $f, "_" ); is( $f->{eth0_mac}, "00:16:3e:7f:fc:3a", "ex1 / flatten / mac" ); is( $f->{eth0_ip}, "10.18.1.107", "ex1 / flatten / ip" ); is( $f->{eth0_netmask}, "255.255.255.0", "ex1 / flatten / netmask" ); is( $f->{eth0_broadcast}, "10.18.1.255", "ex1 / flatten / broadcast" ); @in = eval { local (@ARGV) = ("t/ifconfig.out2"); <>; }; $info = Rex::Hardware::Network::Linux::_parse_ifconfig(@in); ok( !$info->{"vif1.0"}->{broadcast}, "ex2 / broadcast" ); ok( !$info->{"vif1.0"}->{ip}, "ex2 / ip" ); ok( !$info->{"vif1.0"}->{netmask}, "ex2 / netmask" ); is( $info->{"vif1.0"}->{mac}, "fe:ff:ff:ff:ff:ff", "ex2 / mac" ); $f = {}; hash_flatten( $info, $f, "_" ); is( $f->{"vif1_0_mac"}, "fe:ff:ff:ff:ff:ff", "ex2 / flatten / mac" ); ok( !$f->{"vif1_0_ip"}, "ex2 / flatten / ip" ); ok( !$f->{"vif1_0_netmask"}, "ex2 / flatten / netmask" ); ok( !$f->{"vif1_0_broadcast"}, "ex2 / flatten / broadcast" ); @in = eval { local (@ARGV) = ("t/ip.out1"); <>; }; $info = Rex::Hardware::Network::Linux::_parse_ip(@in); is( $info->{wlp2s0}->{ip}, "10.20.30.40", "ip / ip" ); is( $info->{wlp2s0}->{netmask}, "255.255.255.0", "ip / netmask" ); is( $info->{wlp2s0}->{broadcast}, "10.20.30.255", "ip / broadcast" ); is( $info->{wlp2s0}->{mac}, "aa:bb:cc:dd:ee:ff", "ip / mac" ); $f = {}; hash_flatten( $info, $f, "_" ); is( $f->{"wlp2s0_mac"}, "aa:bb:cc:dd:ee:ff", "ip / flatten / mac" ); is( $f->{"wlp2s0_ip"}, "10.20.30.40", "ip / flatten / ip" ); is( $f->{"wlp2s0_netmask"}, "255.255.255.0", "ip / flatten / netmask" ); is( $f->{"wlp2s0_broadcast"}, "10.20.30.255", "ip / flatten / broadcast" ); $info = {}; @in = eval { local (@ARGV) = ("t/ip.out2"); <>; }; $info = Rex::Hardware::Network::Linux::_parse_ip(@in); is( $info->{eth1}->{ip}, "", "ip / ip" ); is( $info->{eth1}->{netmask}, "", "ip / netmask" ); is( $info->{eth1}->{broadcast}, "", "ip / broadcast" ); is( $info->{eth1}->{mac}, "00:1c:42:73:ad:3c", "ip / mac" ); @in = eval { local (@ARGV) = ("t/ifconfig.out6"); <>; }; $info = Rex::Hardware::Network::Linux::_parse_ifconfig(@in); is( $info->{eth0}->{broadcast}, "192.168.112.255", "(fc19) eth0 / broadcast" ); is( $info->{eth0}->{ip}, "192.168.112.182", "(fc19) eth0 / ip" ); is( $info->{eth0}->{netmask}, "255.255.255.0", "(fc19) eth0 / netmask" ); is( $info->{eth0}->{mac}, "52:54:00:37:a8:e1", "(fc19) eth0 / mac" ); is( $info->{"eth0:1"}->{broadcast}, "1.2.255.255", "(fc19) eth0:1 / broadcast" ); is( $info->{"eth0:1"}->{ip}, "1.2.3.4", "(fc19) eth0:1 / ip" ); is( $info->{"eth0:1"}->{netmask}, "255.255.0.0", "(fc19) eth0:1 / netmask" ); is( $info->{"eth0:1"}->{mac}, "52:54:00:37:a8:e1", "(fc19) eth0:1 / mac" ); @in = eval { local (@ARGV) = ("t/ifconfig.out7"); <>; }; $info = Rex::Hardware::Network::Linux::_parse_ifconfig(@in); is( $info->{ppp0}->{ip}, "123.117.251.17", "ppp0 / ip" ); is( $info->{ppp0}->{netmask}, "255.255.255.255", "ppp0 / netmask" ); is( $info->{ppp0}->{broadcast}, "", "ppp0 / broadcast" ); is( $info->{ppp0}->{mac}, "", "ppp0 / mac" ); @in = eval { local (@ARGV) = ("t/ip.out3"); <>; }; $info = Rex::Hardware::Network::Linux::_parse_ip(@in); is( $info->{ppp0}->{ip}, "123.117.251.17", "ppp0 / ip" ); is( $info->{ppp0}->{netmask}, "255.255.255.255", "ppp0 / netmask" ); is( $info->{ppp0}->{broadcast}, "", "ppp0 / broadcast" ); is( $info->{ppp0}->{mac}, "", "ppp0 / mac" ); Config.pm100644001751001751 5206413232707066 14146 0ustar00janjan000000000000Rex-1.6.0/lib/Rex# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: =head1 NAME Rex::Config - Handles the configuration. =head1 DESCRIPTION This module holds all configuration parameters for Rex. With this module you can specify own configuration parameters for your modules. =head1 EXPORTED METHODS =cut package Rex::Config; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Rex::Helper::File::Spec; use Rex::Logger; use YAML; use Data::Dumper; use Rex::Require; our ( $user, $password, $port, $timeout, $max_connect_fails, $password_auth, $key_auth, $krb5_auth, $public_key, $private_key, $parallelism, $log_filename, $log_facility, $sudo_password, $ca_file, $ca_cert, $ca_key, $path, $no_path_cleanup, $set_param, $environment, $connection_type, $distributor, $template_function, $SET_HANDLER, $HOME_CONFIG, $HOME_CONFIG_YAML, %SSH_CONFIG_FOR, $sudo_without_locales, $sudo_without_sh, $no_tty, $source_global_profile, $source_profile, %executor_for, $allow_empty_groups, $use_server_auth, $tmp_dir, %openssh_opt, $use_cache, $cache_type, $use_sleep_hack, $report_type, $do_reporting, $say_format, $exec_autodie, $verbose_run, $disable_taskname_warning, $proxy_command, $task_call_by_method, $fallback_auth, $register_cmdb_template, $check_service_exists, $set_no_append, $use_net_openssh_if_present, $use_template_ng, $use_rex_kvm_agent, $autodie, $task_chaining_cmdline_args, ); # some defaults %executor_for = ( perl => "perl", python => "python", ruby => "ruby", bash => "bash", ); sub set_autodie { my $class = shift; $autodie = shift; } sub get_autodie { return $autodie; } sub set_use_net_openssh_if_present { my $class = shift; $use_net_openssh_if_present = shift; } sub get_use_net_openssh_if_present { return $use_net_openssh_if_present; } sub set_use_rex_kvm_agent { my $class = shift; $use_rex_kvm_agent = shift; } sub get_use_rex_kvm_agent { return $use_rex_kvm_agent; } sub set_use_template_ng { my $class = shift; $use_template_ng = shift; } sub get_use_template_ng { return $use_template_ng; } sub set_set_no_append { my $class = shift; $set_no_append = shift; } sub get_set_no_append { return $set_no_append; } sub set_check_service_exists { my $class = shift; $check_service_exists = shift; } sub get_check_service_exists { return $check_service_exists; } sub set_register_cmdb_template { my $class = shift; $register_cmdb_template = shift; } sub get_register_cmdb_template { return $register_cmdb_template; } sub set_fallback_auth { my $class = shift; $fallback_auth = [@_]; } sub get_fallback_auth { return $fallback_auth; } sub set_task_call_by_method { my $class = shift; $task_call_by_method = shift; } sub get_task_call_by_method { return $task_call_by_method; } sub set_disable_taskname_warning { my $class = shift; $disable_taskname_warning = shift; } sub get_disable_taskname_warning { return $disable_taskname_warning; } sub set_task_chaining_cmdline_args { my $class = shift; $task_chaining_cmdline_args = shift; } sub get_task_chaining_cmdline_args { return $task_chaining_cmdline_args; } sub set_verbose_run { my $class = shift; $verbose_run = shift; } sub get_verbose_run { return $verbose_run; } sub set_exec_autodie { my $class = shift; $exec_autodie = shift; } sub get_exec_autodie { return $exec_autodie; } sub set_no_path_cleanup { my $class = shift; $no_path_cleanup = shift; } sub get_no_path_cleanup { return $no_path_cleanup; } sub set_source_profile { my $class = shift; $source_profile = shift; } sub get_source_profile { return $source_profile; } sub set_say_format { my $class = shift; $say_format = shift; } sub get_say_format { return $say_format; } sub set_do_reporting { my $class = shift; $do_reporting = shift; } sub get_do_reporting { return $do_reporting; } sub set_report_type { my $class = shift; $report_type = shift; } sub get_report_type { if ( exists $ENV{REX_REPORT_TYPE} ) { return $ENV{REX_REPORT_TYPE}; } return $report_type; } sub set_sleep_hack { my $class = shift; $use_sleep_hack = shift; } sub get_sleep_hack { return $use_sleep_hack; } sub set_cache_type { my $class = shift; $cache_type = shift; } sub get_cache_type { if ( exists $ENV{REX_CACHE_TYPE} ) { return $ENV{REX_CACHE_TYPE}; } return $cache_type || "Base"; } sub set_use_cache { my $class = shift; $use_cache = shift; } sub get_use_cache { return $use_cache; } sub get_sudo_without_locales { return $sudo_without_locales; } sub get_sudo_without_sh { return $sudo_without_sh; } sub set_openssh_opt { my ( $class, %opt ) = @_; for my $key ( keys %opt ) { if ( !defined $opt{$key} ) { $openssh_opt{$key} = undef; delete $openssh_opt{$key}; next; } $openssh_opt{$key} = $opt{$key}; } } sub get_openssh_opt { return %openssh_opt; } sub set_sudo_without_locales { my $class = shift; $sudo_without_locales = shift; } sub set_sudo_without_sh { my $class = shift; $sudo_without_sh = shift; } sub set_executor_for { my $class = shift; my $for = shift; my $e = shift; $executor_for{$for} = $e; } sub get_executor_for { my $class = shift; my $e = shift; return $executor_for{$e}; } sub set_tmp_dir { my ( $class, $dir ) = @_; if ( $class eq "Rex::Config" ) { $tmp_dir = $dir; } else { $tmp_dir = $class; } } sub get_tmp_dir { my $cache = Rex::get_cache(); if ( my $cached_tmp = $cache->get("tmpdir") ) { return $cached_tmp; } if ( !$tmp_dir ) { if ( my $ssh = Rex::is_ssh() ) { my $exec; if ( Rex::is_sudo() ) { if ( ref $ssh eq "Net::OpenSSH" ) { $exec = Rex::Interface::Exec->create("OpenSSH"); } else { $exec = Rex::Interface::Exec->create("SSH"); } } else { $exec = Rex::Interface::Exec->create; } my ($out) = $exec->exec("perl -MFile::Spec -le 'print File::Spec->tmpdir'"); if ( $? == 0 && $out ) { $out =~ s/[\r\n]//gms; $cache->set( "tmpdir", $out ); return $out; } $cache->set( "tmpdir", "/tmp" ); return "/tmp"; } else { $cache->set( "tmpdir", Rex::Helper::File::Spec->tmpdir ); return Rex::Helper::File::Spec->tmpdir; } } return $tmp_dir; } sub set_path { my $class = shift; $path = shift; } sub get_path { if ( !$path ) { return ( "/bin", "/sbin", "/usr/bin", "/usr/sbin", "/usr/local/bin", "/usr/local/sbin", "/usr/pkg/bin", "/usr/pkg/sbin" ); } return @{$path}; } sub set_user { my $class = shift; $user = shift; } sub set_password { my $class = shift; $password = shift; } sub set_port { my $class = shift; $port = shift; } sub set_sudo_password { my $class = shift; $sudo_password = shift; } sub set_source_global_profile { my $class = shift; $source_global_profile = shift; } sub get_source_global_profile { return $source_global_profile; } sub set_max_connect_fails { my $class = shift; $max_connect_fails = shift; } sub get_max_connect_fails { my $class = shift; my $param = {@_}; if ( exists $param->{server} && exists $SSH_CONFIG_FOR{ $param->{server} } && exists $SSH_CONFIG_FOR{ $param->{server} }->{connectionattempts} ) { return $SSH_CONFIG_FOR{ $param->{server} }->{connectionattempts}; } return $max_connect_fails || 3; } sub has_user { my $class = shift; return $user; } sub get_user { my $class = shift; if ( exists $ENV{REX_USER} ) { return $ENV{REX_USER}; } if ($user) { return $user; } if ( $^O =~ m/^MSWin/ ) { return getlogin; } else { return getpwuid($<); } } sub get_password { my $class = shift; if ( exists $ENV{REX_PASSWORD} ) { return $ENV{REX_PASSWORD}; } return $password; } sub get_port { my $class = shift; my $param = {@_}; if ( exists $param->{server} && exists $SSH_CONFIG_FOR{ $param->{server} } && exists $SSH_CONFIG_FOR{ $param->{server} }->{port} ) { return $SSH_CONFIG_FOR{ $param->{server} }->{port}; } return $port; } sub set_proxy_command { my $class = shift; $proxy_command = shift; } sub get_proxy_command { my $class = shift; my $param = {@_}; if ( exists $param->{server} && exists $SSH_CONFIG_FOR{ $param->{server} } && exists $SSH_CONFIG_FOR{ $param->{server} }->{proxycommand} ) { return $SSH_CONFIG_FOR{ $param->{server} }->{proxycommand}; } return $proxy_command; } sub get_sudo_password { my $class = shift; if ( exists $ENV{REX_SUDO_PASSWORD} ) { return $ENV{REX_SUDO_PASSWORD}; } if ($sudo_password) { return $sudo_password; } elsif ( !defined $sudo_password ) { return ""; } else { return $password; } return ""; } sub set_timeout { my $class = shift; $timeout = shift; } sub get_timeout { my $class = shift; my $param = {@_}; if ( exists $param->{server} && exists $SSH_CONFIG_FOR{ $param->{server} } && exists $SSH_CONFIG_FOR{ $param->{server} }->{connecttimeout} ) { return $SSH_CONFIG_FOR{ $param->{server} }->{connecttimeout}; } return $timeout || 2; } sub set_password_auth { my $class = shift; $key_auth = 0; $krb5_auth = 0; $password_auth = shift || 1; } sub set_key_auth { my $class = shift; $password_auth = 0; $krb5_auth = 0; $key_auth = shift || 1; } sub set_krb5_auth { my $class = shift; $password_auth = 0; $key_auth = 0; $krb5_auth = shift || 1; } sub get_password_auth { if ( exists $ENV{REX_AUTH_TYPE} && $ENV{REX_AUTH_TYPE} eq "pass" ) { return 1; } return $password_auth; } sub get_key_auth { if ( exists $ENV{REX_AUTH_TYPE} && $ENV{REX_AUTH_TYPE} eq "key" ) { return 1; } return $key_auth; } sub get_krb5_auth { if ( exists $ENV{REX_AUTH_TYPE} && $ENV{REX_AUTH_TYPE} eq "krb5" ) { return 1; } return $krb5_auth; } sub set_public_key { my $class = shift; $public_key = shift; } sub has_public_key { return get_public_key(); } sub get_public_key { if ( exists $ENV{REX_PUBLIC_KEY} ) { return $ENV{REX_PUBLIC_KEY}; } if ($public_key) { return $public_key; } return; } sub set_private_key { my $class = shift; $private_key = shift; } sub has_private_key { return get_private_key(); } sub get_private_key { if ( exists $ENV{REX_PRIVATE_KEY} ) { return $ENV{REX_PRIVATE_KEY}; } if ($private_key) { return $private_key; } return; } sub set_parallelism { my $class = shift; $parallelism = $_[0]; } sub get_parallelism { my $class = shift; return $parallelism || 1; } sub set_log_filename { my $class = shift; $log_filename = shift; } sub get_log_filename { my $class = shift; return $log_filename; } sub set_log_facility { my $class = shift; $log_facility = shift; } sub get_log_facility { my $class = shift; return $log_facility || "local0"; } sub set_environment { my ( $class, $env ) = @_; $environment = $env; } sub get_environment { return $environment || ""; } sub get_ssh_config_username { my $class = shift; my $param = {@_}; if ( exists $param->{server} && exists $SSH_CONFIG_FOR{ $param->{server} } && exists $SSH_CONFIG_FOR{ $param->{server} }->{user} ) { return $SSH_CONFIG_FOR{ $param->{server} }->{user}; } return 0; } sub get_ssh_config_hostname { my $class = shift; my $param = {@_}; if ( exists $param->{server} && exists $SSH_CONFIG_FOR{ $param->{server} } && exists $SSH_CONFIG_FOR{ $param->{server} }->{hostname} ) { if ( $SSH_CONFIG_FOR{ $param->{server} }->{hostname} =~ m/^\%h\.(.*)/ ) { return $param->{server} . "." . $1; } else { return $SSH_CONFIG_FOR{ $param->{server} }->{hostname}; } } return 0; } sub get_ssh_config_private_key { my $class = shift; my $param = {@_}; if ( exists $param->{server} && exists $SSH_CONFIG_FOR{ $param->{server} } && exists $SSH_CONFIG_FOR{ $param->{server} }->{identityfile} ) { my $file = $SSH_CONFIG_FOR{ $param->{server} }->{identityfile}; my $home_dir = _home_dir(); $file =~ s/^~/$home_dir/; return $file; } return 0; } sub get_ssh_config_public_key { my $class = shift; my $param = {@_}; if ( exists $param->{server} && exists $SSH_CONFIG_FOR{ $param->{server} } && exists $SSH_CONFIG_FOR{ $param->{server} }->{identityfile} ) { my $file = $SSH_CONFIG_FOR{ $param->{server} }->{identityfile} . ".pub"; my $home_dir = _home_dir(); $file =~ s/^~/$home_dir/; return $file; } return 0; } sub get_connection_type { my $class = shift; if ( $^O !~ m/^MSWin/ && !$connection_type && $use_net_openssh_if_present ) { my $has_net_openssh = 0; eval { Net::OpenSSH->require; Net::SFTP::Foreign->require; $has_net_openssh = 1; 1; }; if ($has_net_openssh) { Rex::Logger::debug( "Found Net::OpenSSH and Net::SFTP::Foreign - using it as default"); $connection_type = "OpenSSH"; return "OpenSSH"; } } if ( !$connection_type ) { my $has_net_ssh2 = 0; eval { Net::SSH2->require; $has_net_ssh2 = 1; 1; }; if ($has_net_ssh2) { $connection_type = "SSH"; return "SSH"; } } return $connection_type || "SSH"; } sub get_ca { my $class = shift; return $ca_file || ""; } sub get_ca_cert { my $class = shift; return $ca_cert || ""; } sub get_ca_key { my $class = shift; return $ca_key || ""; } sub set_distributor { my $class = shift; $distributor = shift; } sub get_distributor { my $class = shift; return $distributor || "Base"; } sub set_template_function { my $class = shift; ($template_function) = @_; } sub get_template_function { if ( ref($template_function) eq "CODE" ) { return sub { my ( $content, $template_vars ) = @_; $template_vars = { Rex::Commands::task()->get_opts, ( Rex::Resource->is_inside_resource ? %{ Rex::Resource->get_current_resource()->get_all_parameters } : () ), %{ $template_vars || {} } } if ( Rex::Commands::task() ); return $template_function->( $content, $template_vars ); }; } if ( Rex::Template::NG->is_loadable && get_use_template_ng() ) { # new template engine return sub { my ( $content, $template_vars ) = @_; $template_vars = { Rex::Commands::task()->get_opts, ( Rex::Resource->is_inside_resource ? %{ Rex::Resource->get_current_resource()->get_all_parameters } : () ), %{ $template_vars || {} } } if ( Rex::Commands::task() ); Rex::Template::NG->require; my $t = Rex::Template::NG->new; return $t->parse( $content, %{$template_vars} ); }; } return sub { my ( $content, $template_vars ) = @_; $template_vars = { Rex::Commands::task()->get_opts, ( Rex::Resource->is_inside_resource ? %{ Rex::Resource->get_current_resource()->get_all_parameters } : () ), %{ $template_vars || {} } } if ( Rex::Commands::task() ); use Rex::Template; my $template = Rex::Template->new; return $template->parse( $content, $template_vars ); }; } sub set_no_tty { shift; $no_tty = shift; } sub get_no_tty { return $no_tty; } =head2 register_set_handler($handler_name, $code) Register a handler that gets called by I. Rex::Config->register_set_handler("foo", sub { my ($value) = @_; print "The user set foo -> $value\n"; }); And now you can use this handler in your I like this: set foo => "bar"; =cut sub register_set_handler { my ( $class, $handler_name, $code ) = @_; $SET_HANDLER->{$handler_name} = $code; } sub set { my ( $class, $var, $data ) = @_; if ( exists( $SET_HANDLER->{$var} ) ) { shift; shift; return &{ $SET_HANDLER->{$var} }(@_); } if ($set_no_append) { $set_param->{$var} = $data; } else { if ( ref($data) eq "HASH" ) { if ( !ref( $set_param->{$var} ) ) { $set_param->{$var} = {}; } for my $key ( keys %{$data} ) { $set_param->{$var}->{$key} = $data->{$key}; } } elsif ( ref($data) eq "ARRAY" ) { push( @{ $set_param->{$var} }, @{$data} ); } else { $set_param->{$var} = $data; } } } sub unset { my ( $class, $var ) = @_; $set_param->{$var} = undef; delete $set_param->{$var}; } sub get { my ( $class, $var ) = @_; $var or return; if ( exists $set_param->{$var} ) { return $set_param->{$var}; } } sub get_all { my ($class) = @_; return $set_param; } =head2 register_config_handler($topic, $code) With this function it is possible to register own sections in the users config file ($HOME/.rex/config.yml). Example: Rex::Config->register_config_handler("foo", sub { my ($param) = @_; print "bar is: " . $param->{bar} . "\n"; }); And now the user can set this in his configuration file: base: user: theuser password: thepassw0rd foo: bar: baz =cut sub register_config_handler { my ( $class, $topic, $code ) = @_; if ( !ref($HOME_CONFIG) ) { $HOME_CONFIG = {}; } $HOME_CONFIG->{$topic} = $code; if ( ref($HOME_CONFIG_YAML) && exists $HOME_CONFIG_YAML->{$topic} ) { &$code( $HOME_CONFIG_YAML->{$topic} ); } } sub read_config_file { my ($config_file) = @_; $config_file ||= _home_dir() . "/.rex/config.yml"; if ( -f $config_file ) { my $yaml = eval { local ( @ARGV, $/ ) = ($config_file); <>; }; eval { $HOME_CONFIG_YAML = Load($yaml); }; if ($@) { print STDERR "Error loading $config_file\n"; print STDERR "$@\n"; exit 2; } for my $key ( keys %{$HOME_CONFIG} ) { if ( exists $HOME_CONFIG_YAML->{$key} ) { my $code = $HOME_CONFIG->{$key}; &$code( $HOME_CONFIG_YAML->{$key} ); } } } } sub read_ssh_config_file { my ($config_file) = @_; $config_file ||= _home_dir() . '/.ssh/config'; if ( -f $config_file ) { my @lines = eval { local (@ARGV) = ($config_file); <>; }; %SSH_CONFIG_FOR = _parse_ssh_config(@lines); } } sub _parse_ssh_config { my (@lines) = @_; my %ret = (); my ( @host, $in_host ); for my $line (@lines) { chomp $line; next if ( $line =~ m/^\s*#/ ); next if ( $line =~ m/^\s*$/ ); if ( $line =~ m/^Host(?:\s*=\s*|\s+)(.*)$/i ) { my $host_tmp = $1; @host = split( /\s+/, $host_tmp ); $in_host = 1; for my $h (@host) { $ret{$h} = {}; } next; } elsif ($in_host) { #my ($key, $val) = ($line =~ m/^\s*([^\s]+)\s+=?\s*(.*)$/); $line =~ s/^\s*//g; my ( $key, $val_tmp ) = split( /[\s=]/, $line, 2 ); $val_tmp =~ s/^[\s=]+//g; my $val = $val_tmp; $val =~ s/^\s+//; $val =~ s/\s+$//; for my $h (@host) { $ret{$h}->{ lc($key) } = $val; } } } return %ret; } sub set_allow_empty_groups { my ( $class, $set ) = @_; if ($set) { $allow_empty_groups = 1; } else { $allow_empty_groups = 0; } } sub get_allow_empty_groups { if ($allow_empty_groups) { return 1; } return 0; } sub set_use_server_auth { my ( $class, $set ) = @_; if ($set) { $use_server_auth = 1; } else { $use_server_auth = 0; } } sub get_use_server_auth { if ($use_server_auth) { return 1; } return 0; } sub import { read_ssh_config_file(); read_config_file(); } no strict 'refs'; __PACKAGE__->register_config_handler( base => sub { my ($param) = @_; for my $key ( keys %{$param} ) { if ( $key eq "keyauth" ) { $key_auth = $param->{keyauth}; next; } if ( $key eq "passwordauth" ) { $password_auth = $param->{passwordauth}; next; } if ( $key eq "passauth" ) { $password_auth = $param->{passauth}; next; } $$key = $param->{$key}; } } ); my @set_handler = qw/user password private_key public_key -keyauth -passwordauth -passauth parallelism sudo_password connection ca cert key distributor template_function port/; for my $hndl (@set_handler) { __PACKAGE__->register_set_handler( $hndl => sub { my ($val) = @_; if ( $hndl =~ m/^\-/ ) { $hndl = substr( $hndl, 1 ); } if ( $hndl eq "keyauth" ) { $hndl = "key_auth"; $val = 1; } if ( $hndl eq "passwordauth" || $hndl eq "passauth" ) { $hndl = "password_auth"; $val = 1; } if ( $hndl eq "connection" ) { $hndl = "connection_type"; } if ( $hndl eq "ca" ) { $hndl = "ca_file"; } if ( $hndl eq "cert" ) { $hndl = "ca_cert"; } if ( $hndl eq "key" ) { $hndl = "ca_key"; } $$hndl = $val; } ); } use strict; sub _home_dir { if ( $^O =~ m/^MSWin/ ) { return $ENV{'USERPROFILE'}; } return $ENV{'HOME'} || ""; } 1; Output.pm100644001751001751 224513232707066 14215 0ustar00janjan000000000000Rex-1.6.0/lib/Rex# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Output; use strict; use warnings; my $handle; use vars qw($output_object); BEGIN { IPC::Shareable->use; } END { IPC::Shareable->clean_up_all; } use base 'Rex::Output::Base'; our $VERSION = '1.6.0'; # VERSION sub get { my ( $class, $output_module ) = @_; return $output_object if ($output_object); return unless ($output_module); $handle = tie $output_object, 'IPC::Shareable', undef, { destroy => 1 } unless $handle; eval "use Rex::Output::$output_module;"; if ($@) { die("Output Module ,,$output_module'' not found."); } my $output_class = "Rex::Output::$output_module"; $output_object = $output_class->new; return $class; } sub _action { my ( $class, $action, @args ) = @_; return unless ( defined $output_object ); $handle->shlock(); $output_object->$action(@args); $handle->shunlock(); } sub add { my $class = shift; return $class->_action( 'add', @_ ); } sub error { my $class = shift; return $class->_action( 'error', @_ ); } sub write { my $class = shift; return $class->_action( 'write', @_ ); } 1; Notify.pm100644001751001751 446513232707066 14173 0ustar00janjan000000000000Rex-1.6.0/lib/Rex# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Notify; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION sub new { my $that = shift; my $proto = ref($that) || $that; my $self = {@_}; bless( $self, $proto ); $self->{__types__} = {}; $self->{__postponed__} = []; $self->{__running_postponed__} = 0; $self->{__in_notify__} = 0; return $self; } sub add { my ( $self, %option ) = @_; return if ( $self->{__in_notify__} ); if ( exists $self->{__types__}->{ $option{type} }->{ $option{name} } ) { Rex::Logger::debug( "A resource of the type $option{type} and name $option{name}" . "already exists.", "warn" ); return; } $self->{__types__}->{ $option{type} }->{ $option{name} } = { postpone => $option{postpone} || 0, options => $option{options}, cb => $option{cb}, }; } sub run { my ( $self, %option ) = @_; Rex::Logger::debug("Try to notify $option{type} -> $option{name}"); if ( exists $self->{__types__}->{ $option{type} } && exists $self->{__types__}->{ $option{type} }->{ $option{name} } && exists $self->{__types__}->{ $option{type} }->{ $option{name} }->{cb} && $self->{__types__}->{ $option{type} }->{ $option{name} }->{postpone} == 0 ) { Rex::Logger::debug("Running notify $option{type} -> $option{name}"); my $cb = $self->{__types__}->{ $option{type} }->{ $option{name} }->{cb}; $self->{__in_notify__} = 1; $cb->( $self->{__types__}->{ $option{type} }->{ $option{name} }->{options} ); $self->{__in_notify__} = 0; } else { if ( !$self->{__running_postponed__} ) { Rex::Logger::debug( "Can't notify $option{type} -> $option{name}. Postponing..."); $self->_postpone(%option); } else { Rex::Logger::info( "Can't run postponed notification. " . "Resource not found ($option{type} -> $option{name})", "warn" ); } } } sub run_postponed { my ($self) = @_; $self->{__running_postponed__} = 1; Rex::Logger::debug("Running postponed notifications."); for my $p ( @{ $self->{__postponed__} } ) { $self->run( %{$p} ); } } sub _postpone { my ( $self, %option ) = @_; push @{ $self->{__postponed__} }, \%option; } 1; Logger.pm100644001751001751 1250613232707066 14155 0ustar00janjan000000000000Rex-1.6.0/lib/Rex# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: =head1 NAME Rex::Logger - Logging Module =head1 DESCRIPTION This module is the logging module. You can define custom logformats. =head1 SYNOPSIS $Rex::Logger::format = '[%D] %s'; # will output something like # [2012-04-12 18:35:12] Installing package vim $Rex::Logger::format = '%h - %D - %s'; # will output something like # srv001 - 2012-04-12 18:35:12 - Installing package vim =head1 VARIABLES =over 4 =cut package Rex::Logger; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION #use Rex; our $no_color = 0; eval "use Term::ANSIColor"; if ($@) { $no_color = 1; } if ( $^O =~ m/MSWin/ ) { eval "use Win32::Console::ANSI"; if ($@) { $no_color = 1; } } my $has_syslog = 0; my $log_fh; =item $debug Setting this variable to 1 will enable debug logging. $Rex::Logger::debug = 1; =cut our $debug = 0; # we store the default handle to stderr # so that we can restore the handle inside the logging functions my $DEFAULT_STDERR; open $DEFAULT_STDERR, ">&", STDERR; =item $silent If you set this variable to 1 nothing will be logged. $Rex::Logger::silent = 1; =cut our $silent = 0; =item $format You can define the logging format with the following parameters. %D - Appends the current date yyyy-mm-dd HH:mm:ss %h - The target host %p - The pid of the running process %l - Loglevel (INFO or DEBUG) %s - The Logstring Default is: [%D] %l - %s =cut our $format = "[%D] %l - %s"; my $log_opened = 0; sub init { return if $silent; eval { die if ( Rex::Config->get_log_filename || !Rex::Config->get_log_facility ); die if ( $^O =~ m/^MSWin/ ); Sys::Syslog->use; openlog( "rex", "ndelay,pid", Rex::Config->get_log_facility ); $has_syslog = 1; }; $log_opened = 1; } sub info { my ( $msg, $type ) = @_; my $color = 'green'; if ( defined($type) ) { CHECK_COLOR: { $type eq 'warn' && do { $color = 'yellow'; last CHECK_COLOR; }; $type eq 'error' && do { $color = 'red'; last CHECK_COLOR; }; } } return if $silent; local *STDERR; open STDERR, ">&", $DEFAULT_STDERR; if ( defined($type) ) { $msg = format_string( $msg, uc($type) ); } else { $msg = format_string( $msg, "INFO" ); } # workaround for windows Sys::Syslog behaviour on forks # see: #6 unless ($log_opened) { init(); $log_opened = 2; } if ($has_syslog) { syslog( "info", $msg ); } if ( Rex::Config->get_log_filename() ) { open( $log_fh, ">>", Rex::Config->get_log_filename() ) or die($!); flock( $log_fh, 2 ); print {$log_fh} "$msg\n" if ($log_fh); close($log_fh); } if ($no_color) { print STDERR "$msg\n" if ( ( ( defined $::QUIET && $::QUIET == 2 ) && ( defined $type && $type ne 'info' ) ) || !defined $::QUIET ); } else { print STDERR colored( [$color], "$msg\n" ) if ( ( ( defined $::QUIET && $::QUIET == 2 ) && ( defined $type && $type ne 'info' ) ) || !defined $::QUIET ); } # workaround for windows Sys::Syslog behaviour on forks # see: #6 if ( $log_opened == 2 ) { &shutdown(); } } sub debug { my ($msg) = @_; return if $silent; return unless $debug; local *STDERR; open STDERR, ">&", $DEFAULT_STDERR; $msg = format_string( $msg, "DEBUG" ); # workaround for windows Sys::Syslog behaviour on forks # see: #6 unless ($log_opened) { init(); $log_opened = 2; } if ($has_syslog) { syslog( "debug", $msg ); } if ( Rex::Config->get_log_filename() ) { open( $log_fh, ">>", Rex::Config->get_log_filename() ) or die($!); flock( $log_fh, 2 ); print {$log_fh} "$msg\n" if ($log_fh); close($log_fh); } if ($no_color) { print STDERR "$msg\n" unless ($::QUIET); } else { print STDERR colored( ['red'], "$msg\n" ) unless ($::QUIET); } # workaround for windows Sys::Syslog behaviour on forks # see: #6 if ( $log_opened == 2 ) { &shutdown(); } } sub get_timestamp { my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) = localtime(time); $mon++; $year += 1900; return "$year-" . sprintf( "%02i", $mon ) . "-" . sprintf( "%02i", $mday ) . " " . sprintf( "%02i", $hour ) . ":" . sprintf( "%02i", $min ) . ":" . sprintf( "%02i", $sec ); } sub shutdown { return if $silent; return unless $log_opened; if ($has_syslog) { closelog(); } else { close($log_fh) if $log_fh; } $log_opened = 0; } # %D - Date # %h - Host # %s - Logstring sub format_string { my ( $s, $level ) = @_; my $date = get_timestamp; my $host = Rex::get_current_connection() && Rex::get_current_connection()->{conn}->server ? Rex::get_current_connection()->{conn}->server : ""; my $pid = $$; my $line = $format; $line =~ s/\%D/$date/gms; $line =~ s/\%h/$host/gms; $line =~ s/\%s/$s/gms; $line =~ s/\%l/$level/gms; $line =~ s/\%p/$pid/gms; return $line; } sub masq { my ( $format, @params ) = @_; return $format if scalar @params == 0; return $format if scalar( grep { defined } @params ) == 0; if ( exists $ENV{REX_DEBUG_INSECURE} && $ENV{REX_DEBUG_INSECURE} eq "1" ) { return sprintf( $format, @params ); } return sprintf( $format, ("**********") x @params ); } =back =cut 1; Report.pm100644001751001751 104213232707066 14162 0ustar00janjan000000000000Rex-1.6.0/lib/Rex# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Report; use strict; use warnings; use Data::Dumper; our $VERSION = '1.6.0'; # VERSION my $report; sub create { my ( $class, $type ) = @_; if ( $report && $type && ref($report) =~ m/::\Q$type\E$/ ) { return $report; } $type ||= "Base"; my $c = "Rex::Report::$type"; eval "use $c"; if ($@) { die("No reporting class $type found."); } $report = $c->new; return $report; } sub destroy { $report = undef; } 1; author-critic.t100644001751001751 41713232707066 14237 0ustar00janjan000000000000Rex-1.6.0/t#!perl BEGIN { unless ($ENV{AUTHOR_TESTING}) { print qq{1..0 # SKIP these tests are for testing by the author\n}; exit } } use strict; use warnings; use Test::Perl::Critic (-profile => "../../.perlcriticrc") x!! -e "../../.perlcriticrc"; all_critic_ok(); virtualization.t100644001751001751 102413232707066 14561 0ustar00janjan000000000000Rex-1.6.0/tuse strict; use warnings; use Test::More tests => 3; use Rex::Commands; Rex::Commands::set( virtualization => "LibVirt" ); is( Rex::Config->get("virtualization"), "LibVirt", "set virtualization handler" ); Rex::Commands::set( virtualization => { "type" => "LibVirt", "connect" => "qemu:///system", } ); is( Rex::Config->get("virtualization")->{type}, "LibVirt", "Virtualization type with connection URI" ); is( Rex::Config->get("virtualization")->{connect}, "qemu:///system", "Virtualization URI with connection URI" ); ip.out_issue_539100644001751001751 115113232707066 14262 0ustar00janjan000000000000Rex-1.6.0/t1: lo: mtu 16436 qdisc noqueue state UNKNOWN link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: eth0: mtu 1500 qdisc pfifo_fast state UP qlen 1000 link/ether 08:00:27:4b:b8:48 brd ff:ff:ff:ff:ff:ff inet 192.168.178.81/24 brd 192.168.178.255 scope global eth0 inet 192.168.99.37/24 brd 192.168.99.255 scope global secondary eth0 inet6 fe80::a00:27ff:fe4b:b848/64 scope link valid_lft forever preferred_lft forever default.yml100644001751001751 22213232707066 14343 0ustar00janjan000000000000Rex-1.6.0/t/cmdbntp: - ntp1 - ntp2 name: defaultname newntp: - ntp1 - ntp2 users: root: id: 0 password: proot MyTest::foo::mode: '0666' 513_t2.rex100644001751001751 6113232707066 14035 0ustar00janjan000000000000Rex-1.6.0/t/issueuse Rex -base; 1; # simulate true return value 513_t1.rex100644001751001751 6213232707066 14035 0ustar00janjan000000000000Rex-1.6.0/t/issueuse Rex -base; 0; # simulate false return value Require.pm100644001751001751 222413232707066 14326 0ustar00janjan000000000000Rex-1.6.0/lib/Rex# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Require; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Carp; require Rex::Logger; # some code borrowed from: UNIVERSAL::require (neilb) BEGIN { require UNIVERSAL; } my $module_name_rx = qr/[A-Z_a-z][0-9A-Z_a-z]*(?:::[0-9A-Z_a-z]+)*/; sub UNIVERSAL::require { my ( $module, %option ) = @_; $option{level} ||= 0; my ( $caller_package, $caller_file, $caller_line ) = caller( $option{level} ); my $file = $module . ".pm"; $file =~ s/::/\//g; # check if module is already loaded. return eval { 1 } if $INC{$file}; my $ret = eval "CORE::require(\$file)"; if ( !$ret ) { confess $@; } return $ret; } sub UNIVERSAL::use { my ( $module, @imports ) = @_; $module->require( level => 1 ); my ( $caller_package, $caller_file, $caller_line ) = caller(0); eval "package $caller_package;\n\$module->import(\@imports);\n1;"; if ($@) { confess $@; } return 1; } sub UNIVERSAL::is_loadable { my ($module) = @_; eval { $module->require; 1; }; if ($@) { return 0; } return 1; } 1; RunList.pm100644001751001751 601513232707066 14314 0ustar00janjan000000000000Rex-1.6.0/lib/Rex# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::RunList; use strict; use warnings; use Rex::Logger; use Rex::TaskList; our $VERSION = '1.6.0'; # VERSION my $INSTANCE; sub new { my ( $class, %args ) = @_; return bless \%args, $class; } # returns a singleton sub instance { my $class = shift; return $INSTANCE if $INSTANCE; $INSTANCE = $class->new(@_); } sub add_task { my ( $self, $task_name, $task_args, $task_opts ) = @_; $task_args ||= []; $task_opts ||= {}; my $task = $self->task_list->get_task($task_name); $task->set_args(@$task_args); $task->set_opts(%$task_opts); push @{ $self->{tasks} }, $task; } sub current_index { my ($self) = @_; return $self->{current_index} || 0; } sub increment_current_index { my ($self) = @_; return $self->{current_index} += 1; } sub current_task { my $self = shift; my $i = $self->current_index; $self->{tasks}->[$i]; } sub tasks { @{ shift->{tasks} || [] } } sub task_list { my $self = shift; return $self->{task_list} if $self->{task_list}; $self->{task_list} = Rex::TaskList->create; } sub run_tasks { my ($self) = @_; for my $task ( $self->tasks ) { Rex::TaskList->run($task); $self->increment_current_index; } } # Parse @ARGV to get tasks, task args, and task opts. Use these values to # generate a list of tasks the user wants to run. sub parse_opts { my ( $self, @params ) = @_; return $self->pre_1_4_parse_opts(@params) unless Rex::Config->get_task_chaining_cmdline_args; while ( my $task_name = shift @params ) { $self->exit_rex($task_name) unless $self->task_list->is_task($task_name); my @args; my %opts; while ( my $param = shift @params ) { if ( $self->task_list->is_task($param) ) { unshift @params, $param; last; } if ( $param =~ /^--/ ) { my ( $key, $val ) = split /=/, $param, 2; $key =~ s/^--//; $opts{$key} = defined $val ? $val : 1; } else { push @args, $param; } } $self->add_task( $task_name, \@args, \%opts ); } return $self; } # this function is to parse the task parameters in a pre 1.4 fashion. # this is used if the feature flag 'no_task_chaining_cmdline_args' or # '< 1.4' is enabled. sub pre_1_4_parse_opts { my ( $self, @params ) = @_; #### parse task options my %task_opts; for my $p (@params) { my ( $key, $val ) = split( /=/, $p, 2 ); $key =~ s/^--//; if ( defined $val ) { $task_opts{$key} = $val; next; } $task_opts{$key} = 1; } for my $task_name (@params) { next if $task_name =~ m/^\-\-/ || $task_name =~ m/=/; $self->exit_rex($task_name) unless $self->task_list->is_task($task_name); $self->add_task( $task_name, [], \%task_opts ); } } sub exit_rex { my ( $self, $task_name ) = @_; my $msg = "Task names are case sensitive "; $msg .= "and the module delimiter is a single colon."; Rex::Logger::info( "No task named '$task_name' found. $msg", 'error' ); Rex::CLI::exit_rex(1); } 1; PkgConf.pm100644001751001751 300013232707066 14232 0ustar00janjan000000000000Rex-1.6.0/lib/Rex# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::PkgConf; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Rex::Config; use Rex::Commands::Gather; use Rex::Hardware; use Rex::Hardware::Host; use Rex::Logger; my %PKG_PROVIDER; sub register_package_provider { my ( $class, $service_name, $service_class ) = @_; $PKG_PROVIDER{"\L$service_name"} = $service_class; return 1; } sub get { my ($self) = @_; my %_host = %{ Rex::Hardware::Host->get() }; my $host = {%_host}; my $pkg_provider_for = Rex::Config->get("package_provider") || {}; if ( is_redhat() ) { $host->{"operatingsystem"} = "Redhat"; } elsif ( is_debian() ) { $host->{"operatingsystem"} = "Debian"; } my $class = "Rex::PkgConf::" . $host->{"operatingsystem"}; my $provider; if ( ref($pkg_provider_for) && exists $pkg_provider_for->{ $host->{"operatingsystem"} } ) { $provider = $pkg_provider_for->{ $host->{"operatingsystem"} }; $class .= "::$provider"; } elsif ( exists $PKG_PROVIDER{$pkg_provider_for} ) { $class = $PKG_PROVIDER{$pkg_provider_for}; } Rex::Logger::debug("Using $class for package management"); eval "use $class"; if ($@) { if ($provider) { Rex::Logger::info( "Provider not supported (" . $provider . ")" ); } else { Rex::Logger::info( "OS not supported (" . $host->{"operatingsystem"} . ")" ); } die("OS/Provider not supported"); } return $class->new; } 1; Service.pm100644001751001751 454713232707066 14324 0ustar00janjan000000000000Rex-1.6.0/lib/Rex# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Service; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Rex::Config; use Rex::Commands::Gather; use Rex::Hardware; use Rex::Hardware::Host; use Rex::Helper::Run; use Rex::Logger; my %SERVICE_PROVIDER; sub register_service_provider { my ( $class, $service_name, $service_class ) = @_; $SERVICE_PROVIDER{"\L$service_name"} = $service_class; return 1; } sub get { my $operatingsystem = Rex::Hardware::Host->get_operating_system(); i_run "systemctl --no-pager > /dev/null", fail_ok => 1; my $can_run_systemctl = $? == 0 ? 1 : 0; my $class; $class = "Rex::Service::" . $operatingsystem; if ( is_redhat($operatingsystem) && $can_run_systemctl ) { $class = "Rex::Service::Redhat::systemd"; } elsif ( is_redhat($operatingsystem) ) { # this also counts for fedora, centos, ... $class = "Rex::Service::Redhat"; } elsif ( is_suse($operatingsystem) && $can_run_systemctl ) { $class = "Rex::Service::SuSE::systemd"; } elsif ( is_alt($operatingsystem) && $can_run_systemctl ) { $class = "Rex::Service::ALT::systemd"; } elsif ( is_gentoo($operatingsystem) && $can_run_systemctl ) { $class = "Rex::Service::Gentoo::systemd"; } elsif ( is_gentoo($operatingsystem) ) { $class = "Rex::Service::Gentoo"; } elsif ( is_mageia($operatingsystem) && $can_run_systemctl ) { $class = "Rex::Service::Mageia::systemd"; } elsif ( is_debian($operatingsystem) && $can_run_systemctl ) { # this also counts for Ubuntu and LinuxMint $class = "Rex::Service::Debian::systemd"; } elsif ( is_debian($operatingsystem) ) { $class = "Rex::Service::Debian"; } elsif ( is_arch($operatingsystem) && $can_run_systemctl ) { $class = "Rex::Service::Arch::systemd"; } my $provider_for = Rex::Config->get("service_provider") || {}; my $provider; if ( ref($provider_for) && exists $provider_for->{$operatingsystem} ) { $provider = $provider_for->{$operatingsystem}; $class .= "::\L$provider"; } elsif ( exists $SERVICE_PROVIDER{$provider_for} ) { $class = $SERVICE_PROVIDER{$provider_for}; } Rex::Logger::debug("service using class: $class"); eval "use $class"; if ($@) { Rex::Logger::info("OS ($operatingsystem) not supported"); exit 1; } return $class->new; } 1; Pkg000755001751001751 013232707066 12735 5ustar00janjan000000000000Rex-1.6.0/lib/RexALT.pm100644001751001751 331613232707066 14056 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Pkg# # Work with ALT Linux APT-RPM package management system # package Rex::Pkg::ALT; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Rex::Helper::Run; use Rex::Commands::File; use Rex::Commands::Fs; use Rex::Pkg::Base; use base qw(Rex::Pkg::Base); sub new { my $that = shift; my $proto = ref($that) || $that; my $self = $proto->SUPER::new(@_); bless( $self, $proto ); $self->{commands} = { install => '/usr/bin/apt-get -y install %s', install_version => '/usr/bin/apt-get -y install %s-%s', remove => '/usr/bin/apt-get -y remove %s', update_package_db => '/usr/bin/apt-get update', }; return $self; } sub get_installed { my ($self) = @_; my @lines = i_run '/usr/bin/rpm -qa --nodigest --qf "%{NAME} %|EPOCH?{%{EPOCH}}:{0}| %{VERSION} %{RELEASE} %{ARCH}\n"'; my @pkg; for my $line (@lines) { if ( $line =~ m/^([^\s]+)\s([^\s]+)\s([^\s]+)\s([^\s]+)\s(.*)$/ ) { push( @pkg, { name => $1, epoch => $2, version => $3, release => $4, arch => $5, } ); } } return @pkg; } sub add_repository { my ( $self, %data ) = @_; my $name = $data{"name"}; my $sign = $data{"sign_key"} || ""; my @arch = split( /, */, $data{"arch"} ); my $fh = file_write "/etc/apt/sources.list.d/$name.list"; $fh->write("# This file is managed by Rex\n"); foreach (@arch) { $fh->write( "rpm " . ( $sign ? "[" . $sign . "] " : "" ) . $data{"url"} . " " . $_ . " " . $data{"repository"} . "\n" ); } $fh->close; } sub rm_repository { my ( $self, $name ) = @_; unlink "/etc/apt/sources.list.d/$name.list"; } 1; SCM000755001751001751 013232707066 12636 5ustar00janjan000000000000Rex-1.6.0/lib/RexGit.pm100644001751001751 771513232707066 14071 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/SCMpackage Rex::SCM::Git; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Cwd qw(getcwd); use Rex::Commands::Fs; use File::Basename; use Rex::Helper::Run; use vars qw($CHECKOUT_BRANCH_COMMAND $CHECKOUT_TAG_COMMAND $CLONE_COMMAND); $CLONE_COMMAND = "git clone %s %s"; $CHECKOUT_BRANCH_COMMAND = "git checkout -b %s origin/%s"; $CHECKOUT_TAG_COMMAND = "git checkout -b %s %s"; sub new { my $that = shift; my $proto = ref($that) || $that; my $self = {@_}; bless( $self, $proto ); return $self; } sub checkout { my ( $self, $repo_info, $checkout_to, $checkout_opt ) = @_; if ( !is_dir($checkout_to) ) { my $clone_cmd = sprintf( $CLONE_COMMAND, $repo_info->{"url"}, basename($checkout_to) ); Rex::Logger::debug("clone_cmd: $clone_cmd (cwd: " . dirname($checkout_to). ")"); Rex::Logger::info( "Cloning " . $repo_info->{"url"} . " to " . ( $checkout_to ? $checkout_to : "." ) ); my $out = i_run "$clone_cmd", cwd => dirname($checkout_to), fail_ok => 1; unless ( $? == 0 ) { Rex::Logger::info( "Error cloning repository.", "warn" ); Rex::Logger::info($out); die("Error cloning repository."); } Rex::Logger::debug($out); if ( exists $checkout_opt->{"branch"} ) { unless ($checkout_to) { $checkout_to = [ split( /\//, $repo_info->{"url"} ) ]->[-1]; $checkout_to =~ s/\.git$//; } my $checkout_cmd = sprintf( $CHECKOUT_BRANCH_COMMAND, $checkout_opt->{"branch"}, $checkout_opt->{"branch"} ); Rex::Logger::debug("checkout_cmd: $checkout_cmd"); Rex::Logger::info( "Switching to branch " . $checkout_opt->{"branch"} ); $out = i_run "$checkout_cmd", cwd => $checkout_to, fail_ok => 1; unless ( $? == 0 ) { Rex::Logger::info( "Error switching to branch.", "warn" ); Rex::Logger::info($out); die("Error switching to branch."); } Rex::Logger::debug($out); } if ( exists $checkout_opt->{"tag"} ) { my $checkout_cmd = sprintf( $CHECKOUT_TAG_COMMAND, $checkout_opt->{"tag"}, $checkout_opt->{"tag"} ); Rex::Logger::info( "Switching to tag " . $checkout_opt->{"tag"} ); $out = i_run "$checkout_cmd", cwd => $checkout_to, fail_ok => 1; unless ( $? == 0 ) { Rex::Logger::info( "Error switching to tag.", "warn" ); Rex::Logger::info($out); die("Error switching to tag."); } Rex::Logger::debug($out); } } elsif ( is_dir("$checkout_to/.git") ) { my $branch = $checkout_opt->{"branch"} || "master"; Rex::Logger::info( "Pulling " . $repo_info->{"url"} . " to " . ( $checkout_to ? $checkout_to : "." ) ); my $rebase = $checkout_opt->{"rebase"} ? '--rebase' : ''; my $out = i_run "git pull $rebase origin $branch", cwd => $checkout_to, fail_ok => 1; unless ( $? == 0 ) { Rex::Logger::info( "Error pulling.", "warn" ); Rex::Logger::info($out); die("Error pulling."); } else { Rex::Logger::debug($out); } if ( exists $checkout_opt->{"tag"} ) { my $tag = $checkout_opt->{tag}; my $checkout_cmd = sprintf( $CHECKOUT_TAG_COMMAND, $tag, $tag ); Rex::Logger::info( "Switching to tag " . $tag ); $out = i_run "git fetch origin", cwd => $checkout_to, fail_ok => 1; unless ( $? == 0 ) { Rex::Logger::info( "Error switching to tag.", "warn" ); Rex::Logger::info($out); die("Error switching to tag."); } else { Rex::Logger::debug($out); } $out = i_run "$checkout_cmd", cwd => $checkout_to, fail_ok => 1; unless ( $? == 0 ) { Rex::Logger::info( "Error switching to tag.", "warn" ); Rex::Logger::info($out); die("Error switching to tag."); } Rex::Logger::debug($out); } } else { Rex::Logger::info( "Error checking out repository.", "warn" ); die("Error checking out repository."); } } 1; FS000755001751001751 013232707066 12524 5ustar00janjan000000000000Rex-1.6.0/lib/RexFile.pm100644001751001751 663113232707066 14107 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/FS# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: =head1 NAME Rex::FS::File - File Class =head1 DESCRIPTION This is the File Class used by I and I. =head1 SYNOPSIS use Rex::Interface::File; my $fh = Rex::Interface::File->create('Local'); $fh->open( '<', 'filename' ); my $file = Rex::FS::File->new(fh => $fh); $file->read($len); $file->read_all; $file->write($buf); $file->close; =head1 CLASS METHODS =cut package Rex::FS::File; use strict; use warnings; use Rex::Interface::File; our $VERSION = '1.6.0'; # VERSION use constant DEFAULT_READ_LEN => 64; =head2 new This is the constructor. You need to set the filehandle which the object should work on or pass a filename. If you pass a filehandle, it has to be a C object my $fh = Rex::Interface::File->create('Local'); $fh->open( '<', 'filename' ); my $file = Rex::FS::File->new(fh => $fh); Create a C object with a filename # open a local file in read mode my $file = Rex::FS::File->new( filename => 'filename', mode => 'r', # or '<' type => 'Local', ); # or shorter my $file = Rex::FS::File->new( filename => 'filename' ); # open a local file in write mode my $file = Rex::FS::File->new( filename => 'filename', mode => 'w', # or '>' ); Allowed modes: < read r read > write w write >> append a append For allowed C see documentation of L. =cut sub new { my $that = shift; my $proto = ref($that) || $that; my $self = {@_}; my %modes = ( 'w' => '>', 'r' => '<', 'a' => '>>', '<' => '<', '>' => '>', '>>' => '>>', ); if ( $self->{filename} ) { $self->{mode} ||= '<'; my $mode = $modes{ $self->{mode} } || '<'; $self->{fh} = Rex::Interface::File->create( $self->{type} || 'Local' ); $self->{fh}->open( $mode, $self->{filename} ); } bless( $self, $proto ); if ( ref $self->{fh} !~ m{Rex::Interface::File} ) { die "Need an Rex::Interface::File object"; } return $self; } sub DESTROY { my ($self) = @_; if ( ref $self->{'fh'} =~ m/^Rex::Interface::File/ ) { $self->close if ( $self->{'fh'}->{'fh'} ); } else { $self->close if ( $self->{'fh'} ); } } =head2 write($buf) Write $buf into the filehandle. $file->write("Hello World"); =cut sub write { my ( $self, @buf ) = @_; my $fh = $self->{fh}; if ( scalar(@buf) > 1 ) { for my $line (@buf) { $fh->write($line); $fh->write($/); } } else { $fh->write( $buf[0] ); } } =head2 seek($offset) Seek to the file position $offset. Set the file pointer to the 5th byte. $file->seek(5); =cut sub seek { my ( $self, $offset ) = @_; my $fh = $self->{'fh'}; $fh->seek($offset); } =head2 read($len) Read $len bytes out of the filehandle. my $content = $file->read(1024); =cut sub read { my ( $self, $len ) = @_; $len = DEFAULT_READ_LEN if ( !$len ); my $fh = $self->{'fh'}; return $fh->read($len); } =head2 read_all Read everything out of the filehandle. my $content = $file->read_all; =cut sub read_all { my ($self) = @_; my $all = ''; while ( my $in = $self->read() ) { $all .= $in; } if (wantarray) { return split( /\n/, $all ); } return $all; } =head2 close Close the file. $file->close; =cut sub close { my ($self) = @_; my $fh = $self->{'fh'}; $fh->close; } 1; Box000755001751001751 013232707066 12744 5ustar00janjan000000000000Rex-1.6.0/lib/RexKVM.pm100644001751001751 750313232707066 14104 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Box# # (c) Jan Gehring # # vim: set ts=3 sw=3 tw=0: # vim: set expandtab: =head1 NAME Rex::Box::KVM - Rex/Boxes KVM Module =head1 DESCRIPTION This is a Rex/Boxes module to use KVM VMs. You need to have libvirt installed. =head1 EXAMPLES To use this module inside your Rexfile you can use the following commands. use Rex::Commands::Box; set box => "KVM"; task "prepare_box", sub { box { my ($box) = @_; $box->name("mybox"); $box->url("http://box.rexify.org/box/ubuntu-server-12.10-amd64.kvm.qcow2"); $box->network(1 => { name => "default", }); $box->auth( user => "root", password => "box", ); $box->setup("setup_task"); }; }; If you want to use a YAML file you can use the following template. type: KVM vms: vmone: url: http://box.rexify.org/box/ubuntu-server-12.10-amd64.kvm.qcow2 setup: setup_task And then you can use it the following way in your Rexfile. use Rex::Commands::Box init_file => "file.yml"; task "prepare_vms", sub { boxes "init"; }; =head1 METHODS See also the Methods of Rex::Box::Base. This module inherits all methods of it. =cut package Rex::Box::KVM; use strict; use warnings; use Data::Dumper; use Rex::Box::Base; use Rex::Commands -no => [qw/auth/]; use Rex::Commands::Fs; use Rex::Commands::Virtualization; use Rex::Commands::SimpleCheck; our $VERSION = '1.6.0'; # VERSION BEGIN { LWP::UserAgent->use; } use Time::HiRes qw(tv_interval gettimeofday); use File::Basename qw(basename); use base qw(Rex::Box::Base); set virtualization => "LibVirt"; $|++; ################################################################################ # BEGIN of class methods ################################################################################ =head2 new(name => $vmname) Constructor if used in OO mode. my $box = Rex::Box::KVM->new(name => "vmname"); =cut sub new { my $class = shift; my $proto = ref($class) || $class; my $self = $proto->SUPER::new(@_); bless( $self, ref($class) || $class ); return $self; } =head2 memory($memory_size) Sets the memory of a VM in megabyte. =cut sub memory { my ( $self, $mem ) = @_; $self->{memory} = $mem * 1024; # libvirt wants kilobytes } sub import_vm { my ($self) = @_; # check if machine already exists my $vms = vm list => "all"; my $vm_exists = 0; for my $vm ( @{$vms} ) { if ( $vm->{name} eq $self->{name} ) { Rex::Logger::debug("VM already exists. Don't import anything."); $vm_exists = 1; } } if ( !$vm_exists ) { # if not, create it $self->_download; my $filename = basename( $self->{url} ); Rex::Logger::info("Importing VM ./tmp/$filename"); my @options = ( import => $self->{name}, file => "./tmp/$filename", %{$self}, ); if (Rex::Config::get_use_rex_kvm_agent) { my $tcp_port = int( rand(40000) ) + 10000; push @options, 'serial_devices', [ { type => 'tcp', host => '127.0.0.1', port => $tcp_port, }, ]; Rex::Logger::info( "Binding a serial device to TCP port $tcp_port for rex-kvm-agent"); } vm @options; #unlink "./tmp/$filename"; } my $vminfo = vm info => $self->{name}; if ( $vminfo->{State} eq "shut off" ) { $self->start; } $self->{info} = vm guestinfo => $self->{name}; } sub list_boxes { my ($self) = @_; my $vms = vm list => "all"; return @{$vms}; } =head2 info Returns a hashRef of vm information. =cut sub info { my ($self) = @_; $self->ip; return $self->{info}; } =head2 ip This method return the ip of a vm on which the ssh daemon is listening. =cut sub ip { my ($self) = @_; $self->{info} = vm guestinfo => $self->{name}; return $self->{info}->{network}->[0]->{ip}; } 1; Commands.pm100644001751001751 11763213232707066 14525 0ustar00janjan000000000000Rex-1.6.0/lib/Rex # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: =head1 NAME Rex::Commands - All the basic commands =head1 DESCRIPTION This module is the core commands module. =head1 SYNOPSIS desc "Task description"; task "taskname", sub { ... }; task "taskname", "server1", ..., "server20", sub { ... }; group "group" => "server1", "server2", ...; user "user"; password "password"; environment live => sub { user "root"; password "foobar"; pass_auth; group frontend => "www01", "www02"; }; =head1 COMMANDLIST =over 4 =item * Augeas config file management library L =item * Cloud Management L =item * Cron Management L =item * Database Commands L =item * SCP Up- and Download L, L =item * File Manipulation L =item * Filesystem Manipulation L =item * Information Gathering L =item * Manipulation of /etc/hosts L =item * Get an inventory of your Hardware L =item * Manage your iptables rules L =item * Kernel Commands L =item * LVM Commands L =item * MD5 checksums L =item * Network commands L =item * Notify resources to execute L =item * Package Commands L =item * Partition your storage device(s) L =item * Configure packages (via debconf) L =item * Process Management L =item * Rsync Files L =item * Run Remote Commands L =item * Source control via Subversion/Git L =item * Manage System Services (sysvinit) L =item * Simple TCP/alive checks L =item * Sync directories L =item * Sysctl Commands L =item * Live Tail files L =item * Upload local file to remote server L =item * Manage user and group accounts L =item * Manage your virtual environments L =back =head1 EXPORTED FUNCTIONS =cut package Rex::Commands; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION require Rex::Exporter; use Rex::TaskList; use Rex::Logger; use Rex::Config; use Rex::Profiler; use Rex::Report; use Rex; use Rex::Helper::Misc; use Rex::RunList; use vars qw(@EXPORT $current_desc $global_no_ssh $environments $dont_register_tasks $profiler %auth_late); use base qw(Rex::Exporter); @EXPORT = qw(task desc group user password port sudo_password public_key private_key pass_auth key_auth krb5_auth no_ssh get_random batch timeout max_connect_retries parallelism proxy_command do_task run_task run_batch needs exit evaluate_hostname logging include say environment LOCAL path set get before after around before_task_start after_task_finished logformat log_format sayformat say_format connection auth FALSE TRUE set_distributor set_executor_for template_function report make source_global_profile last_command_output case inspect tmp_dir cache ); our $REGISTER_SUB_HASH_PARAMETER = 0; =head2 no_ssh([$task]) Disable ssh for all tasks or a specified task. If you want to disable ssh connection for your complete tasks (for example if you only want to use libVirt) put this in the main section of your Rexfile. no_ssh; If you want to disable ssh connection for a given task, put I in front of the task definition. no_ssh task "mytask", "myserver", sub { say "Do something without a ssh connection"; }; =cut sub no_ssh { if (@_) { $_[0]->( no_ssh => 1 ); } else { $global_no_ssh = 1; } } =head2 task($name [, @servers], $funcref) This function will create a new task. =over 4 =item Create a local task (a server independent task) task "mytask", sub { say "Do something"; }; If you call this task with (R)?ex it will run on your local machine. You can explicit run this task on other machines if you specify the I<-H> command line parameter. =item Create a server bound task. task "mytask", "server1", sub { say "Do something"; }; You can also specify more than one server. task "mytask", "server1", "server2", "server3", sub { say "Do something"; }; Or you can use some expressions to define more than one server. task "mytask", "server[1..3]", sub { say "Do something"; }; If you want, you can overwrite the servers with the I<-H> command line parameter. =item Create a group bound task. You can define server groups with the I function. group "allserver" => "server[1..3]", "workstation[1..10]"; task "mytask", group => "allserver", sub { say "Do something"; }; =back =cut sub task { my ( $class, $file, @tmp ) = caller; my @_ARGS = @_; if ( !@_ ) { if ( my $t = Rex::get_current_connection ) { return $t->{task}->[-1]; } return; } # for things like # no_ssh task ... if (wantarray) { return sub { my %option = @_; $option{class} = $class; $option{file} = $file; $option{tmp} = \@tmp; task( @_ARGS, \%option ); }; } if ( ref( $_ARGS[-1] ) eq "HASH" ) { if ( $_ARGS[-1]->{class} ) { $class = $_ARGS[-1]->{class}; } if ( $_ARGS[-1]->{file} ) { $file = $_ARGS[-1]->{file}; } if ( $_ARGS[-1]->{tmp} ) { @tmp = @{ $_ARGS[-1]->{tmp} }; } } my $task_name = shift; my $task_name_save = $task_name; if ( $task_name !~ m/^[a-zA-Z_][a-zA-Z0-9_]*$/ && !Rex::Config->get_disable_taskname_warning() ) { Rex::Logger::info( "Please use only the following characters for task names:", "warn" ); Rex::Logger::info( " A-Z, a-z, 0-9 and _", "warn" ); Rex::Logger::info( "Also the task should start with A-Z or a-z", "warn" ); Rex::Logger::info( "You can disable this warning by setting feature flag: disable_taskname_warning", "warn" ); } my $options = {}; if ( ref( $_[-1] ) eq "HASH" ) { $options = pop; } if ($global_no_ssh) { $options->{"no_ssh"} = 1; } if ( $class ne "main" && $class ne "Rex::CLI" ) { $task_name = $class . ":" . $task_name; } $task_name =~ s/^Rex:://; $task_name =~ s/::/:/g; if ($current_desc) { push( @_, $current_desc ); $current_desc = ""; } else { push( @_, "" ); } no strict 'refs'; no warnings; push( @{"${class}::tasks"}, { name => $task_name_save, code => $_[-2] } ); use strict; use warnings; $options->{'dont_register'} ||= $dont_register_tasks; my $task_o = Rex::TaskList->create()->create_task( $task_name, @_, $options ); if (!$class->can($task_name_save) && $task_name_save =~ m/^[a-zA-Z_][a-zA-Z0-9_]+$/ ) { no strict 'refs'; Rex::Logger::debug("Registering task: $task_name"); my $code = $_[-2]; *{"${class}::$task_name_save"} = sub { Rex::Logger::info("Running task $task_name on current connection"); my $param; if ( scalar @_ == 1 && ref $_[0] eq "HASH" ) { $param = $_[0]; } elsif ( $REGISTER_SUB_HASH_PARAMETER && scalar @_ % 2 == 0 ) { $param = {@_}; } else { $param = \@_; } $task_o->run( "", params => $param ); }; } $options->{'dont_register'} ||= $dont_register_tasks; return $task_o; } =head2 desc($description) Set the description of a task. desc "This is a task description of the following task"; task "mytask", sub { say "Do something"; } =cut sub desc { $current_desc = shift; } =head2 group($name, @servers) With this function you can group servers, so that you don't need to write too much ;-) group "servergroup", "www1", "www2", "www3", "memcache01", "memcache02", "memcache03"; Or with the expression syntax: group "servergroup", "www[1..3]", "memcache[01..03]"; You can also specify server options after a server name with a hash reference: group "servergroup", "www1" => { user => "other" }, "www2"; These expressions are allowed: =over 4 =item * \d+..\d+ (range) The first number is the start and the second number is the end for numbering the servers. group "name", "www[1..3]"; # www1, www2, www3 =item * \d+..\d+/\d+ (range with step) Just like the range notation, but with an additional "step" defined. If step is omitted, it defaults to 1 (i.e. it behaves like a simple range expression). group "name", "www[1..5/2]"; # www1, www3, www5 group "name", "www[111..133/11]"; # www111, www122, www133 =item * \d+,\d+,\d+ (list) With this variant you can define fixed values. group "name", "www[1,3,7,01]"; # www1, www3, www7, www01 =item * Mixed list, range and range with step You can mix the three variants above www[1..3,5,9..21/3]; # www1, www2, www3, www5, www9, www12, www15, www18, www21 =back =cut sub group { my @params = @_; if ( scalar @params <= 7 && ( defined $params[1] ? ( grep { $params[1] eq $_ } qw/ensure system gid/ ) : 0 ) && ( defined $params[3] ? ( grep { $params[3] eq $_ } qw/ensure system gid/ ) : 1 ) && ( defined $params[5] ? ( grep { $params[5] eq $_ } qw/ensure system gid/ ) : 1 ) ) { # call create_group Rex::Commands::User::group_resource(@params); } else { Rex::Group->create_group(@params); } } # Register set-handler for group Rex::Config->register_set_handler( group => sub { Rex::Commands::group(@_); } ); =head2 batch($name, @tasks) With the batch function you can call tasks in a batch. batch "name", "task1", "task2", "task3"; And call it with the I<-b> console parameter. I =cut sub batch { if ($current_desc) { push( @_, $current_desc ); $current_desc = ""; } else { push( @_, "" ); } Rex::Batch->create_batch(@_); } =head2 user($user) Set the user for the ssh connection. =cut sub user { Rex::Config->set_user(@_); } =head2 password($password) Set the password for the ssh connection (or for the private key file). =cut sub password { Rex::Config->set_password(@_); } =head2 auth(for => $entity, %data) With this function you can modify/set special authentication parameters for tasks and groups. If you want to modify a group's authentication you first have to create it. (Place the auth command after the group.) If you want to set special login information for a group you have to activate that feature first. use Rex -feature => 0.31; # activate setting auth for a group # auth for groups group frontends => "web[01..10]"; group backends => "be[01..05]"; auth for => "frontends" => user => "root", password => "foobar"; auth for => "backends" => user => "admin", private_key => "/path/to/id_rsa", public_key => "/path/to/id_rsa.pub", sudo => TRUE; # auth for tasks task "prepare", group => ["frontends", "backends"], sub { # do something }; auth for => "prepare" => user => "root"; # auth for multiple tasks with regular expression task "step_1", sub { # do something }; task "step_2", sub { # do something }; auth for => qr/step/ => user => $user, password => $password; # fallback auth auth fallback => { user => "fallback_user1", password => "fallback_pw1", public_key => "", private_key => "", }, { user => "fallback_user2", password => "fallback_pw2", public_key => "keys/public.key", private_key => "keys/private.key", sudo => TRUE, }; =cut sub auth { if ( !ref $_[0] && $_[0] eq "fallback" ) { # set fallback authentication shift; Rex::Config->set_fallback_auth(@_); return 1; } my ( $_d, $entity, %data ) = @_; my $group = Rex::Group->get_group_object($entity); if ( !$group ) { Rex::Logger::debug("No group $entity found, looking for a task."); if ( ref($entity) eq "Regexp" ) { my @tasks = Rex::TaskList->create()->get_tasks; my @selected_tasks = grep { m/$entity/ } @tasks; for my $t (@selected_tasks) { auth( $_d, $t, %data ); } return; } else { $group = Rex::TaskList->create()->get_task($entity); } } if ( !$group ) { Rex::Logger::info( "Group or Task $entity not found. Assuming late-binding for task."); $auth_late{$entity} = \%data; return; } if ( ref($group) eq "Rex::Group" ) { Rex::Logger::debug("================================================="); Rex::Logger::debug("You're setting special login credentials for a Group."); Rex::Logger::debug( "Please remember that the default auth information/task auth information has precedence." ); Rex::Logger::debug( "If you want to overwrite this behaviour please use ,,use Rex -feature => 0.31;'' in your Rexfile." ); Rex::Logger::debug("================================================="); } if ( exists $data{pass_auth} ) { $data{auth_type} = "pass"; } if ( exists $data{key_auth} ) { $data{auth_type} = "key"; } if ( exists $data{krb5_auth} ) { $data{auth_type} = "krb5"; } Rex::Logger::debug( "Setting auth info for " . ref($group) . " $entity" ); $group->set_auth(%data); } =head2 port($port) Set the port where the ssh server is listening. =cut sub port { Rex::Config->set_port(@_); } =head2 sudo_password($password) Set the password for the sudo command. =cut sub sudo_password { Rex::Config->set_sudo_password(@_); } =head2 timeout($seconds) Set the timeout for the ssh connection and other network related stuff. =cut sub timeout { Rex::Config->set_timeout(@_); } =head2 max_connect_retries($count) Set the maximum number of connection retries. =cut sub max_connect_retries { Rex::Config->set_max_connect_fails(@_); } =head2 get_random($count, @chars) Returns a random string of $count characters on the basis of @chars. my $rnd = get_random(8, 'a' .. 'z'); =cut sub get_random { return Rex::Helper::Misc::get_random(@_); } =head2 do_task($task) Call $task from another task. It will establish a new connection to the server defined in $task and then execute $task there. task "task1", "server1", sub { say "Running on server1"; do_task "task2"; }; task "task2", "server2", sub { say "Running on server2"; }; You may also use an arrayRef for $task if you want to call multiple tasks. do_task [ qw/task1 task2 task3/ ]; =cut sub do_task { my $task = shift; my $params = shift; # only get all parameters if task_chaining_cmdline_args (or feature flag >= 1.4) # is not active. # since 1.4 every task can have its own arguments. if ( !Rex::Config->get_task_chaining_cmdline_args ) { $params ||= { Rex::Args->get }; } # default is an empty hash $params ||= {}; if ( ref($task) eq "ARRAY" ) { for my $t ( @{$task} ) { Rex::TaskList->create()->get_task($t) || die "Task $t not found."; Rex::TaskList->run( $t, params => $params ); } } else { Rex::TaskList->create()->get_task($task) || die "Task $task not found."; return Rex::TaskList->run( $task, params => $params ); } } =head2 run_task($task_name, %option) Run a task on a given host. my $return = run_task "taskname", on => "192.168.3.56"; Do something on server5 if memory is less than 100 MB free on server3. task "prepare", "server5", sub { my $free_mem = run_task "get_free_mem", on => "server3"; if($free_mem < 100) { say "Less than 100 MB free mem on server3"; # create a new server instance on server5 to unload server3 } }; task "get_free_mem", sub { return memory->{free}; }; If called without a hostname the task is run localy. # this task will run on server5 task "prepare", "server5", sub { # this will call task check_something. but this task will run on localhost. my $check = run_task "check_something"; } task "check_something", "server4", sub { return "foo"; }; If you want to add custom parameters for the task you can do it this way. task "prepare", "server5", sub { run_task "check_something", on => "foo", params => { param1 => "value1", param2 => "value2" }; }; =cut sub run_task { my ( $task_name, %option ) = @_; if ( exists $option{on} ) { my $task = Rex::TaskList->create()->get_task($task_name); if ( exists $option{params} ) { $task->run( $option{on}, params => $option{params} ); } else { $task->run( $option{on} ); } } else { my $task = Rex::TaskList->create()->get_task($task_name); if ( exists $option{params} ) { $task->run( "", params => $option{params} ); } else { $task->run(""); } } } =head2 run_batch($batch_name, %option) Run a batch on a given host. my @return = run_batch "batchname", on => "192.168.3.56"; It calls internally run_task, and passes it any option given. =cut sub run_batch { my ( $batch_name, %option ) = @_; my @tasks = Rex::Batch->get_batch($batch_name); my @results; for my $task (@tasks) { my $return = run_task $task, %option; push @results, $return; } return @results; } =head2 public_key($key) Set the public key. =cut sub public_key { Rex::Config->set_public_key(@_); } =head2 private_key($key) Set the private key. =cut sub private_key { Rex::Config->set_private_key(@_); } =head2 pass_auth If you want to use password authentication, then you need to call I. user "root"; password "root"; pass_auth; =cut sub pass_auth { if (wantarray) { return "pass"; } Rex::Config->set_password_auth(1); } =head2 key_auth If you want to use pubkey authentication, then you need to call I. user "bob"; private_key "/home/bob/.ssh/id_rsa"; # passphrase-less key public_key "/home/bob/.ssh/id_rsa.pub"; key_auth; =cut sub key_auth { if (wantarray) { return "key"; } Rex::Config->set_key_auth(1); } =head2 krb5_auth If you want to use kerberos authentication, then you need to call I. This authentication mechanism is only available if you use Net::OpenSSH. set connection => "OpenSSH"; user "root"; krb5_auth; =cut sub krb5_auth { if (wantarray) { return "krb5"; } Rex::Config->set_krb5_auth(1); } =head2 parallelism($count) Will execute the tasks in parallel on the given servers. $count is the thread count to be used: parallelism '2'; # set parallelism to 2 Alternatively, the following notation can be used to set thread count more dynamically: parallelism 'max'; # set parallelism to the number of servers a task is asked to run on parallelism 'max/3'; # set parallelism to 1/3 of the number of servers parallelism 'max 10%'; # set parallelism to 10% of the number of servers If an unrecognized value is passed, or the calculated thread count would be less than 1, Rex falls back to use a single thread. =cut sub parallelism { Rex::Config->set_parallelism( $_[0] ); } =head2 proxy_command($cmd) Set a proxy command to use for the connection. This is only possible with OpenSSH connection method. set connection => "OpenSSH"; proxy_command "ssh user@jumphost nc %h %p 2>/dev/null"; =cut sub proxy_command { Rex::Config->set_proxy_command( $_[0] ); } =head2 set_distributor($distributor) This sets the task distribution module. Default is "Base". Possible values are: Base, Gearman, Parallel_ForkManager =cut sub set_distributor { Rex::Config->set_distributor( $_[0] ); } =head2 template_function(sub { ... }) This function sets the template processing function. So it is possible to change the template engine. For example to Template::Toolkit. =cut sub template_function { Rex::Config->set_template_function( $_[0] ); } =head2 logging With this function you can define the logging behaviour of (R)?ex. =over 4 =item Logging to a file logging to_file => "rex.log"; =item Logging to syslog logging to_syslog => $facility; =back =cut sub logging { my $args; if ( $_[0] eq "-nolog" || $_[0] eq "nolog" ) { $Rex::Logger::silent = 1 unless $Rex::Logger::debug; return; } else { $args = {@_}; } if ( exists $args->{'to_file'} ) { Rex::Config->set_log_filename( $args->{'to_file'} ); } elsif ( exists $args->{'to_syslog'} ) { Rex::Config->set_log_facility( $args->{'to_syslog'} ); } else { Rex::Config->set_log_filename('rex.log'); } } =head2 needs($package [, @tasks]) With I you can define dependencies between tasks. The "needed" tasks will be called with the same server configuration as the calling task. I will not execute before, around and after hooks. =over 4 =item Depend on all tasks in a given package. Depend on all tasks in the package MyPkg. All tasks will be called with the server I. task "mytask", "server1", sub { needs MyPkg; }; =item Depend on a single task in a given package. Depend on the I task in the package MyPkg. The I task will be called with the server I. task "mytask", "server1", sub { needs MyPkg "uname"; }; =item To call tasks defined in the Rexfile from within a module task "mytask", "server1", sub { needs main "uname"; }; =back =cut sub needs { my ( $self, @args ) = @_; # if no namespace is given, use the current one if ( ref($self) eq "ARRAY" ) { @args = @{$self}; ($self) = caller; } if ( $self eq "main" ) { $self = ""; # Tasks in main namespace are really registered in Rex::CLI } my $tl = Rex::TaskList->create(); my @maybe_tasks_to_run; if($self) { @maybe_tasks_to_run = $tl->get_all_tasks(qr{^\Q$self\E:[A-Za-z0-9_\-]+$}); } else { @maybe_tasks_to_run = $tl->get_all_tasks(qr{^[A-Za-z0-9_\-]+$}); } if ( !@args && !@maybe_tasks_to_run ) { @args = ($self); ($self) = caller; $self = "" if($self =~ m/^(Rex::CLI|main)$/); } if ( ref( $args[0] ) eq "ARRAY" ) { @args = @{ $args[0] }; } Rex::Logger::debug("need to call tasks from $self"); my @tasks_to_run; if($self) { @tasks_to_run = $tl->get_all_tasks(qr{^\Q$self\E:[A-Za-z0-9_\-]+$}); } else { @tasks_to_run = $tl->get_all_tasks(qr{^[A-Za-z0-9_\-]+$}); } my $run_list = Rex::RunList->instance; my $current_task = $run_list->current_task; my %task_opts = $current_task->get_opts; my @task_args = $current_task->get_args; if($self) { my $suffix = $self; $suffix =~ s/::/:/g; @args = map { $_ = "$suffix:$_" } @args; } for my $task (@tasks_to_run) { my $task_o = $tl->get_task($task); my $task_name = $task_o->name; my $suffix = $self . ":"; if ( @args && grep ( /^\Q$task_name\E$/, @args ) ) { Rex::Logger::debug( "Calling " . $task_o->name ); $task_o->run( "", params => \@task_args, args => \%task_opts ); } elsif ( !@args ) { Rex::Logger::debug( "Calling " . $task_o->name ); $task_o->run( "", params => \@task_args, args => \%task_opts ); } } } # register needs in main namespace { my ($caller_pkg) = caller(1); if ( !$caller_pkg ) { ($caller_pkg) = caller(0); } if ( $caller_pkg && ( $caller_pkg eq "Rex::CLI" || $caller_pkg eq "main" ) ) { no strict 'refs'; *{"main::needs"} = \&needs; use strict; } }; =head2 include Module::Name Include a module without registering its tasks. include qw/ Module::One Module::Two /; =cut sub include { my (@mods) = @_; my $old_val = $dont_register_tasks; $dont_register_tasks = 1; for my $mod (@mods) { eval "require $mod"; if ($@) { die $@; } } $dont_register_tasks = $old_val; } =head2 environment($name => $code) Define an environment. With environments one can use the same task for different hosts. For example if you want to use the same task on your integration-, test- and production servers. # define default user/password user "root"; password "foobar"; pass_auth; # define default frontend group containing only testwww01. group frontend => "testwww01"; # define live environment, with different user/password # and a frontend server group containing www01, www02 and www03. environment live => sub { user "root"; password "livefoo"; pass_auth; group frontend => "www01", "www02", "www03"; }; # define stage environment with default user and password. but with # a own frontend group containing only stagewww01. environment stage => sub { group frontend => "stagewww01"; }; task "prepare", group => "frontend", sub { say run "hostname"; }; Calling this task I will execute on testwww01. Calling this task with I will execute on www01, www02, www03. Calling this task I will execute on stagewww01. You can call the function within a task to get the current environment. task "prepare", group => "frontend", sub { if(environment() eq "dev") { say "i'm in the dev environment"; } }; If no I<-E> option is passed on the command line, the default environment (named 'default') will be used. =cut sub environment { if (@_) { my ( $name, $code ) = @_; $environments->{$name} = { code => $code, description => $current_desc || '', name => $name, }; $current_desc = ""; if ( Rex::Config->get_environment eq $name ) { &$code(); } return 1; } else { return Rex::Config->get_environment || "default"; } } =head2 LOCAL(&) With the LOCAL function you can do local commands within a task that is defined to work on remote servers. task "mytask", "server1", "server2", sub { # this will call 'uptime' on the servers 'server1' and 'server2' say run "uptime"; # this will call 'uptime' on the local machine. LOCAL { say run "uptime"; }; }; =cut sub LOCAL (&) { my $cur_conn = Rex::get_current_connection(); my $local_connect = Rex::Interface::Connection->create("Local"); my $old_global_sudo = $Rex::GLOBAL_SUDO; $Rex::GLOBAL_SUDO = 0; Rex::push_connection( { conn => $local_connect, ssh => 0, server => $cur_conn->{server}, cache => Rex::Interface::Cache->create(), task => [ task() ], reporter => Rex::Report->create( Rex::Config->get_report_type ), notify => Rex::Notify->new(), } ); my $ret = $_[0]->(); Rex::pop_connection(); $Rex::GLOBAL_SUDO = $old_global_sudo; return $ret; } =head2 path(@path) Set the execution path for all commands. path "/bin", "/sbin", "/usr/bin", "/usr/sbin", "/usr/pkg/bin", "/usr/pkg/sbin"; =cut sub path { Rex::Config->set_path( [@_] ); } =head2 set($key, $value) Set a configuration parameter. These variables can be used in templates as well. set database => "db01"; task "prepare", sub { my $db = get "database"; }; Or in a template DB: <%= $::database %> The following list of configuration parameters are Rex specific: =over =back =cut sub set { my ( $key, @value ) = @_; Rex::Config->set( $key, @value ); } =head2 get($key, $value) Get a configuration parameter. set database => "db01"; task "prepare", sub { my $db = get "database"; }; Or in a template DB: <%= $::database %> =cut sub get { my ($key) = @_; if ( ref($key) eq "Rex::Value" ) { return $key->value; } return Rex::Config->get($key); } =head2 before($task => sub {}) Run code before executing the specified task. The special taskname 'ALL' can be used to run code before all tasks. If called repeatedly, each sub will be appended to a list of 'before' functions. In this hook you can overwrite the server to which the task will connect to. The second argument is a reference to the server object that will be used for the connection. Note: must come after the definition of the specified task before mytask => sub { my ($server, $server_ref, $cli_args) = @_; run "vzctl start vm$server"; }; =cut sub before { my ( $task, $code ) = @_; if ( $task eq "ALL" ) { $task = qr{.*}; } my ( $package, $file, $line ) = caller; Rex::TaskList->create() ->modify( 'before', $task, $code, $package, $file, $line ); } =head2 after($task => sub {}) Run code after the task is finished. The special taskname 'ALL' can be used to run code after all tasks. If called repeatedly, each sub will be appended to a list of 'after' functions. Note: must come after the definition of the specified task after mytask => sub { my ($server, $failed, $cli_args) = @_; if($failed) { say "Connection to $server failed."; } run "vzctl stop vm$server"; }; =cut sub after { my ( $task, $code ) = @_; if ( $task eq "ALL" ) { $task = qr{.*}; } my ( $package, $file, $line ) = caller; Rex::TaskList->create() ->modify( 'after', $task, $code, $package, $file, $line ); } =head2 around($task => sub {}) Run code before and after the task is finished. The special taskname 'ALL' can be used to run code around all tasks. If called repeatedly, each sub will be appended to a list of 'around' functions. In this hook you can overwrite the server to which the task will connect to. The second argument is a reference to the server object that will be used for the connection. Note: must come after the definition of the specified task around mytask => sub { my ($server, $server_ref, $cli_args, $position) = @_; unless($position) { say "Before Task\n"; } else { say "After Task\n"; } }; =cut sub around { my ( $task, $code ) = @_; if ( $task eq "ALL" ) { $task = qr{.*}; } my ( $package, $file, $line ) = caller; Rex::TaskList->create() ->modify( 'around', $task, $code, $package, $file, $line ); } =head2 before_task_start($task => sub {}) Run code before executing the specified task. This gets executed only once for a task. The special taskname 'ALL' can be used to run code before all tasks. If called repeatedly, each sub will be appended to a list of 'before_task_start' functions. Note: must come after the definition of the specified task before_task_start mytask => sub { # do some things }; =cut sub before_task_start { my ( $task, $code ) = @_; if ( $task eq "ALL" ) { $task = qr{.*}; } my ( $package, $file, $line ) = caller; Rex::TaskList->create() ->modify( 'before_task_start', $task, $code, $package, $file, $line ); } =head2 after_task_finished($task => sub {}) Run code after the task is finished (and after the ssh connection is terminated). This gets executed only once for a task. The special taskname 'ALL' can be used to run code before all tasks. If called repeatedly, each sub will be appended to a list of 'after_task_finished' functions. Note: must come after the definition of the specified task after_task_finished mytask => sub { # do some things }; =cut sub after_task_finished { my ( $task, $code ) = @_; if ( $task eq "ALL" ) { $task = qr{.*}; } my ( $package, $file, $line ) = caller; Rex::TaskList->create() ->modify( 'after_task_finished', $task, $code, $package, $file, $line ); } =head2 logformat($format) You can define the logging format with the following parameters. %D - Appends the current date yyyy-mm-dd HH:mm:ss %h - The target host %p - The pid of the running process %l - Loglevel (INFO or DEBUG) %s - The Logstring Default is: [%D] %l - %s =cut sub logformat { my ($format) = @_; $Rex::Logger::format = $format; } sub log_format { logformat(@_); } =head2 connection This function returns the current connection object. task "foo", group => "baz", sub { say "Current Server: " . connection->server; }; =cut sub connection { return Rex::get_current_connection()->{conn}; } =head2 cache This function returns the current cache object. =cut sub cache { my ($type) = @_; if ( !$type ) { return Rex::get_cache(); } Rex::Config->set_cache_type($type); } =head2 profiler Returns the profiler object for the current connection. =cut sub profiler { my $c_profiler = Rex::get_current_connection()->{"profiler"}; unless ($c_profiler) { $c_profiler = $profiler || Rex::Profiler->new; $profiler = $c_profiler; } return $c_profiler; } =head2 report($switch, $type) This function will initialize the reporting. report -on => "YAML"; =cut sub report { my ( $str, $type ) = @_; $type ||= "Base"; Rex::Config->set_report_type($type); if ( $str && ( $str eq "-on" || $str eq "on" ) ) { Rex::Config->set_do_reporting(1); return; } elsif ( $str && ( $str eq "-off" || $str eq "off" ) ) { Rex::Config->set_do_reporting(0); return; } return Rex::get_current_connection()->{reporter}; } =head2 source_global_profile(0|1) If this option is set, every run() command will first source /etc/profile before getting executed. =cut sub source_global_profile { my ($source) = @_; Rex::Config->set_source_global_profile($source); } =head2 last_command_output This function returns the output of the last "run" command. On a debian system this example will return the output of I. task "mytask", "myserver", sub { install "foobar"; say last_command_output(); }; =cut sub last_command_output { return $Rex::Commands::Run::LAST_OUTPUT->[0]; } =head2 case($compare, $option) This is a function to compare a string with some given options. task "mytask", "myserver", sub { my $ntp_service = case operating_sytem, { Debian => "ntp", default => "ntpd", }; my $ntp_service = case operating_sytem, { qr{debian}i => "ntp", default => "ntpd", }; my $ntp_service = case operating_sytem, { qr{debian}i => "ntp", default => sub { return "foo"; }, }; }; =cut sub case { my ( $compare, $option ) = @_; my $to_return = undef; if ( exists $option->{$compare} ) { $to_return = $option->{$compare}; } else { for my $key ( keys %{$option} ) { if ( $compare =~ $key ) { $to_return = $option->{$key}; last; } } } if ( exists $option->{default} && !$to_return ) { $to_return = $option->{default}; } if ( ref $to_return eq "CODE" ) { $to_return = &$to_return(); } return $to_return; } =head2 set_executor_for($type, $executor) Set the executor for a special type. This is primary used for the upload_and_run helper function. set_executor_for perl => "/opt/local/bin/perl"; =cut sub set_executor_for { Rex::Config->set_executor_for(@_); } =head2 tmp_dir($tmp_dir) Set the tmp directory on the remote host to store temporary files. =cut sub tmp_dir { Rex::Config->set_tmp_dir(@_); } =head2 inspect($varRef) This function dumps the contents of a variable to STDOUT. task "mytask", "myserver", sub { my $myvar = { name => "foo", sys => "bar", }; inspect $myvar; }; =cut my $depth = 0; sub _dump_hash { my ( $hash, $option ) = @_; unless ( $depth == 0 && exists $option->{no_root} && $option->{no_root} ) { print "{\n"; } $depth++; for my $key ( keys %{$hash} ) { _print_indent($option); if ( exists $option->{prepend_key} ) { print $option->{prepend_key}; } print "$key" . ( exists $option->{key_value_sep} ? $option->{key_value_sep} : " => " ); _dump_var( $hash->{$key} ); } $depth--; _print_indent($option); unless ( $depth == 0 && exists $option->{no_root} && $option->{no_root} ) { print "}\n"; } } sub _dump_array { my ( $array, $option ) = @_; unless ( $depth == 0 && exists $option->{no_root} && $option->{no_root} ) { print "[\n"; } $depth++; for my $itm ( @{$array} ) { _print_indent($option); _dump_var($itm); } $depth--; _print_indent($option); unless ( $depth == 0 && exists $option->{no_root} && $option->{no_root} ) { print "]\n"; } } sub _print_indent { my ($option) = @_; unless ( $depth == 1 && exists $option->{no_root} && $option->{no_root} ) { print " " x $depth; } } sub _dump_var { my ( $var, $option ) = @_; if ( ref $var eq "HASH" ) { _dump_hash( $var, $option ); } elsif ( ref $var eq "ARRAY" ) { _dump_array( $var, $option ); } else { if ( defined $var ) { $var =~ s/\n/\\n/gms; $var =~ s/\r/\\r/gms; $var =~ s/'/\\'/gms; print "'$var'\n"; } else { print "no value\n"; } } } sub inspect { _dump_var(@_); } ######### private functions sub evaluate_hostname { my $str = shift; return unless $str; # e.g. server[0..4/2].domain.com my ( $start, $rule, $end ) = $str =~ m{ ^ ([0-9\.\w\-:]*) # prefix (e.g. server) \[ # rule -> 0..4 | 0..4/2 | 0,2,4 ( (?: \d+ \.\. \d+ # range-rule e.g. 0..4 (?:\/ \d+ )? # step for range-rule ) | (?: (?: \d+ (?:,\s*)? ) | (?: \d+ \.\. \d+ (?: \/ \d+ )? (?:,\s*)? ) )+ # list ) \] # end of rule ([0-9\w\.\-:]+)? # suffix (e.g. .domain.com) $ }xms; if ( !defined $rule ) { return $str; } my @ret; if ( $rule =~ m/,/ ) { @ret = _evaluate_hostname_list( $start, $rule, $end ); } else { @ret = _evaluate_hostname_range( $start, $rule, $end ); } return @ret; } sub _evaluate_hostname_range { my ( $start, $rule, $end ) = @_; my ( $from, $to, $step ) = $rule =~ m{(\d+) \.\. (\d+) (?:/(\d+))?}xms; $end ||= ''; $step ||= 1; my $strict_length = 0; if ( length $from == length $to ) { $strict_length = length $to; } my @ret = (); for ( ; $from <= $to ; $from += $step ) { my $format = "%0" . $strict_length . "i"; push @ret, $start . sprintf( $format, $from ) . $end; } return @ret; } sub _evaluate_hostname_list { my ( $start, $rule, $end ) = @_; my @values = split /,\s*/, $rule; $end ||= ''; my @ret; for my $value (@values) { if ( $value =~ m{\d+\.\.\d+(?:/\d+)?} ) { push @ret, _evaluate_hostname_range( $start, $value, $end ); } else { push @ret, "$start$value$end"; } } return @ret; } sub exit { Rex::Logger::info("Exiting Rex..."); Rex::Logger::info("Cleaning up..."); Rex::global_sudo(0); unlink("$::rexfile.lock") if ($::rexfile); CORE::exit( $_[0] || 0 ); } sub get_environment { my ( $class, $env ) = @_; if ( exists $environments->{$env} ) { return $environments->{$env}; } } sub get_environments { my $class = shift; my @ret = sort { $a cmp $b } keys %{$environments}; return @ret; } =head2 sayformat($format) You can define the format of the say() function. %D - The current date yyyy-mm-dd HH:mm:ss %h - The target host %p - The pid of the running process %s - The Logstring You can also define the following values: default - the default behaviour. asis - will print every single parameter in its own line. This is useful if you want to print the output of a command. =cut sub sayformat { my ($format) = @_; Rex::Config->set_say_format($format); } sub say_format { sayformat(@_); } sub say { my (@data) = @_; return unless defined $_[0]; my $format = Rex::Config->get_say_format; if ( !defined $format || $format eq "default" ) { print @_, "\n"; return; } if ( $format eq "asis" ) { print join( "\n", @_ ); return; } for my $line (@data) { print _format_string( $format, $line ) . "\n"; } } # %D - Date # %h - Host # %s - Logstring sub _format_string { my ( $format, $line ) = @_; my $date = _get_timestamp(); my $host = Rex::get_current_connection() ? Rex::get_current_connection()->{conn}->server : ""; my $pid = $$; $format =~ s/\%D/$date/gms; $format =~ s/\%h/$host/gms; $format =~ s/\%s/$line/gms; $format =~ s/\%p/$pid/gms; return $format; } sub _get_timestamp { my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) = localtime(time); $mon++; $year += 1900; return "$year-" . sprintf( "%02i", $mon ) . "-" . sprintf( "%02i", $mday ) . " " . sprintf( "%02i", $hour ) . ":" . sprintf( "%02i", $min ) . ":" . sprintf( "%02i", $sec ); } sub TRUE { return 1; } sub FALSE { return 0; } sub make(&) { return $_[0]; } 1; Resource.pm100644001751001751 610513232707066 14503 0ustar00janjan000000000000Rex-1.6.0/lib/Rex# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Resource; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Rex::Constants; our @CURRENT_RES; sub is_inside_resource { ref $CURRENT_RES[-1] ? 1 : 0 } sub get_current_resource { $CURRENT_RES[-1] } sub new { my $that = shift; my $proto = ref($that) || $that; my $self = {@_}; bless( $self, $proto ); $self->{__status__} = "unchanged"; return $self; } sub name { (shift)->{name}; } sub display_name { (shift)->{display_name}; } sub type { (shift)->{type}; } sub call { my ( $self, $name, %params ) = @_; if ( ref $name eq "HASH" ) { # multiple resource call for my $n ( keys %{$name} ) { $self->call( $n, %{ $name->{$n} } ); } return; } push @CURRENT_RES, $self; $self->set_all_parameters(%params); $self->{res_name} = $name; $self->{res_ensure} = $params{ensure} ||= present; Rex::get_current_connection()->{reporter} ->report_resource_start( type => $self->display_name, name => $name ); my $failed = 0; eval { $self->{cb}->( \%params ); 1; } or do { Rex::Logger::info( $@, "error" ); Rex::Logger::info( "Resource execution failed: $name", "error" ); $failed = 1; }; if ( $self->was_updated ) { Rex::get_current_connection()->{reporter}->report( changed => 1, failed => $failed, message => $self->message, ); } else { Rex::get_current_connection()->{reporter}->report( changed => 0, failed => $failed, message => $self->display_name . " not changed.", ); } if ( exists $params{on_change} && $self->was_updated ) { $params{on_change}->( $self->{__status__} ); } Rex::get_current_connection()->{reporter} ->report_resource_end( type => $self->display_name, name => $name ); pop @CURRENT_RES; } sub was_updated { my ($self) = @_; if ( $self->changed || $self->created || $self->removed ) { return 1; } return 0; } sub changed { my ( $self, $changed ) = @_; if ( defined $changed ) { $self->{__status__} = "changed"; } else { return ( $self->{__status__} eq "changed" ? 1 : 0 ); } } sub created { my ( $self, $created ) = @_; if ( defined $created ) { $self->{__status__} = "created"; } else { return ( $self->{__status__} eq "created" ? 1 : 0 ); } } sub removed { my ( $self, $removed ) = @_; if ( defined $removed ) { $self->{__status__} = "removed"; } else { return ( $self->{__status__} eq "removed" ? 1 : 0 ); } } sub message { my ( $self, $message ) = @_; if ( defined $message ) { $self->{message} = $message; } else { return ( $self->{message} || ( $self->display_name . " changed." ) ); } } sub set_parameter { my ( $self, $key, $value ) = @_; $self->{__res_parameters__}->{$key} = $value; } sub set_all_parameters { my ( $self, %params ) = @_; $self->{__res_parameters__} = \%params; } sub get_all_parameters { my ($self) = @_; return $self->{__res_parameters__}; } 1; Hardware.pm100644001751001751 360013232707066 14446 0ustar00janjan000000000000Rex-1.6.0/lib/Rex# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: =head1 NAME Rex::Hardware - Base Class for hardware / information gathering =head1 DESCRIPTION This module is the base class for hardware/information gathering. =head1 SYNOPSIS use Rex::Hardware; my %host_info = Rex::Hardware->get(qw/ Host /); my %all_info = Rex::Hardware->get(qw/ All /); =head1 CLASS METHODS =cut package Rex::Hardware; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Rex::Logger; require Rex::Args; =head2 get(@modules) Returns a hash with the wanted information. task "get-info", "server1", sub { %hw_info = Rex::Hardware->get(qw/ Host Network /); }; Or if you want to get all information task "get-all-info", "server1", sub { %hw_info = Rex::Hardware->get(qw/ All /); }; Available modules: =over 4 =item Host =item Kernel =item Memory =item Network =item Swap =item VirtInfo =back =cut my %HW_PROVIDER; sub register_hardware_provider { my ( $class, $service_name, $service_class ) = @_; $HW_PROVIDER{"\L$service_name"} = $service_class; return 1; } sub get { my ( $class, @modules ) = @_; my %hardware_information; if ( "all" eq "\L$modules[0]" ) { @modules = qw(Host Kernel Memory Network Swap VirtInfo); push( @modules, keys(%HW_PROVIDER) ); } for my $mod_string (@modules) { Rex::Commands::profiler()->start("hardware: $mod_string"); my $mod = "Rex::Hardware::$mod_string"; if ( exists $HW_PROVIDER{$mod_string} ) { $mod = $HW_PROVIDER{$mod_string}; } Rex::Logger::debug("Loading $mod"); eval "use $mod"; if ($@) { Rex::Logger::info("$mod not found."); Rex::Logger::debug("$@"); next; } $hardware_information{$mod_string} = $mod->get(); Rex::Commands::profiler()->end("hardware: $mod_string"); } return %hardware_information; } 1; Template.pm100644001751001751 1101313232707066 14501 0ustar00janjan000000000000Rex-1.6.0/lib/Rex# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: =head1 NAME Rex::Template - Simple Template Engine. =head1 DESCRIPTION This is a simple template engine for configuration files. =head1 SYNOPSIS my $template = Rex::Template->new; print $template->parse($content, \%template_vars); =head1 EXPORTED FUNCTIONS =cut package Rex::Template; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Rex::Config; use Rex::Logger; require Rex::Args; our $DO_CHOMP = 0; our $BE_LOCAL = 1; sub function { my ( $class, $name, $code ) = @_; no strict 'refs'; *{ $class . "::" . $name } = $code; use strict; } sub new { my $that = shift; my $proto = ref($that) || $that; my $self = {@_}; bless( $self, $proto ); return $self; } sub parse { my $self = shift; my $data = shift; my $vars = {}; if ( ref( $_[0] ) eq "HASH" ) { $vars = shift; } else { $vars = {@_}; } my $new_data; my $___r = ""; my $do_chomp = 0; $new_data = join( "\n", map { my ( $code, $type, $text ) = ( $_ =~ m/(\<%)*([+=])*(.+)%\>/s ); if ($code) { my $pcmd = substr( $text, -1 ); if ( $pcmd eq "-" ) { $text = substr( $text, 0, -1 ); $do_chomp = 1; } my ( $var_type, $var_name ) = ( $text =~ m/([\$])::([a-zA-Z0-9_]+)/ ); if ( $var_name && !ref( $vars->{$var_name} ) && !$BE_LOCAL ) { $text =~ s/([\$])::([a-zA-Z0-9_]+)/$1\{\$$2\}/g; } elsif ( $var_name && !ref( $vars->{$var_name} ) && $BE_LOCAL ) { $text =~ s/([\$])::([a-zA-Z0-9_]+)/$1$2/g; } else { $text =~ s/([\$])::([a-zA-Z0-9_]+)/\$$2/g; } if ( $type && $type =~ m/^[+=]$/ ) { "\$___r .= $text;"; } else { $text; } } else { my $chomped = $_; if ( $DO_CHOMP || $do_chomp ) { chomp $chomped; $do_chomp = 0; } '$___r .= "' . _quote($chomped) . '";'; } } split( /(\<%.*?%\>)/s, $data ) ); eval { no strict 'refs'; no strict 'vars'; for my $var ( keys %{$vars} ) { Rex::Logger::debug("Registering: $var"); unless ( ref( $vars->{$var} ) ) { $$var = \$vars->{$var}; } else { $$var = $vars->{$var}; } } if ( $BE_LOCAL == 1 ) { my $var_data = ' return sub { my $___r = ""; my ( '; my @code_values; for my $var ( keys %{$vars} ) { my $new_var = _normalize_var_name($var); Rex::Logger::debug("Registering local: $new_var"); $var_data .= '$' . $new_var . ", \n"; push( @code_values, $vars->{$var} ); } $var_data .= '$this_is_really_nothing) = @_;'; $var_data .= "\n"; $var_data .= $new_data; $var_data .= "\n"; $var_data .= ' return $___r;'; $var_data .= "\n};"; Rex::Logger::debug("BE_LOCAL==1"); my %args = Rex::Args->getopts; if ( defined $args{'d'} && $args{'d'} > 1 ) { Rex::Logger::debug($var_data); } my $tpl_code = eval($var_data); if ($@) { Rex::Logger::info($@); } $___r = $tpl_code->(@code_values); } else { Rex::Logger::debug("BE_LOCAL==0"); my %args = Rex::Args->getopts; if ( defined $args{'d'} && $args{'d'} > 1 ) { Rex::Logger::debug($new_data); } $___r = eval($new_data); if ($@) { Rex::Logger::info($@); } } # undef the vars for my $var ( keys %{$vars} ) { $$var = undef; } }; if ( !$___r ) { Rex::Logger::info( "It seems that there was an error processing the template", "warn" ); Rex::Logger::info( "because the result is empty.", "warn" ); die("Error processing template"); } return $___r; } sub _quote { my ($str) = @_; $str =~ s/\\/\\\\/g; $str =~ s/"/\\"/g; $str =~ s/\@/\\@/g; $str =~ s/\%/\\%/g; $str =~ s/\$/\\\$/g; return $str; } sub _normalize_var_name { my ($input) = @_; $input =~ s/[^A-Za-z0-9_]/_/g; return $input; } =head2 is_defined($variable, $default_value) This function will check if $variable is defined. If yes, it will return the value of $variable, otherwise it will return $default_value. You can use this function inside your templates. ServerTokens <%= is_defined($::server_tokens, "Prod") %> =cut sub is_defined { my ( $check_var, $default ) = @_; if ( defined $check_var ) { return $check_var; } return $default; } 1; Profiler.pm100644001751001751 322313232707066 14474 0ustar00janjan000000000000Rex-1.6.0/lib/Rex# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Profiler; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Time::HiRes qw(gettimeofday tv_interval); sub new { my $that = shift; my $proto = ref($that) || $that; my $self = {@_}; bless( $self, $proto ); $self->{__data} = {}; return $self; } sub start { my ( $self, $info ) = @_; push( @{ $self->{__data}->{$info} }, { start => [gettimeofday] } ); } sub end { my ( $self, $info ) = @_; return unless ( $self->{__data}->{$info}->[-1] ); my $data = $self->{__data}->{$info}->[-1]; $data->{end} = [gettimeofday]; $data->{duration} = tv_interval( $data->{start}, $data->{end} ); $self->{__data}->{$info}->[-1] = $data; } sub report { my ($self) = @_; for my $info ( keys %{ $self->{__data} } ) { print "# $info (count: " . scalar( @{ $self->{__data}->{$info} } ) . ")\n"; print " Timings:\n"; my ( $max, $min, $avg, $all ); for my $entry ( @{ $self->{__data}->{$info} } ) { if ( !$max || $max < $entry->{duration} ) { $max = $entry->{duration}; } if ( !$min || $min > $entry->{duration} ) { $min = $entry->{duration}; } $all += $entry->{duration}; } $avg = $all / scalar( @{ $self->{__data}->{$info} } ); print " min: $min / max: $max / avg: $avg / all: $all\n"; print " Overview:\n"; for my $entry ( @{ $self->{__data}->{$info} } ) { print " " . $entry->{duration} . "\n"; } print "--------------------------------------------------------------------------------\n"; } } 1; TaskList.pm100644001751001751 264313232707066 14455 0ustar00janjan000000000000Rex-1.6.0/lib/Rex# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::TaskList; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Data::Dumper; use Rex::Config; use Rex::Logger; use Rex::Interface::Executor; use vars qw(%tasks); # will be set from Rex::Transaction::transaction() our $task_list = {}; sub create { my ($class) = @_; # create only one object if ( ref($task_list) =~ m/^Rex::TaskList::/ ) { Rex::Logger::debug( "Returning existing distribution class of type: " . ref($task_list) ); return $task_list; } my $type = Rex::Config->get_distributor; Rex::Logger::debug("Creating new distribution class of type: $type"); my $class_name = "Rex::TaskList::$type"; eval "use $class_name"; if ($@) { die("TaskList module not found: $@"); } $task_list = $class_name->new; Rex::Logger::debug( "new distribution class of type " . ref($task_list) . " created." ); return $task_list; } sub run { my ( $class, $task_name, %option ) = @_; $class = ref $class ? $class : $class->create; my $task = ref $task_name ? $task_name : $class->get_task($task_name); my $old_task = $class->{__current_task__}; $class->{__current_task__} = $task; $_->($task) for @{ $task->{before_task_start} }; $class->run( $task, %option ); $_->($task) for @{ $task->{after_task_finished} }; $class->{__current_task__} = $old_task; } Exporter.pm100644001751001751 147613232707066 14532 0ustar00janjan000000000000Rex-1.6.0/lib/Rex# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Exporter; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Data::Dumper; our @EXPORT; no strict 'refs'; sub import { my ( $mod_to_register, %option ) = @_; my ( $mod_to_register_in, $file, $line ) = caller; if ( exists $option{register_in} && $option{register_in} ) { $mod_to_register_in = $option{register_in}; } my $no_import = ""; if ( exists $option{"-no"} && $option{"-no"} ) { $no_import = "," . join( ",", @{ $option{"-no"} } ) . ","; } for my $reg_func ( @{ $_[0] . "::EXPORT" } ) { if ( $no_import =~ m/,$reg_func,/ ) { next; } *{ $mod_to_register_in . "::" . $reg_func } = *{ $mod_to_register . "::" . $reg_func }; } } use strict; 1; SuSE.pm100644001751001751 466113232707066 14261 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Pkg# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Pkg::SuSE; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Rex::Helper::Run; use Rex::Pkg::Base; use base qw(Rex::Pkg::Base); sub new { my $that = shift; my $proto = ref($that) || $that; my $self = $proto->SUPER::new(@_); bless( $self, $proto ); if ( Rex::has_feature_version('1.5') ) { $self->{commands} = { install => 'zypper -n install %s', install_version => 'zypper -n install $pkg-%s', update_system => 'zypper -n --no-refresh up', dist_update_system => 'zypper -n --no-refresh up', remove => 'zypper -n remove %s', update_package_db => 'zypper -n ref -fd', }; } else { $self->{commands} = { install => 'zypper -n install %s', install_version => 'zypper -n install $pkg-%s', update_system => 'zypper -n up', dist_update_system => 'zypper -n up', remove => 'zypper -n remove %s', update_package_db => 'zypper --no-gpg-checks -n ref -fd', }; } return $self; } sub bulk_install { my ( $self, $packages_aref, $option ) = @_; delete $option->{version}; # makes no sense to specify the same version for several packages $self->update( "@{$packages_aref}", $option ); return 1; } sub get_installed { my ($self) = @_; my @lines = i_run 'rpm -qa --nosignature --nodigest --qf "%{NAME} %|EPOCH?{%{EPOCH}}:{0}| %{VERSION} %{RELEASE} %{ARCH}\n"'; my @pkg; for my $line (@lines) { if ( $line =~ m/^([^\s]+)\s([^\s]+)\s([^\s]+)\s([^\s]+)\s(.*)$/ ) { push( @pkg, { name => $1, epoch => $2, version => $3, release => $4, arch => $5, } ); } } return @pkg; } sub add_repository { my ( $self, %data ) = @_; i_run "zypper addrepo -f -n " . $data{"name"} . " " . $data{"url"} . " " . $data{"name"}, fail_ok => 1; if ( $? == 4 ) { if ( Rex::Config->get_do_reporting ) { return { changed => 0 }; } } if ( $? != 0 ) { die( "Error adding repository " . $data{name} ); } } sub rm_repository { my ( $self, $name ) = @_; i_run "zypper removerepo $name", fail_ok => 1; if ( $? != 0 ) { die("Error removing repository $name"); } if ( Rex::Config->get_do_reporting ) { return { changed => 1 }; } } 1; Arch.pm100644001751001751 561213232707066 14314 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Pkg# # (c) Harm Müller # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Pkg::Arch; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Rex::Helper::Run; use Rex::Commands::File; use Rex::Commands::Fs; use Rex::Pkg::Base; use base qw(Rex::Pkg::Base); sub new { my $that = shift; my $proto = ref($that) || $that; my $self = $proto->SUPER::new(@_); bless( $self, $proto ); if ( Rex::has_feature_version('1.5') ) { $self->{commands} = { install => 'pacman --noprogressbar --noconfirm --needed -S %s', install_version => 'pacman --noprogressbar --noconfirm --needed -S %s', # makes no sense to specify the package version update_system => 'pacman --noprogressbar --noconfirm -Su', dist_update_system => 'pacman --noprogressbar --noconfirm -Su', remove => 'pacman --noprogressbar --noconfirm -Rs %s', update_package_db => 'pacman --noprogressbar -Sy', }; } else { $self->{commands} = { install => 'pacman --noprogressbar --noconfirm --needed -S %s', install_version => 'pacman --noprogressbar --noconfirm --needed -S %s', # makes no sense to specify the package version update_system => 'pacman --noprogressbar --noconfirm -Syu', dist_update_system => 'pacman --noprogressbar --noconfirm -Syu', remove => 'pacman --noprogressbar --noconfirm -Rs %s', update_package_db => 'pacman --noprogressbar -Sy', }; } return $self; } sub bulk_install { my ( $self, $packages_aref, $option ) = @_; delete $option->{version}; # makes no sense to specify the same version for several packages $self->update( "@{$packages_aref}", $option ); return 1; } sub get_installed { my ( $self, $pkg ) = @_; my ( @pkgs, $name, $version, $release, $arch ); my $pkg_query = 'pacman -Qi | egrep "^Name|^Version|^Architecture"'; if ( defined($pkg) ) { $pkg_query .= " " . $pkg; } my @installed_packages = i_run $pkg_query; for my $line (@installed_packages) { if ( $line =~ m/^Name\s+:\s+([\S]+)$/ ) { $name = $1; } elsif ( $line =~ m/^Version\s+:\s+([\S]+)-([\S]+)$/ ) { $version = $1; $release = $2; } elsif ( $line =~ m/^Architecture\s+:\s+([\S]+)$/ ) { $arch = $1; } if ( defined($name) && defined($version) && defined($release) && defined($arch) ) { push( @pkgs, { name => $name, version => $version, release => $release, arch => $arch, } ); $name = $version = $release = $arch = undef; } } return @pkgs; } sub add_repository { Rex::Logger::info( "no suitable repo management use template/file for pacman.conf"); return 1; } sub rm_repository { Rex::Logger::info( "no suitable repo management use template/file for pacman.conf"); return 1; } 1; Base.pm100644001751001751 1267213232707066 14335 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Pkg# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Pkg::Base; use strict; use warnings; use Rex::Helper::Run; use Rex::Interface::Exec; our $VERSION = '1.6.0'; # VERSION sub new { my $that = shift; my $proto = ref($that) || $that; my $self = {@_}; bless( $self, $proto ); return $self; } sub is_installed { my ( $self, $pkg, $option ) = @_; my $version = $option->{version}; Rex::Logger::debug( "Checking if $pkg" . ( $version ? "-$version" : "" ) . " is installed" ); my @pkg_info = grep { $_->{name} eq $pkg } $self->get_installed(); @pkg_info = grep { $_->{version} eq $version } @pkg_info if defined $version; unless (@pkg_info) { Rex::Logger::debug( "$pkg" . ( $version ? "-$version" : "" ) . " is NOT installed." ); return 0; } Rex::Logger::debug( "$pkg" . ( $version ? "-$version" : "" ) . " is installed." ); return 1; } sub install { my ( $self, $pkg, $option ) = @_; if ( $self->is_installed( $pkg, $option ) ) { Rex::Logger::info("$pkg is already installed"); return 1; } $self->update( $pkg, $option ); return 1; } sub update { my ( $self, $pkg, $option ) = @_; my $version = $option->{'version'} || ''; Rex::Logger::debug( "Installing $pkg" . ( $version ? "-$version" : "" ) ); my $cmd = sprintf $self->{commands}->{install}, $pkg; if ( exists $option->{version} ) { $cmd = sprintf $self->{commands}->{install_version}, $pkg, $option->{version}; } my $f = i_run $cmd, fail_ok => 1; unless ( $? == 0 ) { Rex::Logger::info( "Error installing $pkg.", "warn" ); Rex::Logger::debug($f); die("Error installing $pkg"); } Rex::Logger::debug("$pkg successfully installed."); return 1; } sub update_system { my ( $self, %option ) = @_; # default is to update packages $option{update_packages} = 1 if ( !exists $option{update_packages} ); if ( !exists $self->{commands}->{update_system} ) { Rex::Logger::debug("Not supported under this OS"); return; } if ( $option{update_metadata} ) { $self->update_pkg_db(%option); } if ( $option{update_packages} ) { my $cmd = $self->{commands}->{update_system}; my $f = i_run $cmd, fail_ok => 1; unless ( $? == 0 ) { Rex::Logger::debug($f); die("Error updating system"); } } if ( $option{dist_upgrade} ) { if ( !exists $self->{commands}->{dist_update_system} ) { Rex::Logger::debug("dist upgrades not supported under this OS"); } else { my $cmd = $self->{commands}->{dist_update_system}; my $f = i_run $cmd, fail_ok => 1; unless ( $? == 0 ) { Rex::Logger::debug($f); die("Error dist-updating system"); } } } Rex::Logger::debug("System successfully updated."); return 1; } sub remove { my ( $self, $pkg ) = @_; Rex::Logger::debug("Removing $pkg"); my $cmd = sprintf $self->{commands}->{remove}, $pkg; my $f = i_run $cmd, fail_ok => 1; unless ( $? == 0 ) { Rex::Logger::info( "Error removing $pkg.", "warn" ); Rex::Logger::debug($f); die("Error removing $pkg"); } Rex::Logger::debug("$pkg successfully removed."); return 1; } sub purge { my ( $self, $pkg ) = @_; return 1 if ( !exists $self->{commands}->{purge} ); Rex::Logger::debug("Purging $pkg"); my $cmd = sprintf $self->{commands}->{purge}, $pkg; my $f = i_run $cmd, fail_ok => 1; unless ( $? == 0 ) { Rex::Logger::info( "Error purging $pkg.", "warn" ); Rex::Logger::debug($f); die("Error purging $pkg"); } Rex::Logger::debug("$pkg successfully purged."); return 1; } sub update_pkg_db { my ( $self, %option ) = @_; if ( !exists $self->{commands}->{update_package_db} ) { Rex::Logger::debug("Not supported under this OS"); return; } my $cmd = $self->{commands}->{update_package_db}; i_run $cmd, fail_ok => 1; if ( $? != 0 ) { die("Error updating package database"); } } sub bulk_install { Rex::Logger::info( "Installing bulk packages not supported on this platform. Falling back to one by one method", "warn" ); my ( $self, $packages_aref, $option ) = @_; for my $pkg_to_install ( @{$packages_aref} ) { $self->install( $pkg_to_install, $option ); } return 1; } sub add_repository { my ( $self, %data ) = @_; Rex::Logger::debug("Not supported under this OS"); } sub rm_repository { my ( $self, $name ) = @_; Rex::Logger::debug("Not supported under this OS"); } sub diff_package_list { my ( $self, $list1, $list2 ) = @_; my @old_installed = @{$list1}; my @new_installed = @{$list2}; my @modifications; # getting modifications of old packages OLD_PKG: for my $old_pkg (@old_installed) { NEW_PKG: for my $new_pkg (@new_installed) { if ( $old_pkg->{name} eq $new_pkg->{name} ) { # flag the package as found in new package list, # to find removed and new ones. $old_pkg->{found} = 1; $new_pkg->{found} = 1; if ( $old_pkg->{version} ne $new_pkg->{version} ) { push @modifications, { %{$new_pkg}, action => 'updated' }; } next OLD_PKG; } } } # getting removed old packages push @modifications, map { $_->{action} = 'removed'; $_ } grep { !exists $_->{found} } @old_installed; # getting new packages push @modifications, map { $_->{action} = 'installed'; $_ } grep { !exists $_->{found} } @new_installed; map { delete $_->{found} } @modifications; return @modifications; } 1; VBox.pm100644001751001751 1705713232707066 14352 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Box# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: =head1 NAME Rex::Box::VBox - Rex/Boxes VirtualBox Module =head1 DESCRIPTION This is a Rex/Boxes module to use VirtualBox VMs. =head1 EXAMPLES To use this module inside your Rexfile you can use the following commands. use Rex::Commands::Box; set box => "VBox"; task "prepare_box", sub { box { my ($box) = @_; $box->name("mybox"); $box->url("http://box.rexify.org/box/ubuntu-server-12.10-amd64.ova"); $box->network(1 => { type => "nat", }); $box->network(1 => { type => "bridged", bridge => "eth0", }); $box->forward_port(ssh => [2222, 22]); $box->share_folder(myhome => "/home/myuser"); $box->auth( user => "root", password => "box", ); $box->setup("setup_task"); }; }; If you want to use a YAML file you can use the following template. type: VBox vms: vmone: url: http://box.rexify.org/box/ubuntu-server-12.10-amd64.ova forward_port: ssh: - 2222 - 22 share_folder: myhome: /home/myhome setup: setup_task And then you can use it the following way in your Rexfile. use Rex::Commands::Box init_file => "file.yml"; task "prepare_vms", sub { boxes "init"; }; =head1 HEADLESS MODE It is also possible to run VirtualBox in headless mode. This only works on Linux and MacOS. If you want to do this you can use the following option at the top of your I. set box_options => { headless => TRUE }; =head1 METHODS See also the Methods of Rex::Box::Base. This module inherits all methods of it. =cut package Rex::Box::VBox; use strict; use warnings; use Data::Dumper; use Rex::Box::Base; use Rex::Commands -no => [qw/auth/]; use Rex::Commands::Fs; use Rex::Commands::Virtualization; use Rex::Commands::SimpleCheck; our $VERSION = '1.6.0'; # VERSION BEGIN { LWP::UserAgent->use; } use Time::HiRes qw(tv_interval gettimeofday); use File::Basename qw(basename); use base qw(Rex::Box::Base); set virtualization => "VBox"; $|++; ################################################################################ # BEGIN of class methods ################################################################################ =head2 new(name => $vmname) Constructor if used in OO mode. my $box = Rex::Box::VBox->new(name => "vmname"); =cut sub new { my $class = shift; my $proto = ref($class) || $class; my $self = $proto->SUPER::new(@_); bless( $self, ref($class) || $class ); if ( exists $self->{options} && exists $self->{options}->{headless} && $self->{options}->{headless} ) { set virtualization => { type => "VBox", headless => TRUE }; } $self->{get_ip_count} = 0; return $self; } sub import_vm { my ($self) = @_; # check if machine already exists my $vms = vm list => "all"; my $vm_exists = 0; for my $vm ( @{$vms} ) { if ( $vm->{name} eq $self->{name} ) { Rex::Logger::debug("VM already exists. Don't import anything."); $vm_exists = 1; } } if ( !$vm_exists ) { # if not, create it $self->_download; my $filename = basename( $self->{url} ); Rex::Logger::info("Importing VM ./tmp/$filename"); vm import => $self->{name}, file => "./tmp/$filename", %{$self}; #unlink "./tmp/$filename"; } my $vminfo = vm info => $self->{name}; # check if networksettings should be set if ( exists $self->{__network} && $vminfo->{VMState} ne "running" ) { my $option = $self->{__network}; for my $nic_no ( keys %{$option} ) { if ( $option->{$nic_no}->{type} ) { Rex::Logger::debug( "Setting network type (dev: $nic_no) to: " . $option->{$nic_no}->{type} ); vm option => $self->{name}, "nic$nic_no" => $option->{$nic_no}->{type}; if ( $option->{$nic_no}->{type} eq "bridged" ) { $option->{$nic_no}->{bridge} = select_bridge() if ( !$option->{$nic_no}->{bridge} ); Rex::Logger::debug( "Setting network bridge (dev: $nic_no) to: " . ( $option->{$nic_no}->{bridge} || "eth0" ) ); vm option => $self->{name}, "bridgeadapter$nic_no" => ( $option->{$nic_no}->{bridge} || "eth0" ); } } if ( $option->{$nic_no}->{driver} ) { Rex::Logger::debug( "Setting network driver (dev: $nic_no) to: " . $option->{$nic_no}->{driver} ); vm option => $self->{name}, "nictype$nic_no" => $option->{$nic_no}->{driver}; } } } if ( exists $self->{__forward_port} && $vminfo->{VMState} ne "running" ) { # remove all forwards vm forward_port => $self->{name}, delete => -all; # add forwards vm forward_port => $self->{name}, add => $self->{__forward_port}; } # shared folder if ( exists $self->{__shared_folder} && $vminfo->{VMState} ne "running" ) { vm share_folder => $self->{name}, add => $self->{__shared_folder}; } if ( $vminfo->{VMState} ne "running" ) { $self->start; } $self->{info} = vm guestinfo => $self->{name}; } sub select_bridge { my $bridges = vm "bridge"; my $ifname; if ( @$bridges == 1 ) { Rex::Logger::debug( "Only one bridged interface available. Using it by default."); $ifname = $bridges->[0]->{name}; } elsif ( @$bridges > 1 ) { for ( my $i = 0 ; $i < @$bridges ; $i++ ) { my $bridge = $bridges->[$i]; next if ( $bridge->{status} =~ /^down$/i ); local $Rex::Logger::format = "%s"; Rex::Logger::info( $i + 1 . " $bridge->{name}" ); } my $choice; do { print "What interface should network bridge to? "; chomp( $choice = ); $choice = int($choice); } while ( !$choice ); $ifname = $bridges->[ $choice - 1 ]->{name}; } return $ifname; } =head2 share_folder(%option) Creates a shared folder inside the VM with the content from a folder from the Host machine. This only works with VirtualBox. $box->share_folder( name => "/path/on/host", name2 => "/path_2/on/host", ); =cut sub share_folder { my ( $self, %option ) = @_; $self->{__shared_folder} = \%option; } =head2 info Returns a hashRef of vm information. =cut sub info { my ($self) = @_; $self->ip; my $vm_info = vm info => $self->{name}; # get forwarded ports my @forwarded_ports = grep { m/^Forwarding/ } keys %{$vm_info}; my %forward_port; for my $fwp (@forwarded_ports) { my ( $name, $proto, $host_ip, $host_port, $vm_ip, $vm_port ) = split( /,/, $vm_info->{$fwp} ); $forward_port{$name} = [ $host_port, $vm_port ]; } $self->forward_port(%forward_port); return $self->{info}; } =head2 ip This method return the ip of a vm on which the ssh daemon is listening. =cut sub ip { my ($self) = @_; $self->{info} ||= vm guestinfo => $self->{name}; if ( scalar keys %{ $self->{info} } == 0 ) { return; } my $server = $self->{info}->{net}->[0]->{ip}; if ( $self->{__forward_port} && $self->{__forward_port}->{ssh} && !Rex::is_local() ) { $server = connection->server . ":" . $self->{__forward_port}->{ssh}->[0]; } elsif ( $self->{__forward_port} && $self->{__forward_port}->{ssh} && Rex::is_local() ) { $server = "127.0.0.1:" . $self->{__forward_port}->{ssh}->[0]; } $self->{info}->{ip} = $server; if ( !$server ) { sleep 1; $self->{get_ip_count}++; if ( $self->{get_ip_count} >= 30 ) { die "Can't get ip of VM."; } my $ip = $self->ip; if ($ip) { $self->{get_ip_count} = 0; return $ip; } } return $server; } 1; Base.pm100644001751001751 2005613232707066 14337 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Box# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: =head1 NAME Rex::Box::Base - Rex/Boxes Base Module =head1 DESCRIPTION This is a Rex/Boxes base module. =head1 METHODS These methods are shared across all other Rex::Box modules. =cut package Rex::Box::Base; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Rex::Commands -no => [qw/auth/]; use Rex::Helper::Run; use Rex::Commands::Fs; use Rex::Commands::Virtualization; use Rex::Commands::SimpleCheck; use Rex::Helper::IP; BEGIN { LWP::UserAgent->use; } use Time::HiRes qw(tv_interval gettimeofday); use File::Basename qw(basename); use Data::Dumper; sub new { my $that = shift; my $proto = ref($that) || $that; my $self = {@_}; bless( $self, $proto ); # default auth for rex boxes $self->{__auth} = { user => Rex::Config->get_user(), password => Rex::Config->get_password(), private_key => Rex::Config->get_private_key(), public_key => Rex::Config->get_public_key(), }; # for box this is needed, because we have changing ips Rex::Config->set_openssh_opt( StrictHostKeyChecking => "no", UserKnownHostsFile => "/dev/null", LogLevel => "QUIET" ); return $self; } =head2 info Returns a hashRef of vm information. =cut sub info { my ($self) = @_; return $self->{info}; } =head2 name($vmname) Sets the name of the virtual machine. =cut sub name { my ( $self, $name ) = @_; $self->{name} = $name; } =head2 setup(@tasks) Sets the tasks that should be executed as soon as the VM is available through SSH. =cut =head2 storage('path/to/vm/disk') Sets the disk path of the virtual machine. Works only on KVM =cut sub storage { my ( $self, $folder ) = @_; $self->{storage_path} = $folder; } sub setup { my ( $self, @tasks ) = @_; $self->{__tasks} = \@tasks; } =head2 import_vm() This method must be overwritten by the implementing class. =cut sub import_vm { my ($self) = @_; die("This method must be overwritten."); } =head2 stop() Stops the VM. =cut sub stop { my ($self) = @_; $self->info; vm shutdown => $self->{name}; } =head2 destroy() Destroy the VM. =cut sub destroy { my ($self) = @_; $self->info; vm destroy => $self->{name}; } =head2 start() Starts the VM. =cut sub start { my ($self) = @_; $self->info; vm start => $self->{name}; } =head2 ip() Return the ip:port to which rex will connect to. =cut sub ip { die("Must be implemented by box class.") } =head2 status() Returns the status of a VM. Valid return values are "running" and "stopped". =cut sub status { my ($self) = @_; return vm status => $self->{name}; } =head2 provision_vm([@tasks]) Executes the given tasks on the VM. =cut sub provision_vm { my ( $self, @tasks ) = @_; if ( !@tasks ) { @tasks = @{ $self->{__tasks} } if ( exists $self->{__tasks} ); } $self->wait_for_ssh(); for my $task (@tasks) { my $task_o = Rex::TaskList->create()->get_task($task); if ( !$task_o ) { die "Task $task not found."; } $task_o->set_auth( %{ $self->{__auth} } ); Rex::Commands::set( "box_object", $self ); $task_o->run( $self->ip ); Rex::Commands::set( "box_object", undef ); } } =head2 cpus($count) Set the amount of CPUs for the VM. =cut sub cpus { my ( $self, $cpus ) = @_; $self->{cpus} = $cpus; } =head2 memory($memory_size) Sets the memory of a VM in megabyte. =cut sub memory { my ( $self, $mem ) = @_; $self->{memory} = $mem; } =head2 network(%option) Configure the network for a VM. Currently it supports 2 modes: I and I. Currently it supports only one network card. $box->network( 1 => { type => "nat", }, } $box->network( 1 => { type => "bridged", bridge => "eth0", }, ); =cut sub network { my ( $self, %option ) = @_; $self->{__network} = \%option; } =head2 forward_port(%option) Set ports to be forwarded to the VM. This is not supported by all Box providers. $box->forward_port( name => [$from_host_port, $to_vm_port], name2 => [$from_host_port_2, $to_vm_port_2], ... ); =cut sub forward_port { my ( $self, %option ) = @_; $self->{__forward_port} = \%option; } =head2 list_boxes List all available boxes. =cut sub list_boxes { my ($self) = @_; my $vms = vm list => "all"; return @{$vms}; } =head2 url($url) The URL where to download the Base VM Image. You can use self-made images or prebuild images from http://box.rexify.org/. =cut sub url { my ( $self, $url, $force ) = @_; $self->{url} = $url; $self->{force} = $force; } =head2 auth(%option) Configure the authentication to the VM. $box->auth( user => $user, password => $password, private_key => $private_key, public_key => $public_key, ); =cut sub auth { my ( $self, %auth ) = @_; if (%auth) { $self->{__auth} = \%auth; } else { return $self->{__auth}; } } =head2 options(%option) Addition options for boxes $box->options( opt1 => $val1, opt2 => $val2, ); =cut sub options { my ( $self, %opt ) = @_; if (%opt) { $self->{__options} = \%opt; } else { return $self->{__options}; } } sub wait_for_ssh { my ( $self, $ip, $port ) = @_; if ( !$ip ) { ( $ip, $port ) = Rex::Helper::IP::get_server_and_port( $self->ip, 22 ); } print "Waiting for SSH to come up on $ip:$port."; while ( !is_port_open( $ip, $port ) ) { print "."; sleep 1; } print "\n"; } sub _download { my ($self) = @_; my $filename = basename( $self->{url} ); my $force = $self->{force} || FALSE; my $fs = Rex::Interface::Fs->create; if ( $fs->is_file("./tmp/$filename") ) { Rex::Logger::info( "File already downloaded. Please remove the file ./tmp/$filename if you want to download a fresh copy." ); } else { $force = TRUE; } if ($force) { Rex::Logger::info("Downloading $self->{url} to ./tmp/$filename"); mkdir "tmp"; if ( Rex::is_local() ) { my $ua = LWP::UserAgent->new(); $ua->env_proxy; my $final_data = ""; my $current_size = 0; my $current_modulo = 0; my $start_time = [ gettimeofday() ]; open( my $fh, ">", "./tmp/$filename" ) or die("Failed to open ./tmp/$filename for writing: $!"); binmode $fh; my $resp = $ua->get( $self->{url}, ':content_cb' => sub { my ( $data, $response, $protocol ) = @_; $current_size += length($data); my $content_length = $response->header("content-length"); print $fh $data; my $current_time = [ gettimeofday() ]; my $time_diff = tv_interval( $start_time, $current_time ); my $bytes_per_seconds = $current_size / $time_diff; my $mbytes_per_seconds = $bytes_per_seconds / 1024 / 1024; my $mbytes_current = $current_size / 1024 / 1024; my $mbytes_total = $content_length / 1024 / 1024; my $left_bytes = $content_length - $current_size; my $time_one_byte = $time_diff / $current_size; my $time_all_bytes = $time_one_byte * ( $content_length - $current_size ); if ( ( ( $current_size / ( 1024 * 1024 ) ) % ( 1024 * 1024 ) ) > $current_modulo ) { print "."; $current_modulo++; if ( $current_modulo % 10 == 0 ) { printf( ". %.2f MBytes/s (%.2f MByte / %.2f MByte) %.2f secs left\n", $mbytes_per_seconds, $mbytes_current, $mbytes_total, $time_all_bytes ); } } } ); close($fh); if ( $resp->is_success ) { print " done.\n"; } else { Rex::Logger::info( "Error downloading box image.", "warn" ); unlink "./tmp/$filename"; } } else { i_exec "wget", "-c", "-qO", "./tmp/$filename", $self->{url}; if ( $? != 0 ) { die( "Downloading of $self->{url} failed. Please verify if wget is installed and if you have the right permissions to download this box." ); } } } } 1; before_all_tasks.t100644001751001751 102413232707066 14774 0ustar00janjan000000000000Rex-1.6.0/tuse strict; use warnings; use lib 't/lib'; use Test::More; use Test::Deep; use Rex::Commands; use Rex::Commands::Run; use t::tasks::cowbefore; $::QUIET = 1; my $task_list = Rex::TaskList->create; my @task_names = $task_list->get_tasks; cmp_deeply \@task_names, [qw/t:tasks:cowbefore:roundup/], "found task"; for my $tn (@task_names) { my $before = $task_list->get_task($tn)->get_data->{before}; ok($before); is( ( scalar @$before ), 1, $tn ); is( $before->[0]->(), 'yo', $tn ) if (@$before); } done_testing(); Constants.pm100644001751001751 101513232707066 14663 0ustar00janjan000000000000Rex-1.6.0/lib/Rex# # (c) Jan Gehring # # vim: set ts=3 sw=3 tw=0: # vim: set expandtab: package Rex::Constants; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION require Rex::Exporter; use base qw(Rex::Exporter); use vars qw(@EXPORT); @EXPORT = qw(present absent latest running started stopped); sub present { return "present"; } sub absent { return "absent"; } sub latest { return "latest"; } sub running { return "running"; } sub started { return "started"; } sub stopped { return "stopped"; } 1; Inventory.pm100644001751001751 1260413232707066 14732 0ustar00janjan000000000000Rex-1.6.0/lib/Rex# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Inventory; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Rex::Inventory::DMIDecode; use Rex::Inventory::Hal; use Rex::Inventory::Proc; use Rex::Commands::Network; use Rex::Commands::Run; use Rex::Commands::Gather; use Rex::Commands::LVM; use Rex::Commands::Fs; use Rex::Inventory::HP::ACU; use Data::Dumper; sub new { my $that = shift; my $proto = ref($that) || $that; my $self = {@_}; bless( $self, $proto ); return $self; } sub get { my ($self) = @_; my $dmi = Rex::Inventory::DMIDecode->new; my ( $base_board, $bios, @cpus, @dimms, @mem_arrays, $sys_info ); $base_board = $dmi->get_base_board; $bios = $dmi->get_bios; @cpus = $dmi->get_cpus; @dimms = $dmi->get_memory_modules; @mem_arrays = $dmi->get_memory_arrays; $sys_info = $dmi->get_system_information; my $hal = {}; my ( @net_devs, @storage, @volumes ); eval { $hal = Rex::Inventory::Hal->new; @net_devs = $hal->get_network_devices; @storage = $hal->get_storage_devices; @volumes = $hal->get_storage_volumes; }; eval { if ( scalar @cpus == 0 ) { # get cpu info from /proc if ( is_dir("/proc") ) { Rex::Logger::info( "Got no cpu information from dmidecode. Falling back to /proc/cpuinfo" ); my $proc_i = Rex::Inventory::Proc->new; @cpus = @{ $proc_i->get_cpus }; } } }; my @routes = route; my @netstat = netstat; my $default_gw = default_gateway; my ( @pvs, @vgs, @lvs ); eval { @pvs = pvs; @vgs = vgs; @lvs = lvs; }; my @raid_controller; eval { if ( my $hp_raid = Rex::Inventory::HP::ACU->get() ) { # hp raid entdeckt for my $key ( keys %{$hp_raid} ) { my %raid_shelfs; for my $shelf ( keys %{ $hp_raid->{$key}->{"array"} } ) { my $shelf_data = $hp_raid->{$key}->{"array"}->{$shelf}; my @raid_logical_drives; for my $l_drive ( keys %{ $hp_raid->{$key}->{"array"}->{$shelf}->{"logical_drive"} } ) { my $l_drive_data = $hp_raid->{$key}->{"array"}->{$shelf}->{"logical_drive"} ->{$l_drive}; my ($size) = ( $l_drive_data->{"size"} =~ m/^([0-9\.]+)/ ); my $multi = 1024 * 1024 * 1024; if ( $l_drive_data->{"size"} =~ m/TB$/ ) { $multi *= 1024; } push( @raid_logical_drives, { status => ( $l_drive_data->{"status"} eq "OK" ? 1 : 0 ), raid_level => $l_drive_data->{"fault_tolerance"}, size => sprintf( "%i", $size * $multi ), dev => $l_drive_data->{"disk_name"}, shelf => $shelf, } ); } $raid_shelfs{$shelf} = { type => $shelf_data->{"interface_type"}, status => ( $shelf_data->{"status"} eq "OK" ? 1 : 0 ), logical_drives => \@raid_logical_drives, }; } push( @raid_controller, { type => $hp_raid->{$key}->{"description"}, model => $hp_raid->{$key}->{"model"}, serial_number => $hp_raid->{$key}->{"serial_number"}, cache_status => ( $hp_raid->{$key}->{"cache_status"} eq "OK" ? 1 : 0 ), shelfs => \%raid_shelfs, } ); } } }; my ($fusion_inventory_xmlref); if ( can_run("fusioninventory-agent") ) { require XML::Simple; my $xml = XML::Simple->new; my $fusion_inventory = run "fusioninventory-agent --stdout 2>/dev/null"; $fusion_inventory_xmlref = $xml->XMLin($fusion_inventory); } return { base_board => ( $base_board ? $base_board->get_all() : {} ), bios => $bios->get_all(), system_info => $sys_info->get_all(), cpus => sub { my $ret = []; push( @{$ret}, ( ref $_ ne "HASH" ? $_->get_all() : $_ ) ) for @cpus; return $ret; } ->(), dimms => sub { my $ret = []; push( @{$ret}, $_->get_all() ) for @dimms; return $ret; } ->(), mem_arrays => sub { my $ret = []; push( @{$ret}, $_->get_all() ) for @mem_arrays; return $ret; } ->(), net => sub { my $ret = []; push( @{$ret}, $_->get_all() ) for @net_devs; return $ret; } ->(), storage => sub { my $ret = []; push( @{$ret}, $_->get_all() ) for @storage; return $ret; } ->(), volumes => sub { my $ret = []; push( @{$ret}, $_->get_all() ) for @volumes; return $ret; } ->(), raid => { controller => \@raid_controller, }, lvm => { physical_volumes => \@pvs, volume_groups => \@vgs, logical_volumes => \@lvs, }, configuration => { network => { routes => \@routes, current_connections => \@netstat, default_gateway => $default_gw, current_configuration => network_interfaces(), }, host => { name => [ run "hostname -s" ]->[0], domain => [ run "hostname -d" || qw() ]->[0], kernel => [ run "uname -r" || qw() ]->[0], }, }, fusion_inventory => $fusion_inventory_xmlref, }; } 1; Helper000755001751001751 013232707066 13433 5ustar00janjan000000000000Rex-1.6.0/lib/RexIP.pm100644001751001751 156213232707066 14445 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Helper# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Helper::IP; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Data::Validate::IP 'is_ipv4', 'is_ipv6'; sub is_ip { my ($ip) = @_; if ( is_ipv4($ip) || is_ipv6($ip) ) { return 1; } return 0; } sub get_server_and_port { my ( $server, $default_port ) = @_; my ( $rs, $rp ); if ( $server =~ m/:(\d+)$/ && !is_ipv6($server) ) { return split( /:/, $server ); } if ( $server =~ m/\/(\d+)$/ ) { return split( /\/(\d+)$/, $server ); } if ( is_ip($server) ) { if ( is_ipv6($server) ) { ( $rs, $rp ) = split( /\//, $server ); } else { ( $rs, $rp ) = split( /[:\/]/, $server ); } } else { ( $rs, $rp ) = split( /[:\/]/, $server ); } $rp ||= $default_port; return ( $rs, $rp ); } 1; Cron000755001751001751 013232707066 13115 5ustar00janjan000000000000Rex-1.6.0/lib/RexBase.pm100644001751001751 1304213232707066 14505 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Cron# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Cron::Base; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Rex::Logger; use Rex::Commands; use Rex::Commands::File; use Rex::Commands::Fs; use Rex::Helper::Run; use Data::Dumper; use Rex::Helper::Path; sub new { my $that = shift; my $proto = ref($that) || $that; my $self = {@_}; bless( $self, $proto ); return $self; } sub list { my ($self) = @_; return @{ $self->{cron} }; } sub list_jobs { my ($self) = @_; my @jobs = @{ $self->{cron} }; my @ret = map { { line => $_->{line}, %{ $_->{cron} } } } grep { $_->{type} eq "job" } @jobs; } sub list_envs { my ($self) = @_; my @jobs = @{ $self->{cron} }; my @ret = grep { $_->{type} eq "env" } @jobs; } sub add { my ( $self, %config ) = @_; %config = $self->_create_defaults(%config); my $new_cron = sprintf( "%s %s %s %s %s %s", $config{"minute"}, $config{"hour"}, $config{"day_of_month"}, $config{"month"}, $config{"day_of_week"}, $config{"command"}, ); my $dupe = grep { $_->{line} eq $new_cron } @{ $self->{cron} }; if ($dupe) { Rex::Logger::debug("Job \"$new_cron\" already installed, skipping."); return 0; } push( @{ $self->{cron} }, { type => "job", line => $new_cron, cron => \%config, } ); return 1; } sub add_env { my ( $self, $name, $value ) = @_; my $env_index = 0; my $exists = 0; for my $env ( $self->list_envs ) { if ( $env->{name} eq "$name" ) { if ( $env->{value} ne "\"$value\"" ) { Rex::Logger::debug("Environment variable changed : $name"); $self->delete_env($env_index); } else { Rex::Logger::debug( "Environment variable already exists with same value: $name=$value"); $exists = 1; } } $env_index++; } if ( $exists == 0 ) { unshift( @{ $self->{cron} }, { type => "env", line => "$name=\"$value\"", name => $name, value => $value, } ); } } sub delete_job { my ( $self, $num ) = @_; my @jobs = $self->list_jobs; my $i = 0; my $to_delete; for my $j ( @{ $self->{cron} } ) { if ( $j->{line} eq $jobs[$num]->{line} ) { $to_delete = $i; last; } $i++; } unless ( defined $to_delete ) { die("Cron Entry $num not found."); } $self->delete($to_delete); } sub delete_env { my ( $self, $num ) = @_; my @jobs = $self->list_envs; my $i = 0; my $to_delete; for my $j ( @{ $self->{cron} } ) { if ( $j->{line} eq $jobs[$num]->{line} ) { $to_delete = $i; last; } $i++; } unless ( defined $to_delete ) { die("Cron Entry $num not found."); } $self->delete($to_delete); } sub delete { my ( $self, $num ) = @_; splice( @{ $self->{cron} }, $num, 1 ); } # returns a filename where the new cron is written to # after that the cronfile must be activated sub write_cron { my ($self) = @_; my $rnd_file = get_tmp_file; my @lines = map { $_->{line} } @{ $self->{cron} }; my $fh = file_write $rnd_file; $fh->write( join( "\n", @lines ) . "\n" ); $fh->close; return $rnd_file; } sub activate_user_cron { my ( $self, $file, $user ) = @_; $user = undef if $user eq &_whoami; my $command = 'crontab'; $command .= " -u $user" if defined $user; i_run "$command $file"; unlink $file; } sub read_user_cron { my ( $self, $user ) = @_; $user = undef if $user eq &_whoami; my $command = 'crontab -l'; $command .= " -u $user" if defined $user; $command .= ' 2> /dev/null'; my @lines = i_run $command, fail_ok => 1; $self->parse_cron(@lines); } sub parse_cron { my ( $self, @lines ) = @_; chomp @lines; my @cron; for my $line (@lines) { # comment if ( $line =~ m/^#/ ) { push( @cron, { type => "comment", line => $line, } ); } # empty line elsif ( $line =~ m/^\s*$/ ) { push( @cron, { type => "empty", line => $line, } ); } # job elsif ( $line =~ m/^(@|\*|[0-9])/ ) { my ( $min, $hour, $day, $month, $dow, $cmd ) = split( /\s+/, $line, 6 ); push( @cron, { type => "job", line => $line, cron => { minute => $min, hour => $hour, day_of_month => $day, month => $month, day_of_week => $dow, command => $cmd, }, } ); } elsif ( $line =~ m/=/ ) { my ( $name, $value ) = split( /=/, $line, 2 ); $name =~ s/^\s+//; $name =~ s/\s+$//; $value =~ s/^\s+//; $value =~ s/\s+$//; push( @cron, { type => "env", line => $line, name => $name, value => $value, } ); } else { Rex::Logger::debug("Error parsing cron line: $line"); next; } } $self->{cron} = \@cron; return @cron; } sub _create_defaults { my ( $self, %config ) = @_; $config{"minute"} = "*" unless defined $config{minute}; $config{"hour"} = "*" unless defined $config{hour}; $config{"day_of_month"} = "*" unless defined $config{day_of_month}; $config{"month"} = "*" unless defined $config{month}; $config{"day_of_week"} = "*" unless defined $config{day_of_week}; $config{"command"} ||= "false"; return %config; } sub _whoami { return i_run q(perl -e 'print scalar getpwuid($<)'); } 1; SunOS.pm100644001751001751 453113232707066 14445 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Pkg# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Pkg::SunOS; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Rex::Helper::Run; use Rex::Commands::File; use Rex::Pkg::Base; use base qw(Rex::Pkg::Base); sub new { my $that = shift; my $proto = ref($that) || $that; my $self = $proto->SUPER::new(@_); bless( $self, $proto ); $self->{commands} = {}; return $self; } sub update { my ( $self, $pkg, $option ) = @_; my $version = $option->{'version'} || ''; Rex::Logger::debug("Version option not supported."); Rex::Logger::debug("Installing $pkg / $version"); my $cmd = "pkgadd "; if ( !exists $option->{"source"} ) { die("You have to specify the source."); } $cmd .= " -a " . $option->{"adminfile"} if ( $option->{"adminfile"} ); $cmd .= " -r " . $option->{"responsefile"} if ( $option->{"responsefile"} ); $cmd .= " -d " . $option->{"source"}; $cmd .= " -n " . $pkg; my $f = i_run( $cmd, fail_ok => 1 ); unless ( $? == 0 ) { Rex::Logger::info( "Error installing $pkg.", "warn" ); Rex::Logger::debug($f); die("Error installing $pkg"); } Rex::Logger::debug("$pkg successfully installed."); return 1; } sub remove { my ( $self, $pkg, $option ) = @_; Rex::Logger::debug("Removing $pkg"); my $cmd = "pkgrm -n "; $cmd .= " -a " . $option->{"adminfile"} if ( $option->{"adminfile"} ); my $f = i_run( $cmd . " $pkg", fail_ok => 1 ); unless ( $? == 0 ) { Rex::Logger::info( "Error removing $pkg.", "warn" ); Rex::Logger::debug($f); die("Error removing $pkg"); } Rex::Logger::debug("$pkg successfully removed."); return 1; } sub get_installed { my ($self) = @_; my @lines = i_run "pkginfo -l"; my ( @pkg, %current ); for my $line (@lines) { if ( $line =~ m/^$/ ) { push( @pkg, {%current} ); next; } if ( $line =~ m/PKGINST:\s+([^\s]+)/ ) { $current{"name"} = $1; next; } if ( $line =~ m/VERSION:\s+([^\s]+)/ ) { my ( $version, $rev ) = split( /,/, $1 ); $current{"version"} = $version; $rev =~ s/^REV=// if ($rev); $current{"revision"} = $rev; next; } if ( $line =~ m/STATUS:\s+(.*?)$/ ) { $current{"status"} = ( $1 eq "completely installed" ? "installed" : $1 ); next; } } return @pkg; } 1; Fork000755001751001751 013232707066 13115 5ustar00janjan000000000000Rex-1.6.0/lib/RexTask.pm100644001751001751 130013232707066 14507 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Fork# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Fork::Task; use strict; use warnings; use POSIX ":sys_wait_h"; our $VERSION = '1.6.0'; # VERSION sub new { my $that = shift; my $proto = ref($that) || $that; my $self = {@_}; bless( $self, $proto ); $self->{'running'} = 0; return $self; } sub start { my ($self) = @_; $self->{'running'} = 1; $self->{pid} = fork; if ( !$self->{pid} ) { $self->{coderef}->($self); $self->{'running'} = 0; exit(); } } sub wait { my ($self) = @_; my $rpid = waitpid( $self->{pid}, &WNOHANG ); if ( $rpid == -1 ) { $self->{'running'} = 0; } return $rpid; } 1; CMDB000755001751001751 013232707066 12721 5ustar00janjan000000000000Rex-1.6.0/lib/RexBase.pm100644001751001751 64613232707066 14257 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/CMDB# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::CMDB::Base; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Rex::Helper::Path; sub new { my $that = shift; my $proto = ref($that) || $that; my $self = {@_}; bless( $self, $proto ); return $self; } sub _parse_path { my ( $self, $path ) = @_; return parse_path($path); } 1; YAML.pm100644001751001751 602113232707066 14160 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/CMDB# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::CMDB::YAML; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use base qw(Rex::CMDB::Base); use Rex::Commands -no => [qw/get/]; use Rex::Logger; use YAML; use Data::Dumper; use Hash::Merge qw/merge/; require Rex::Commands::File; sub new { my $that = shift; my $proto = ref($that) || $that; my $self = {@_}; $self->{merger} = Hash::Merge->new(); if ( !defined $self->{merge_behavior} ) { $self->{merger}->specify_behavior( { SCALAR => { SCALAR => sub { $_[0] }, ARRAY => sub { $_[0] }, HASH => sub { $_[0] }, }, ARRAY => { SCALAR => sub { $_[0] }, ARRAY => sub { $_[0] }, HASH => sub { $_[0] }, }, HASH => { SCALAR => sub { $_[0] }, ARRAY => sub { $_[0] }, HASH => sub { Hash::Merge::_merge_hashes( $_[0], $_[1] ) }, }, }, 'REX_DEFAULT', ); # first found value always wins $self->{merger}->set_behavior('REX_DEFAULT'); } else { if ( ref $self->{merge_behavior} eq 'HASH' ) { $self->{merger} ->specify_behavior( $self->{merge_behavior}, 'USER_DEFINED' ); $self->{merger}->set_behavior('USER_DEFINED'); } else { $self->{merger}->set_behavior( $self->{merge_behavior} ); } } bless( $self, $proto ); return $self; } sub get { my ( $self, $item, $server ) = @_; # first open $server.yml # second open $environment/$server.yml # third open $environment/default.yml # forth open default.yml my (@files); if ( !ref $self->{path} ) { my $env = environment; my $yaml_path = $self->{path}; @files = ( "$yaml_path/$env/$server.yml", "$yaml_path/$env/default.yml", "$yaml_path/$server.yml", "$yaml_path/default.yml" ); } elsif ( ref $self->{path} eq "CODE" ) { @files = $self->{path}->( $self, $item, $server ); } elsif ( ref $self->{path} eq "ARRAY" ) { @files = @{ $self->{path} }; } @files = map { $self->_parse_path($_) } @files; my $all = {}; Rex::Logger::debug( Dumper( \@files ) ); # configuration variables my $config_values = Rex::Config->get_all; my %template_vars; for my $key ( keys %{$config_values} ) { if ( !exists $template_vars{$key} ) { $template_vars{$key} = $config_values->{$key}; } } $template_vars{environment} = Rex::Commands::environment(); for my $file (@files) { Rex::Logger::debug("CMDB - Opening $file"); if ( -f $file ) { my $content = eval { local ( @ARGV, $/ ) = ($file); <>; }; my $t = Rex::Config->get_template_function(); $content .= "\n"; # for safety $content = $t->( $content, \%template_vars ); my $ref = YAML::Load($content); $all = $self->{merger}->merge( $all, $ref ); } } if ( !$item ) { return $all; } else { return $all->{$item}; } Rex::Logger::debug("CMDB - no item ($item) found"); return; } 1; Test000755001751001751 013232707066 13133 5ustar00janjan000000000000Rex-1.6.0/lib/RexBase.pm100644001751001751 1246513232707066 14533 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Test# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: =head1 NAME Rex::Test::Base - Basic Test Module =head1 DESCRIPTION This is a basic test module to test your code with the help of local VMs. You can place your tests in the "t" directory. =head1 EXAMPLE use Rex::Test::Base; use Data::Dumper; use Rex -base; test { my $t = shift; $t->name("ubuntu test"); $t->base_vm("http://box.rexify.org/box/ubuntu-server-12.10-amd64.ova"); $t->vm_auth(user => "root", password => "box"); $t->run_task("setup"); $t->has_package("vim"); $t->has_package("ntp"); $t->has_package("unzip"); $t->has_file("/etc/ntp.conf"); $t->has_service_running("ntp"); $t->has_content("/etc/passwd", qr{root:x:0:}ms); run "ls -l"; $t->ok($? == 0, "ls -l returns success."); $t->finish; }; 1; # last line =head1 METHODS =cut package Rex::Test::Base; use strict; use warnings; use base 'Test::Builder::Module'; our $VERSION = '1.6.0'; # VERSION require Rex::Commands; use Rex::Commands::Box; use Data::Dumper; use Carp; require Exporter; use base qw(Exporter); use vars qw(@EXPORT); @EXPORT = qw(test); =head2 new(name => $test_name) Constructor if used in OO mode. my $test = Rex::Test::Base->new(name => "test_name"); =cut sub new { my $that = shift; my $proto = ref($that) || $that; my $self = {@_}; bless( $self, $proto ); my ( $pkg, $file ) = caller(0); $self->{name} ||= $file; $self->{redirect_port} = 2222; $self->{memory} ||= 512; # default, in MB $self->{cpu} ||= 1; # default return $self; } =head2 name($name) The name of the test. A VM called $name will be created for each test. If the VM already exists, Rex will try to reuse it. =cut sub name { my ( $self, $name ) = @_; $self->{name} = $name; } =head2 memory($amount) The amount of memory the VM should use, in Megabytes. =cut sub memory { my ( $self, $memory ) = @_; $self->{memory} = $memory; } =head2 cpus($number) The number of CPUs the VM should use. =cut sub cpus { my ( $self, $cpus ) = @_; $self->{cpus} = $cpus; } =head2 vm_auth(%auth) Authentication options for the VM. It accepts the same parameters as Cauth()>. =cut sub vm_auth { my ( $self, %auth ) = @_; $self->{auth} = \%auth; } =head2 base_vm($vm) The URL to a base image to be used for the test VM. =cut sub base_vm { my ( $self, $vm ) = @_; $self->{vm} = $vm; } sub test(&) { my $code = shift; my $test = __PACKAGE__->new; $code->($test); } =head2 redirect_port($port) Redirect local $port to the VM's SSH port (default: 2222). =cut sub redirect_port { my ( $self, $port ) = @_; $self->{redirect_port} = $port; } =head2 run_task($task) The task to run on the test VM. You can run multiple tasks by passing an array reference. =cut sub run_task { my ( $self, $task ) = @_; # allow multiple calls to run_task() without setting up new box if ( $self->{box} ) { $self->{box}->provision_vm($task); return; } my $box; box { $box = shift; $box->name( $self->{name} ); $box->url( $self->{vm} ); $box->memory( $self->{memory} ); $box->cpus( $self->{cpus} ); $box->network( 1 => { type => "nat", } ); $box->forward_port( ssh => [ $self->{redirect_port}, 22 ] ); $box->auth( %{ $self->{auth} } ); if ( ref $task eq 'ARRAY' ) { $box->setup(@$task); } else { $box->setup($task); } }; $self->{box} = $box; # connect to the machine Rex::connect( server => $box->ip, %{ $self->{auth} }, ); } sub ok($;$) { my ( $self, $test, $msg ) = @_; my $tb = Rex::Test::Base->builder; $tb->ok( $test, $msg ); } sub like { my ( $self, $thing, $want, $name ) = @_; my $tb = Rex::Test::Base->builder; $tb->like( $thing, $want, $name ); } sub diag { my ( $self, $msg ) = @_; my $tb = Rex::Test::Base->builder; $tb->diag($msg); } sub finish { my $tb = Rex::Test::Base->builder; $tb->done_testing(); $tb->is_passing() ? print "PASS\n" : print "FAIL\n"; if ( !$tb->is_passing() ) { Rex::Test::push_exit("FAIL"); } $tb->reset(); Rex::pop_connection(); } =head1 TEST METHODS =head2 has_content($file, $regexp) Test if the content of $file matches against $regexp. =head2 has_dir($path) Test if $path is present and is a directory. =head2 has_file($file) Test if $file is present. =head2 has_package($package, $version) Test if $package is installed, optionally at $version. =head2 has_service_running($service) Test if $service is running. =head2 has_service_stopped($service) Test if $service is stopped. =head2 has_stat($file, $stat) Test if $file has properties described in hash reference $stat. List of supported checks: =over 4 =item group =item owner =back =cut our $AUTOLOAD; sub AUTOLOAD { my $self = shift or return; ( my $method = $AUTOLOAD ) =~ s{.*::}{}; if ( $method eq "DESTROY" ) { return; } my $real_method = $method; my $is_not = 0; if ( $real_method =~ m/^has_not_/ ) { $real_method =~ s/^has_not_/has_/; $is_not = 1; } my $pkg = __PACKAGE__ . "::$real_method"; eval "use $pkg"; if ($@) { confess "Error loading $pkg. No such test method."; } my $p = $pkg->new; if ($is_not) { $p->run_not_test(@_); } else { $p->run_test(@_); } } 1; User000755001751001751 013232707066 13132 5ustar00janjan000000000000Rex-1.6.0/lib/RexBase.pm100644001751001751 113213232707066 14477 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/User# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::User::Base; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION sub new { my $that = shift; my $proto = ref($that) || $that; my $self = {@_}; bless( $self, $proto ); return $self; } sub lock_password { # Overridden in those classes that implement it die "lock_password is not available on this operating system"; } sub unlock_password { # Overridden in those classes that implement it die "unlock_password is not available on this operating system"; } 1; Sudo000755001751001751 013232707066 13126 5ustar00janjan000000000000Rex-1.6.0/lib/RexFile.pm100644001751001751 421613232707066 14506 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Sudo# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: ###### DEPRECATED package Rex::Sudo::File; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Rex; use Rex::Commands; use Rex::Helper::Run; use Rex::Commands::Fs; use Rex::Helper::Path; use IO::File; sub open { my $that = shift; my $proto = ref($that) || $that; my $self = {}; $self->{mode} = shift; $self->{file} = shift; $self->{rndfile} = get_tmp_file; if ( my $sftp = Rex::get_sftp() ) { if ( $self->{mode} eq ">" ) { $self->{fh} = $sftp->open( $self->{rndfile}, O_WRONLY | O_CREAT | O_TRUNC ); } elsif ( $self->{mode} eq ">>" ) { cp( $self->{file}, $self->{rndfile} ); chmod( 666, $self->{rndfile} ); $self->{fh} = $sftp->open( $self->{rndfile}, O_WRONLY | O_APPEND ); my %stat = stat $self->{rndfile}; $self->{fh}->seek( $stat{size} ); } else { cp( $self->{file}, $self->{rndfile} ); chmod( 666, $self->{rndfile} ); $self->{fh} = $sftp->open( $self->{rndfile}, O_RDONLY ); } } else { $self->{fh} = IO::File->new; $self->{fh}->open( $self->{mode} . " " . $self->{rndfile} ); } bless( $self, $proto ); return $self; } sub write { my ( $self, $content ) = @_; if ( ref( $self->{fh} ) eq "Net::SSH2::File" ) { $self->{fh}->write($content); } else { $self->{fh}->print($content); } } sub seek { my ( $self, $offset ) = @_; if ( ref( $self->{fh} ) eq "Net::SSH2::File" ) { $self->{fh}->seek($offset); } else { $self->{fh}->seek( $offset, 0 ); } } sub read { my ( $self, $len ) = @_; $len ||= 64; my $buf; $self->{fh}->read( $buf, $len ); return $buf; } sub close { my ($self) = @_; return unless $self->{fh}; if ( ref( $self->{fh} ) eq "Net::SSH2::File" ) { $self->{fh} = undef; } else { $self->{fh}->close; } # use cat to not overwrite attributes/owner/group if ( $self->{mode} eq ">" || $self->{mode} eq ">>" ) { i_run "cat " . $self->{rndfile} . " >" . $self->{file}; rm( $self->{rndfile} ); } } sub DESTROY { my ($self) = @_; $self->close; } 1; param_lookup_cmdb.t100644001751001751 53413232707066 15140 0ustar00janjan000000000000Rex-1.6.0/t use Rex -base; use Rex::Resource; use Test::More; use Rex::CMDB; use Rex::Commands; $::QUIET = 1; set( cmdb => { type => "YAML", path => "t/cmdb", } ); use Test::More tests => 1; use Rex -base; task( "test1", sub { my $x = param_lookup( "name", "foo" ); is( $x, "defaultname", "got default value" ); } ); test1(); commands000755001751001751 013232707066 12754 5ustar00janjan000000000000Rex-1.6.0/tiptables.t100644001751001751 540213232707066 15105 0ustar00janjan000000000000Rex-1.6.0/t/commandsuse strict; use warnings; use Test::More tests => 32; use Rex::Commands::Iptables; my @iptables_list_1 = ( "# Generated by iptables-save v1.4.12 on Fri Nov 16 22:20:13 2012", "*nat", ":PREROUTING ACCEPT [831648:47063372]", ":INPUT ACCEPT [71162:4082850]", ":OUTPUT ACCEPT [159345:9708626]", ":POSTROUTING ACCEPT [263463:17336449]", "-A PREROUTING -d 1.2.3.4/32 -p tcp -m tcp --dport 25 -j DNAT --to-destination 4.3.2.1:25", "COMMIT", "*foo", "-A syn_flood -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -m limit --limit 25/sec --limit-burst 50 -j RETURN", ); my $rules = Rex::Commands::Iptables::_iptables_list(@iptables_list_1); ok( exists $rules->{nat}, "found nat tables" ); is( $rules->{nat}->[0]->[0], "A", "first is append" ); is( $rules->{nat}->[0]->[1], "PREROUTING", "append to prerouting" ); is( $rules->{nat}->[0]->[2], "d", "should be destination" ); is( $rules->{nat}->[0]->[3], "1.2.3.4/32", "to destination 1.2.3.4" ); is( $rules->{nat}->[0]->[4], "p", "should be proto" ); is( $rules->{nat}->[0]->[5], "tcp", "tcp" ); is( $rules->{nat}->[0]->[6], "m", "in module" ); is( $rules->{nat}->[0]->[7], "tcp", "tcp" ); is( $rules->{nat}->[0]->[8], "dport", "should be destination port" ); is( $rules->{nat}->[0]->[9], "25", "dport 25" ); is( $rules->{nat}->[0]->[10], "j", "jump to" ); is( $rules->{nat}->[0]->[11], "DNAT", "dnating" ); is( $rules->{nat}->[0]->[12], "to-destination", "should be forwarded to destination" ); is( $rules->{nat}->[0]->[13], "4.3.2.1:25", "4.3.2.1:25" ); ok( exists $rules->{foo}, "found foo table" ); is( $rules->{foo}->[0]->[0], "A", "frist is append" ); is( $rules->{foo}->[0]->[1], "syn_flood", "append to sys_flood" ); is( $rules->{foo}->[0]->[2], "p", "should use protocol" ); is( $rules->{foo}->[0]->[3], "tcp", "proto tcp" ); is( $rules->{foo}->[0]->[4], "m", "in module" ); is( $rules->{foo}->[0]->[5], "tcp", "tcp module" ); is( $rules->{foo}->[0]->[6], "tcp-flags", "should match tcp flags" ); is( $rules->{foo}->[0]->[7], "FIN,SYN,RST,ACK SYN", "only these flags" ); is( $rules->{foo}->[0]->[8], "m", "should use module" ); is( $rules->{foo}->[0]->[9], "limit", "limit module" ); is( $rules->{foo}->[0]->[10], "limit", "limit the bandwidth" ); is( $rules->{foo}->[0]->[11], "25/sec", "to 25 req per second" ); is( $rules->{foo}->[0]->[12], "limit-burst", "use burst" ); is( $rules->{foo}->[0]->[13], "50", "up to 50" ); is( $rules->{foo}->[0]->[14], "j", "jump to" ); is( $rules->{foo}->[0]->[15], "RETURN", "RETURN" ); URI.pm100644001751001751 56213232707066 14553 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Helper# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Helper::URI; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION sub encode { my ($part) = @_; $part =~ s/([^\w\-\.\@])/_encode_char($1)/eg; return $part; } sub _encode_char { my ($char) = @_; return "%" . sprintf "%lx", ord($char); } 1; INI.pm100644001751001751 370413232707066 14554 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Helper# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Helper::INI; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION BEGIN { String::Escape->use('string2hash'); } sub parse { my (@lines) = @_; my $ini; my $section; for (@lines) { chomp; s/\n|\r//g; (/^#|^;|^\s*$/) && (next); if ( /^\[(.*)\]/ && !/^\[(\d+((?:,)|(?:\.\.))*)+(\/\d+)*\]/ ) { # check for inheritance $section = $1; $ini->{$section} = {}; if ( $section =~ /{$section}; my @inherit = split( /{$is} } ) { $ini->{$section}->{$ik} = $ini->{$is}->{$ik}; } } } next; } my ( $key, $val ) = split( /[= ]/, $_, 2 ); $key =~ s/^\s*|\s*$//g if $key; $val =~ s/^\s*|\s*$//g if $val; my @splitted; if ( !$val ) { $val = $key; @splitted = ($key); } # commented out due to #184 else { #@splitted = split(/\./, $key); @splitted = ($key); } my $ref = $ini->{$section}; my $last = pop @splitted; for my $sub (@splitted) { unless ( exists $ini->{$section}->{$sub} ) { $ini->{$section}->{$sub} = {}; } $ref = $ini->{$section}->{$sub}; } # include other group if ( $key =~ m/^\@(.*)/ ) { for my $ik ( keys %{ $ini->{$1} } ) { $ini->{$section}->{$ik} = $ini->{$1}->{$ik}; } next; } if ( $val =~ m/\$\{(.*)\}/ ) { my $var_name = $1; my $ref = $ini; my @splitted = split( /\./, $var_name ); for my $s (@splitted) { $ref = $ref->{$s}; } $val = $ref; } if ( $val =~ m/=/ ) { $val = { string2hash($val) }; } $ref->{$last} = $val; } return $ini; } 1; Run.pm100644001751001751 676213232707066 14710 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Helper# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Helper::Run; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION require Exporter; use base qw(Exporter); use vars qw(@EXPORT); use Net::OpenSSH::ShellQuoter; use Rex::Interface::File; use Rex::Interface::Fs; use Rex::Helper::Path; use Carp; require Rex::Commands; require Rex::Config; @EXPORT = qw(upload_and_run i_run i_exec i_exec_nohup); sub upload_and_run { my ( $template, %option ) = @_; my $rnd_file = get_tmp_file; my $fh = Rex::Interface::File->create; $fh->open( ">", $rnd_file ); $fh->write($template); $fh->close; my $fs = Rex::Interface::Fs->create; $fs->chmod( 755, $rnd_file ); my @argv; my $command = $rnd_file; if ( exists $option{with} ) { $command = Rex::Config->get_executor_for( $option{with} ) . " $command"; } if ( exists $option{args} ) { $command .= join( " ", @{ $option{args} } ); } return i_run("$command 2>&1"); } # internal run command, doesn't get reported sub i_run { my $cmd = shift; my ( $code, $option ); $option = {}; if ( ref $_[0] eq "CODE" ) { $code = shift; } if ( scalar @_ > 0 ) { $option = {@_}; } $option->{valid_retval} ||= [0]; $option->{fail_ok} //= 0; if ( $option->{no_stderr} ) { $cmd = "$cmd 2>/dev/null"; } if ( $option->{stderr_to_stdout} ) { $cmd = "$cmd 2>&1"; } if ( ref $option->{valid_retval} ne "ARRAY" ) { $option->{valid_retval} = [ $option->{valid_retval} ]; } my $is_no_hup = 0; my $tmp_output_file = get_tmp_file(); if ( exists $option->{nohup} && $option->{nohup} ) { $cmd = "nohup $cmd >$tmp_output_file"; delete $option->{nohup}; $is_no_hup = 1; } my $path; if ( !Rex::Config->get_no_path_cleanup() ) { $path = join( ":", Rex::Config->get_path() ); } my $exec = Rex::Interface::Exec->create; my ( $out, $err ) = $exec->exec( $cmd, $path, $option ); my $ret_val = $?; chomp $out if $out; chomp $err if $err; $Rex::Commands::Run::LAST_OUTPUT = [ $out, $err ]; $out ||= ""; $err ||= ""; if ( scalar( grep { $_ == $ret_val } @{ $option->{valid_retval} } ) == 0 ) { if ( !$option->{fail_ok} ) { Rex::Logger::debug("Error executing `$cmd`: "); Rex::Logger::debug("STDOUT:"); Rex::Logger::debug($out); Rex::Logger::debug("STDERR:"); Rex::Logger::debug($err); if ($is_no_hup) { $out = $exec->exec("cat $tmp_output_file ; rm -f $tmp_output_file"); $Rex::Commands::Run::LAST_OUTPUT = [$out]; $? = $ret_val; } confess("Error during `i_run`"); } } if ($code) { return &$code( $out, $err ); } if (wantarray) { return split( /\r?\n/, $out ); } if ($is_no_hup) { $out = $exec->exec("cat $tmp_output_file ; rm -f $tmp_output_file"); $Rex::Commands::Run::LAST_OUTPUT = [$out]; $? = $ret_val; } return $out; } sub i_exec { my ( $cmd, @args ) = @_; my $exec = Rex::Interface::Exec->create; my $quoter = Net::OpenSSH::ShellQuoter->quoter( $exec->shell->name ); my $_cmd_str = "$cmd " . join( " ", map { $_ = $quoter->quote($_) } @args ); i_run $_cmd_str; } sub i_exec_nohup { my ( $cmd, @args ) = @_; my $exec = Rex::Interface::Exec->create; my $quoter = Net::OpenSSH::ShellQuoter->quoter( $exec->shell->name ); my $_cmd_str = "$cmd " . join( " ", map { $_ = $quoter->quote($_) } @args ); i_run $_cmd_str, nohup => 1; } 1; DBI.pm100644001751001751 136213232707066 14531 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Helper# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Helper::DBI; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION BEGIN { use Rex::Require; DBI->require; } my %db_connections; sub perform_request { my ( $dsn, $user, $pass, $req ) = @_; $user ||= ""; $pass ||= ""; my $con_key = "$dsn-$user-$pass"; if ( !exists $db_connections{$dsn} ) { $db_connections{$con_key} = DBI->connect( $dsn, $user, $pass ) or die("Cannot connect: $DBI::errstr\n"); } my %res; my $sth = $db_connections{$con_key}->prepare($req); $sth->execute(); my $i = 1; while ( my $ref = $sth->fetchrow_hashref() ) { $res{$i} = $ref; $i++; } return \%res; } 1; Shared000755001751001751 013232707066 13422 5ustar00janjan000000000000Rex-1.6.0/lib/RexVar.pm100644001751001751 337213232707066 14655 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Shared# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: =head1 NAME Rex::Shared::Var - Share variables across Rex tasks =head1 DESCRIPTION Share variables across Rex tasks with the help of Storable, using a C file in the local directory, where C<$PID> is the PID of the parent process. =head1 SYNOPSIS BEGIN { # put share in a BEGIN block use Rex::Shared::Var; share qw($scalar @array %hash); # share the listed variables } =head1 METHODS =cut package Rex::Shared::Var; use strict qw(vars subs); use warnings; our $VERSION = '1.6.0'; # VERSION require Exporter; use base qw(Exporter); use vars qw(@EXPORT); @EXPORT = qw(share); =head2 share Share the passed list of variables across Rex tasks. Should be used in a C block. BEGIN { use Rex::Shared::Var; share qw($error_count); } task 'count', sub { $error_count += run 'wc -l /var/log/syslog'; }; after_task_finished 'count', sub { say "Total number of errors: $error_count"; }; =cut sub share { my @vars = @_; my ( $package, $file, $line ) = caller; my ( $sigil, $sym ); for my $var (@vars) { if ( ( $sigil, $sym ) = ( $var =~ /^([\$\@\%\*\&])(.+)/ ) ) { $sym = "${package}::$sym"; if ( $sigil eq "\$" ) { eval "use Rex::Shared::Var::Scalar;"; tie $$sym, "Rex::Shared::Var::Scalar", $sym; *$sym = \$$sym; } elsif ( $sigil eq "\@" ) { eval "use Rex::Shared::Var::Array;"; tie @$sym, "Rex::Shared::Var::Array", $sym; *$sym = \@$sym; } elsif ( $sigil eq "\%" ) { eval "use Rex::Shared::Var::Hash;"; tie %$sym, "Rex::Shared::Var::Hash", $sym; *$sym = \%$sym; } } } } 1; Linux.pm100644001751001751 60713232707066 14675 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Cron# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Cron::Linux; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Rex::Cron::Base; use base qw(Rex::Cron::Base); sub new { my $that = shift; my $proto = ref($that) || $that; my $self = $proto->SUPER::new(@_); bless( $self, $proto ); return $self; } 1; SunOS.pm100644001751001751 122613232707066 14623 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Cron# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Cron::SunOS; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Rex::Cron::Base; use base qw(Rex::Cron::Base); use Rex::Helper::Run; use Rex::Commands::Fs; sub new { my $that = shift; my $proto = ref($that) || $that; my $self = $proto->SUPER::new(@_); bless( $self, $proto ); return $self; } sub read_user_cron { my ( $self, $user ) = @_; my @lines = i_run "crontab -l $user"; $self->parse_cron(@lines); } sub activate_user_cron { my ( $self, $file, $user ) = @_; i_run "crontab $file"; unlink $file; } 1; Ubuntu.pm100644001751001751 61213232707066 14674 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Pkg# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Pkg::Ubuntu; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Rex::Pkg::Debian; use base qw(Rex::Pkg::Debian); sub new { my $that = shift; my $proto = ref($that) || $that; my $self = $proto->SUPER::new(@_); bless( $self, $proto ); return $self; } 1; Gentoo.pm100644001751001751 1276213232707066 14716 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Pkg# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Pkg::Gentoo; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Rex::Commands::Run; use Rex::Helper::Run; use Rex::Commands::File; use Rex::Pkg::Base; use base qw(Rex::Pkg::Base); sub new { my $that = shift; my $proto = ref($that) || $that; my $self = $proto->SUPER::new(@_); bless( $self, $proto ); $self->{commands} = { install => 'emerge --update --changed-use %s', install_version => 'emerge --update --changed-use =%s-%s', reinstall_check => 'emerge --pretend --update --changed-use --nodeps --quiet --verbose =%s', update_system => 'emerge --update --deep --with-bdeps=y --newuse world', dist_update_system => 'emerge --update --deep --with-bdeps=y --newuse world', remove => 'emerge -C %s', update_package_db => 'emerge --sync', }; return $self; } sub bulk_install { my ( $self, $packages_aref, $option ) = @_; delete $option->{version}; # makes no sense to specify the same version for several packages $self->update( "@{$packages_aref}", $option ); return 1; } sub is_installed { my ( $self, $pkg, $option ) = @_; my $slot; my $version = $option->{version}; # Determine slot. my $slot_idx = index($pkg, ':'); if ( $slot_idx != -1 ) { die "Illegal package spec. `$pkg-$version': Both package and version has SLOT" if $version && index($version, ':') != -1; $slot = substr($pkg, $slot_idx + 1); substr($pkg, $slot_idx) = ''; } elsif ( $version ) { $slot_idx = index($version, ':'); if ( $slot_idx != -1 ) { $slot = substr($version, $slot_idx + 1); substr($version, $slot_idx) = ''; } } $self->{short} = 0; Rex::Logger::debug( "Checking if $pkg" . ( $version ? "-$version" : "" ) . ( $slot ? ":$slot" : '') . " is installed" ); my @pkg_info = grep { $_->{name} eq $pkg } $self->get_installed(); @pkg_info = grep { $_->{version} eq $version } @pkg_info if defined $version; unless (@pkg_info) { Rex::Logger::debug( "Couldn't find package by category/packagename, trying with packagename only" ); $self->{short} = 1; @pkg_info = grep { $_->{name} eq $pkg } $self->get_installed(); @pkg_info = grep { $_->{version} eq $version } @pkg_info if defined $version; } unless (@pkg_info) { Rex::Logger::debug( "$pkg" . ( $version ? "-$version" : "" ) . ( $slot ? ":$slot" : '') . " is NOT installed." ); return 0; } # Check for requested SLOT. my $pkg_atom; if ( defined $slot ) { my $slot_ok; for my $info (@pkg_info) { $pkg_atom = "$info->{name}-$info->{version}$info->{suffix}$info->{release}"; my $fh = file_read("/var/db/pkg/$pkg_atom/SLOT"); chomp( my $slot_installed = $fh->read_all ); $fh->close; if ( $slot eq $slot_installed ) { $pkg_atom .= ":$slot"; $slot_ok = 1; last; } } unless ( $slot_ok ) { Rex::Logger::debug( "$pkg" . ( $version ? "-$version" : "" ) . ( $slot ? ":$slot" : '') . " is NOT installed." ); return 0; } } else { $pkg_atom = "$pkg_info[0]->{name}-$pkg_info[0]->{version}$pkg_info[0]->{suffix}$pkg_info[0]->{release}"; } # Check for any USE flag changes. my $rchk_cmd = sprintf( $self->{commands}->{reinstall_check}, $pkg_atom ); my @rchk_out = i_run $rchk_cmd; for my $line (@rchk_out) { next unless $line =~ /\s*\[ebuild[^]]+\]\s+$pkg_info[0]->{name}/; Rex::Logger::debug( "$pkg" . ( $version ? "-$version" : "" ) . ( $slot ? ":$slot" : '') . " is installed but USE flags have changed." ); return 0; } Rex::Logger::debug( "$pkg" . ( $version ? "-$version" : "" ) . ( $slot ? ":$slot" : '') . " is installed." ); return 1; } sub get_installed { my ($self) = @_; my $cut_cmd; if ( $self->{short} ) { $cut_cmd = "cut -d '/' -f6-"; } else { $cut_cmd = "cut -d '/' -f5-"; } # ,,stolen'' from epm my $pkgregex = '(.+?)' . # name '-(\d+(?:\.\d+)*\w*)' . # version, eg 1.23.4a '((?:(?:_alpha|_beta|_pre|_rc)\d*)?)' . # special suffix '((?:-r\d+)?)$'; # revision, eg r12 my @ret; for my $line ( i_run("ls -d /var/db/pkg/*/* | $cut_cmd") ) { my $r = qr{$pkgregex}; my ( $name, $version, $suffix, $revision ) = ( $line =~ $r ); push( @ret, { name => $name, version => $version, suffix => $suffix, release => $revision, } ); } return @ret; } sub add_repository { my ( $self, %data ) = @_; my $name = $data{"name"}; my $readd = $data{"readd"} // 0; if ( can_run("layman") ) { my $op; i_run "layman -lqnN | grep -q '$name'", fail_ok => 1; if ($? == 0) { if ($readd) { $op = 'r'; # --readd } else { Rex::Logger::info("Repository $name is present, use `readd' option to re-add from scratch."); } } else { $op = 'a'; # --add } i_run "layman -$op $name" if defined $op; } else { Rex::Logger::debug("You have to install layman, git and subversion."); die("Please install layman, git and subversion"); } } sub rm_repository { my ( $self, $name ) = @_; if ( can_run("layman") ) { i_run "layman -d $name"; } else { Rex::Logger::debug("You have to install layman, git and subversion."); die("Please install layman, git and subversion"); } } 1; NetBSD.pm100644001751001751 234013232707066 14511 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Pkg# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Pkg::NetBSD; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Rex::Helper::Run; use Rex::Commands::File; use Rex::Pkg::Base; use base qw(Rex::Pkg::Base); sub new { my $that = shift; my $proto = ref($that) || $that; my $self = $proto->SUPER::new(@_); bless( $self, $proto ); $self->{commands} = { install => '. /etc/profile; /usr/sbin/pkg_add %s', install_version => '. /etc/profile; /usr/sbin/pkg_add %s-%s', remove => '/usr/sbin/pkg_delete %s', }; return $self; } sub remove { my ( $self, $pkg ) = @_; my ($pkg_found) = grep { $_->{"name"} eq "$pkg" } $self->get_installed(); my $pkg_version = $pkg_found->{"version"}; return $self->SUPER::remove("$pkg-$pkg_version"); } sub get_installed { my ($self) = @_; my @lines = i_run "/usr/sbin/pkg_info"; my @pkg; for my $line (@lines) { my ( $pkg_name_v, $descr ) = split( /\s/, $line, 2 ); my ( $pkg_name, $pkg_version ) = ( $pkg_name_v =~ m/^(.*)-(.*?)$/ ); push( @pkg, { name => $pkg_name, version => $pkg_version, } ); } return @pkg; } 1; Redhat.pm100644001751001751 601413232707066 14643 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Pkg# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Pkg::Redhat; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Rex::Helper::Run; use Rex::Commands::File; use Rex::Commands::Fs; use Rex::Pkg::Base; use base qw(Rex::Pkg::Base); sub new { my $that = shift; my $proto = ref($that) || $that; my $self = $proto->SUPER::new(@_); bless( $self, $proto ); if ( Rex::has_feature_version('1.5') ) { $self->{commands} = { install => $self->_yum('-y install %s'), install_version => $self->_yum('-y install %s-%s'), update_system => $self->_yum("-y -C upgrade"), dist_update_system => $self->_yum("-y -C upgrade"), remove => $self->_yum('-y erase %s'), update_package_db => $self->_yum("clean all") . " ; " . $self->_yum("makecache"), }; } else { $self->{commands} = { install => $self->_yum('-y install %s'), install_version => $self->_yum('-y install %s-%s'), update_system => $self->_yum("-y upgrade"), dist_update_system => $self->_yum("-y upgrade"), remove => $self->_yum('-y erase %s'), update_package_db => $self->_yum("clean all") . " ; " . $self->_yum("makecache"), }; } return $self; } sub bulk_install { my ( $self, $packages_aref, $option ) = @_; delete $option->{version}; # makes no sense to specify the same version for several packages $self->update( "@{$packages_aref}", $option ); return 1; } sub get_installed { my ($self) = @_; my @lines = i_run 'rpm -qa --nosignature --nodigest --qf "%{NAME} %|EPOCH?{%{EPOCH}}:{0}| %{VERSION} %{RELEASE} %{ARCH}\n"'; my @pkg; for my $line (@lines) { if ( $line =~ m/^([^\s]+)\s([^\s]+)\s([^\s]+)\s([^\s]+)\s(.*)$/ ) { push( @pkg, { name => $1, epoch => $2, version => $3, release => $4, arch => $5, } ); } } return @pkg; } sub add_repository { my ( $self, %data ) = @_; my $name = $data{"name"}; my $desc = $data{"description"} || $data{"name"}; if ( exists $data{"key_url"} ) { i_run "rpm --import $data{key_url}"; } if ( exists $data{"key_file"} ) { i_run "rpm --import $data{key_file}"; } my $fh = file_write "/etc/yum.repos.d/$name.repo"; $fh->write("# This file is managed by Rex\n"); $fh->write("[$name]\n"); $fh->write("name=$desc\n"); $fh->write( "baseurl=" . $data{"url"} . "\n" ); $fh->write("enabled=1\n"); $fh->write( "gpgkey=" . $data{"gpgkey"} . "\n" ) if defined $data{"gpgkey"}; $fh->write( "gpgcheck=" . $data{"gpgcheck"} . "\n" ) if defined $data{"gpgcheck"}; $fh->close; } sub rm_repository { my ( $self, $name ) = @_; unlink "/etc/yum.repos.d/$name.repo"; } sub _yum { my ( $self, @cmd ) = @_; my $str; if ($Rex::Logger::debug) { $str = join( ' ', "yum ", @cmd ); } else { $str = join( ' ', "yum -q ", @cmd ); } return $str; } 1; Debian.pm100644001751001751 1020213232707066 14630 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Pkg# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Pkg::Debian; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Rex::Helper::Run; use Rex::Commands::File; use Rex::Commands::Fs; use Rex::Pkg::Base; use base qw(Rex::Pkg::Base); sub new { my $that = shift; my $proto = ref($that) || $that; my $self = $proto->SUPER::new(@_); bless( $self, $proto ); $self->{commands} = { install => 'APT_LISTCHANGES_FRONTEND=none DEBIAN_FRONTEND=noninteractive apt-get -o Dpkg::Options::=--force-confold -y install %s', install_version => 'APT_LISTCHANGES_FRONTEND=none DEBIAN_FRONTEND=noninteractive apt-get -o Dpkg::Options::=--force-confold -y install %s=%s', update_system => 'APT_LISTCHANGES_FRONTEND=none DEBIAN_FRONTEND=noninteractive apt-get -y -qq upgrade', dist_update_system => 'APT_LISTCHANGES_FRONTEND=none DEBIAN_FRONTEND=noninteractive apt-get -y -qq dist-upgrade', remove => 'apt-get -y remove %s', purge => 'dpkg --purge %s', update_package_db => 'apt-get -y update', }; return $self; } sub bulk_install { my ( $self, $packages_aref, $option ) = @_; delete $option->{version}; # makes no sense to specify the same version for several packages $self->update( "@{$packages_aref}", $option ); return 1; } sub get_installed { my ( $self, $pkg ) = @_; my @pkgs; my $dpkg_cmd = 'dpkg-query -W --showformat "\${Status} \${Package}|\${Version}|\${Architecture}\n"'; if ($pkg) { $dpkg_cmd .= " " . $pkg; } my @lines = i_run $dpkg_cmd; for my $line (@lines) { if ( $line =~ m/^install ok installed ([^\|]+)\|([^\|]+)\|(.*)$/ ) { push( @pkgs, { name => $1, version => $2, architecture => $3, } ); } } return @pkgs; } sub diff_package_list { my ( $self, $list1, $list2 ) = @_; my @old_installed = @{$list1}; my @new_installed = @{$list2}; my @modifications; # getting modifications of old packages OLD_PKG: for my $old_pkg (@old_installed) { NEW_PKG: for my $new_pkg (@new_installed) { if ( $old_pkg->{name} eq $new_pkg->{name} && $old_pkg->{architecture} eq $new_pkg->{architecture} ) { # flag the package as found in new package list, # to find removed and new ones. $old_pkg->{found} = 1; $new_pkg->{found} = 1; if ( $old_pkg->{version} ne $new_pkg->{version} ) { push @modifications, { %{$new_pkg}, action => 'updated' }; } next OLD_PKG; } } } # getting removed old packages push @modifications, map { $_->{action} = 'removed'; $_ } grep { !exists $_->{found} } @old_installed; # getting new packages push @modifications, map { $_->{action} = 'installed'; $_ } grep { !exists $_->{found} } @new_installed; map { delete $_->{found} } @modifications; return @modifications; } sub add_repository { my ( $self, %data ) = @_; my $name = $data{"name"}; my $fh = file_write "/etc/apt/sources.list.d/$name.list"; $fh->write("# This file is managed by Rex\n"); if ( exists $data{"arch"} ) { $fh->write( "deb [arch=" . $data{"arch"} . "] " . $data{"url"} . " " . $data{"distro"} . " " . $data{"repository"} . "\n" ); } else { $fh->write( "deb " . $data{"url"} . " " . $data{"distro"} . " " . $data{"repository"} . "\n" ); } if ( exists $data{"source"} && $data{"source"} ) { $fh->write( "deb-src " . $data{"url"} . " " . $data{"distro"} . " " . $data{"repository"} . "\n" ); } $fh->close; if ( exists $data{"key_url"} ) { i_run "wget -O - " . $data{"key_url"} . " | apt-key add -"; } if ( exists $data{"key_file"} ) { i_run "apt-key add $data{'key_file'}"; } if ( exists $data{"key_id"} && $data{"key_server"} ) { i_run "apt-key adv --keyserver " . $data{"key_server"} . " --recv-keys " . $data{"key_id"}; } } sub rm_repository { my ( $self, $name ) = @_; unlink "/etc/apt/sources.list.d/$name.list"; } 1; Mageia.pm100644001751001751 403413232707066 14617 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Pkg# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Pkg::Mageia; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Rex::Helper::Run; use Rex::Pkg::Base; use base qw(Rex::Pkg::Base); sub new { my $that = shift; my $proto = ref($that) || $that; my $self = $proto->SUPER::new(@_); bless( $self, $proto ); if ( Rex::has_feature_version('1.5') ) { $self->{commands} = { install => 'urpmi --auto --quiet %s', install_version => 'urpmi --auto --quiet %s', update_system => 'urpmi --auto --quiet --auto-select', dist_update_system => 'urpmi --auto --quiet --auto-select', remove => 'urpme --auto %s', update_package_db => 'urpmi.update -a', }; } else { $self->{commands} = { install => 'urpmi --auto --quiet %s', install_version => 'urpmi --auto --quiet %s', update_system => 'urpmi --auto --quiet --auto-update', dist_update_system => 'urpmi --auto --quiet --auto-update', remove => 'urpme --auto %s', update_package_db => 'urpmi.update -a', }; } return $self; } sub get_installed { my ($self) = @_; my @lines = i_run 'rpm -qa --nosignature --nodigest --qf "%{NAME} %|EPOCH?{%{EPOCH}}:{0}| %{VERSION} %{RELEASE} %{ARCH}\n"'; my @pkg; for my $line (@lines) { if ( $line =~ m/^([^\s]+)\s([^\s]+)\s([^\s]+)\s([^\s]+)\s(.*)$/ ) { push( @pkg, { name => $1, epoch => $2, version => $3, release => $4, arch => $5, } ); } } return @pkg; } sub add_repository { my ( $self, %data ) = @_; my $name = $data{"name"}; i_run "urpmi.addmedia $name " . $data{"url"}, fail_ok => 1; if ( $? != 0 ) { die("Error adding repository $name"); } } sub rm_repository { my ( $self, $name ) = @_; i_run "urpmi.removemedia $name", fail_ok => 1; if ( $? != 0 ) { die("Error removing repository $name"); } } 1; Amazon.pm100644001751001751 1273613232707066 14720 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Box# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: =head1 NAME Rex::Box::Amazon - Rex/Boxes Amazon Module =head1 DESCRIPTION This is a Rex/Boxes module to use Amazon EC2. =head1 EXAMPLES To use this module inside your Rexfile you can use the following commands. use Rex::Commands::Box; set box => "Amazon", { access_key => "your-access-key", private_access_key => "your-private-access-key", region => "ec2.eu-west-1.amazonaws.com", zone => "eu-west-1a", authkey => "default", }; task "prepare_box", sub { box { my ($box) = @_; $box->name("mybox"); $box->ami("ami-c1aaabb5"); $box->type("m1.large"); $box->security_group("default"); $box->auth( user => "root", password => "box", ); $box->setup("setup_task"); }; }; If you want to use a YAML file you can use the following template. type: Amazon amazon: access_key: your-access-key private_access_key: your-private-access-key region: ec2.eu-west-1.amazonaws.com zone: eu-west-1a auth_key: default vms: vmone: ami: ami-c1aaabb5 type: m1.large security_group: default setup: setup_task And then you can use it the following way in your Rexfile. use Rex::Commands::Box init_file => "file.yml"; task "prepare_vms", sub { boxes "init"; }; =head1 METHODS See also the Methods of Rex::Box::Base. This module inherits all methods of it. =cut package Rex::Box::Amazon; use strict; use warnings; use Data::Dumper; use Rex::Box::Base; use Rex::Commands -no => [qw/auth/]; use Rex::Commands::Fs; use Rex::Commands::Cloud; our $VERSION = '1.6.0'; # VERSION BEGIN { LWP::UserAgent->use; } use Time::HiRes qw(tv_interval gettimeofday); use File::Basename qw(basename); use base qw(Rex::Box::Base); $|++; ################################################################################ # BEGIN of class methods ################################################################################ =head2 new(name => $vmname) Constructor if used in OO mode. my $box = Rex::Box::VBox->new(name => "vmname"); =cut sub new { my $class = shift; my $proto = ref($class) || $class; my $self = $proto->SUPER::new(@_); bless( $self, ref($class) || $class ); cloud_service "Amazon"; cloud_auth $self->{options}->{access_key}, $self->{options}->{private_access_key}; cloud_region $self->{options}->{region}; return $self; } sub import_vm { my ($self) = @_; # check if machine already exists # Rex::Logger::debug("VM already exists. Don't import anything."); #my @vms = cloud_instance_list; my @vms = $self->list_boxes; my $vminfo; my $vm_exists = 0; for my $vm (@vms) { if ( $vm->{name} && $vm->{name} eq $self->{name} ) { Rex::Logger::debug("VM already exists. Don't import anything."); $vm_exists = 1; $vminfo = $vm; } } if ( !$vm_exists ) { # if not, create it Rex::Logger::info("Creating Amazon instance $self->{name}."); $vminfo = cloud_instance create => { image_id => $self->{ami}, name => $self->{name}, key => $self->{options}->{auth_key}, zone => $self->{options}->{zone}, type => $self->{type} || "m1.large", security_group => $self->{security_group} || "default", options => $self->options, }; } # start if stopped if ( $vminfo->{state} eq "stopped" ) { cloud_instance start => $vminfo->{id}; } $self->{info} = $vminfo; } =head2 ami($ami_id) Set the AMI ID for the box. =cut sub ami { my ( $self, $ami ) = @_; $self->{ami} = $ami; } =head2 type($type) Set the type of the Instance. For example "m1.large". =cut sub type { my ( $self, $type ) = @_; $self->{type} = $type; } =head2 security_group($sec_group) Set the Amazon security group for this Instance. =cut sub security_group { my ( $self, $sec_group ) = @_; $self->{security_group} = $sec_group; } =head2 forward_port(%option) Not available for Amazon Boxes. =cut sub forward_port { Rex::Logger::debug("Not available for Amazon Boxes."); } =head2 share_folder(%option) Not available for Amazon Boxes. =cut sub share_folder { Rex::Logger::debug("Not available for Amazon Boxes."); } sub list_boxes { my ($self) = @_; my @vms = cloud_instance_list; my @ret = grep { $_->{name} && $_->{state} ne "terminated" && $_->{state} ne "shutting-down" } @vms; # only vms with names... return @ret; } sub status { my ($self) = @_; $self->info; if ( $self->{info}->{state} eq "running" ) { return "running"; } else { return "stopped"; } } sub start { my ($self) = @_; $self->info; Rex::Logger::info( "Starting instance: " . $self->{name} ); cloud_instance start => $self->{info}->{id}; } sub stop { my ($self) = @_; Rex::Logger::info( "Stopping instance: " . $self->{name} ); $self->info; cloud_instance stop => $self->{info}->{id}; } sub destroy { my ($self) = @_; Rex::Logger::info( "Destroying instance: " . $self->{name} ); $self->info; cloud_instance terminate => $self->{info}->{id}; } =head2 info Returns a hashRef of vm information. =cut sub info { my ($self) = @_; ( $self->{info} ) = grep { $_->{name} eq $self->{name} } $self->list_boxes; return $self->{info}; } sub ip { my ($self) = @_; # get instance info ( $self->{info} ) = grep { $_->{name} eq $self->{name} } $self->list_boxes; return $self->{info}->{ip}; } 1; Docker.pm100644001751001751 1045713232707066 14700 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Box# # (c) Jan Gehring # # vim: set ts=3 sw=3 tw=0: # vim: set expandtab: =head1 NAME Rex::Box::Docker - Rex/Boxes Docker Module =head1 DESCRIPTION This is a Rex/Boxes module to use Docker Images. You need to have dockerd installed. =head1 EXAMPLES To use this module inside your Rexfile you can use the following commands. use Rex::Commands::Box; set box => "Docker"; task "prepare_box", sub { box { my ($box) = @_; $box->name("mybox"); $box->url("http://box.rexify.org/box/ubuntu-server-12.10-amd64.tar.gz"); $box->url("debian:latest"); $box->network(1 => { name => "default", }); $box->auth( user => "root", password => "box", ); $box->setup("setup_task"); }; }; If you want to use a YAML file you can use the following template. type: Docker vms: vmone: url: debian:latest setup: setup_task And then you can use it the following way in your Rexfile. use Rex::Commands::Box init_file => "file.yml"; task "prepare_vms", sub { boxes "init"; }; =head1 METHODS See also the Methods of Rex::Box::Base. This module inherits all methods of it. =cut package Rex::Box::Docker; use strict; use warnings; use Data::Dumper; use Rex::Box::Base; use Rex::Commands -no => [qw/auth/]; use Rex::Commands::Fs; use Rex::Commands::Virtualization; use Rex::Commands::SimpleCheck; use Rex::Virtualization::Docker::create; our $VERSION = '1.6.0'; # VERSION BEGIN { LWP::UserAgent->use; } use Time::HiRes qw(tv_interval gettimeofday); use File::Basename qw(basename); use base qw(Rex::Box::Base); set virtualization => "Docker"; $|++; ################################################################################ # BEGIN of class methods ################################################################################ =head2 new(name => $vmname) Constructor if used in OO mode. my $box = Rex::Box::Docker->new(name => "vmname"); =cut sub new { my $class = shift; my $proto = ref($class) || $class; my $self = $proto->SUPER::new(@_); bless( $self, ref($class) || $class ); return $self; } =head2 memory($memory_size) Sets the memory of a VM in megabyte. =cut sub memory { my ( $self, $mem ) = @_; Rex::Logger::debug("Memory option not supported."); } sub import_vm { my ($self) = @_; # check if machine already exists my $vms = vm list => "all"; my $vm_exists = 0; for my $vm ( @{$vms} ) { if ( $vm->{name} eq $self->{name} ) { Rex::Logger::debug("VM already exists. Don't import anything."); $vm_exists = 1; } } if ( !$vm_exists ) { # if not, create it my ($filename); if ( $self->{url} =~ m/^(http|https):/ ) { $self->_download; $filename = "./tmp/" . basename( $self->{url} ); Rex::Logger::info("Importing VM ./tmp/$filename"); my @options = ( import => $self->{name}, file => $filename, %{$self}, ); vm @options; } else { vm import => $self->{name}, file => $self->{url}, %{$self}; } } my $vminfo = vm info => $self->{name}; unless ( $vminfo->{State}->{Running} ) { $self->start; } $self->{info} = vm guestinfo => $self->{name}; } sub list_boxes { my ($self) = @_; my $vms = vm list => "all"; return @{$vms}; } =head2 info Returns a hashRef of vm information. =cut sub info { my ($self) = @_; $self->ip; return $self->{info}; } =head2 ip This method return the ip of a vm on which the ssh daemon is listening. =cut sub ip { my ($self) = @_; $self->{info} = vm guestinfo => $self->{name}; if ( $self->{info}->{redirects} && $self->{info}->{redirects}->{tcp} && $self->{info}->{redirects}->{tcp}->{22} ) { return ( $self->{info}->{redirects}->{tcp}->{22}->[0]->{ip} eq "0.0.0.0" ? "127.0.0.1" : $self->{info}->{redirects}->{tcp}->{22}->[0]->{ip} ) . ":" . $self->{info}->{redirects}->{tcp}->{22}->[0]->{port}; } else { return $self->{info}->{network}->[0]->{ip}; } } sub create { my ($self) = @_; my @options = ( forward_port => [ $self->{__forward_port}->{ssh}->[0] . ":" . $self->{__forward_port}->{ssh}->[1] ], image => $self->{url}, ); Rex::Virtualization::Docker::create->execute( $self->{name}, @options ); } 1; Cloud000755001751001751 013232707066 13262 5ustar00janjan000000000000Rex-1.6.0/lib/RexBase.pm100644001751001751 322013232707066 14627 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Cloud# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Cloud::Base; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Rex::Logger; sub new { my $that = shift; my $proto = ref($that) || $that; my $self = {@_}; bless( $self, $proto ); return $self; } sub set_auth { Rex::Logger::debug("Not implemented"); } sub set_endpoint { my ( $self, $endpoint ) = @_; # only set endpoint if defined if ( defined $endpoint ) { $self->{__endpoint} = $endpoint; } } sub list_plans { Rex::Logger::debug("Not implemented"); } sub list_operating_systems { Rex::Logger::debug("Not implemented"); } sub run_instance { Rex::Logger::debug("Not implemented"); } sub terminate_instance { Rex::Logger::debug("Not implemented"); } sub start_instance { Rex::Logger::debug("Not implemented"); } sub stop_instance { Rex::Logger::debug("Not implemented"); } sub list_instances { Rex::Logger::debug("Not implemented"); } sub list_running_instances { Rex::Logger::debug("Not implemented"); } sub create_volume { Rex::Logger::debug("Not implemented"); } sub attach_volume { Rex::Logger::debug("Not implemented"); } sub detach_volume { Rex::Logger::debug("Not implemented"); } sub delete_volume { Rex::Logger::debug("Not implemented"); } sub list_volumes { Rex::Logger::debug("Not implemented"); } sub list_images { Rex::Logger::debug("Not implemented"); } sub add_tag { Rex::Logger::debug("Not implemented"); } sub get_regions { Rex::Logger::debug("Not implemented"); } sub get_availability_zones { Rex::Logger::debug("Not implemented"); } 1; Linux.pm100644001751001751 2551213232707066 14754 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/User# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::User::Linux; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Rex::Logger; require Rex::Commands; use Rex::Commands::MD5; use Rex::Helper::Run; use Rex::Helper::Encode; use Rex::Commands::Fs; use Rex::Interface::File; use Rex::Interface::Fs; use Rex::Interface::Exec; use Rex::Helper::Path; use JSON::MaybeXS; use Rex::User::Base; use base qw(Rex::User::Base); sub new { my $that = shift; my $proto = ref($that) || $that; my $self = $proto->SUPER::new(@_); bless( $self, $proto ); return $self; } sub create_user { my ( $self, $user, $data ) = @_; my $cmd; my $uid = $self->get_uid($user); my $run_cmd = 0; my $should_create_home; # if any home creation intent has been defined, # don't follow the default home creation policy my $use_default_home_policy = ( defined $data->{'create_home'} || defined $data->{'create-home'} || defined $data->{'no_create_home'} || defined $data->{'no-create-home'} ) ? 0 : 1; if ( !$use_default_home_policy ) { if ( $data->{'create_home'} || $data->{'create-home'} ) { $should_create_home = 1; } elsif ( $data->{'no_create_home'} || $data->{'no-create-home'} ) { $should_create_home = 0; } elsif ( ( exists $data->{'no_create_home'} && $data->{'no_create_home'} == 0 ) || ( exists $data->{'no-create-home'} && $data->{'no-create-home'} == 0 ) ) { $should_create_home = 1; } } if ( !defined $uid ) { Rex::Logger::debug("User $user does not exists. Creating it now."); $cmd = "/usr/sbin/useradd "; if ( exists $data->{system} ) { $cmd .= " -r"; } $run_cmd = 1; } else { # only the user should be there, no modifications. # so just return if ( !defined $data ) { return { changed => 0, uid => $uid, }; } Rex::Logger::debug("User $user already exists. Updating..."); if ( exists $data->{uid} && $data->{uid} == $uid ) { delete $data->{uid}; } $cmd = "/usr/sbin/usermod "; } if ( exists $data->{non_uniq} ) { $cmd .= " -o "; $run_cmd = 1; } if ( exists $data->{uid} ) { $cmd .= " --uid " . $data->{uid}; $run_cmd = 1; } if ( exists $data->{home} ) { $run_cmd = 1; $cmd .= " -d " . $data->{home}; # don't create home directory in useradd mode if it already exists $should_create_home = 0 if ( !defined $uid && is_dir( $data->{home} ) ); } if ( !$use_default_home_policy ) { if ( !defined $uid ) { #useradd mode if ($should_create_home) { $cmd .= " -m "; } else { $cmd .= " -M "; } } else { #usermod mode $cmd .= " -m " if ( exists $data->{home} ); } } if ( exists $data->{shell} ) { $run_cmd = 1; $cmd .= " --shell " . $data->{shell}; } if ( exists $data->{comment} ) { $run_cmd = 1; $cmd .= " --comment '" . $data->{comment} . "'"; } if ( exists $data->{expire} ) { $run_cmd = 1; $cmd .= " --expiredate '" . $data->{expire} . "'"; } if ( exists $data->{groups} ) { $run_cmd = 1; my @groups = @{ $data->{groups} }; my $pri_group = shift @groups; $cmd .= " --gid $pri_group"; if (@groups) { $cmd .= " --groups " . join( ",", @groups ); } } my $old_pw_md5 = md5("/etc/passwd"); my $old_sh_md5 = ""; eval { $old_sh_md5 = md5("/etc/shadow"); }; # only run the cmd if needed if ($run_cmd) { my $rnd_file = get_tmp_file; my $fh = Rex::Interface::File->create; $fh->open( ">", $rnd_file ); $fh->write("rm \$0\n$cmd $user\nexit \$?\n"); $fh->close; i_run "/bin/sh $rnd_file", fail_ok => 1; if ( $? == 0 ) { Rex::Logger::debug("User $user created/updated."); } else { Rex::Logger::info( "Error creating/updating user $user", "warn" ); die("Error creating/updating user $user"); } } if ( exists $data->{password} ) { my $rnd_file = get_tmp_file; my $fh = Rex::Interface::File->create; $fh->open( ">", $rnd_file ); $fh->write( "rm \$0\n/bin/echo -e '" . $data->{password} . "\\n" . $data->{password} . "' | /usr/bin/passwd $user\nexit \$?\n" ); $fh->close; Rex::Logger::debug("Changing password of $user."); i_run "/bin/sh $rnd_file", fail_ok => 1; if ( $? != 0 ) { die("Error setting password for $user"); } } if ( exists $data->{crypt_password} && $data->{crypt_password} ) { my $rnd_file = get_tmp_file; my $fh = Rex::Interface::File->create; $fh->open( ">", $rnd_file ); $fh->write( "rm \$0\nusermod -p '" . $data->{crypt_password} . "' $user\nexit \$?\n" ); $fh->close; Rex::Logger::debug("Setting encrypted password of $user"); i_run "/bin/sh $rnd_file", fail_ok => 1; if ( $? != 0 ) { die("Error setting password for $user"); } } my $new_pw_md5 = md5("/etc/passwd"); my $new_sh_md5 = ""; eval { $new_sh_md5 = md5("/etc/shadow"); }; if ( $new_pw_md5 eq $old_pw_md5 && $new_sh_md5 eq $old_sh_md5 ) { return { changed => 0, ret => $self->get_uid($user), }; } else { return { changed => 1, ret => $self->get_uid($user), }, ; } } sub rm_user { my ( $self, $user, $data ) = @_; Rex::Logger::debug("Removing user $user"); my $cmd = "/usr/sbin/userdel"; if ( exists $data->{delete_home} ) { $cmd .= " --remove"; } if ( exists $data->{force} ) { $cmd .= " --force"; } my $output = i_run $cmd . " " . $user, fail_ok => 1; if ( $? == 6 ) { Rex::Logger::info("Cannot delete user $user (no such user)", "warn"); } elsif ( $? != 0 ) { die("Error deleting user $user ($output)"); } } sub get_uid { my ( $self, $user ) = @_; my %data = $self->get_user($user); return $data{uid}; } sub user_groups { my ( $self, $user ) = @_; Rex::Logger::debug("Getting group membership of $user"); my $rnd_file = get_tmp_file; my $fh = Rex::Interface::File->create; my $script = q| unlink $0; $exe = "/usr/bin/groups"; if(! -x $exe) { $exe = "/bin/groups"; } print to_json([ map {chomp; $_ =~ s/^[^:]*:\s*(.*)\s*$/$1/; split / /, $_} qx{$exe $ARGV[0]} ]); |; $fh->open( ">", $rnd_file ); $fh->write($script); $fh->write( func_to_json() ); $fh->close; my $data_str = i_run "perl $rnd_file $user", fail_ok => 1; if ( $? != 0 ) { die("Error getting group list"); } my $data = decode_json($data_str); my $wantarray = wantarray(); if ( defined $wantarray && !$wantarray ) { # arrayref return $data; } return @{$data}; } sub user_list { my $self = shift; Rex::Logger::debug("Getting user list"); my $rnd_file = get_tmp_file; my $script = q| unlink $0; print to_json([ map {chomp; $_ =~ s/^([^:]*):.*$/$1/; $_} qx{/usr/bin/getent passwd} ]); |; my $fh = Rex::Interface::File->create; $fh->open( ">", $rnd_file ); $fh->write($script); $fh->write( func_to_json() ); $fh->close; my $data_str = i_run "perl $rnd_file", fail_ok => 1; if ( $? != 0 ) { die("Error getting user list"); } my $data = decode_json($data_str); return @$data; } sub get_user { my ( $self, $user ) = @_; Rex::Logger::debug("Getting information for $user"); my $rnd_file = get_tmp_file; my $fh = Rex::Interface::File->create; my $script = q| unlink $0; print to_json([ getpwnam($ARGV[0]) ]); |; $fh->open( ">", $rnd_file ); $fh->write($script); $fh->write( func_to_json() ); $fh->close; my $data_str = i_run "perl $rnd_file $user", fail_ok => 1; if ( $? != 0 ) { die("Error getting user information for $user"); } my $data = decode_json($data_str); return ( name => $data->[0], password => $data->[1], uid => $data->[2], gid => $data->[3], comment => $data->[5], home => $data->[7], shell => $data->[8], expire => exists $data->[9] ? $data->[9] : 0, ); } sub lock_password { my ( $self, $user ) = @_; # Is the password already locked? my $result = i_run "passwd --status $user", fail_ok => 1; die "Unexpected result from passwd: $result" unless $result =~ /^$user\s+(L|NP|P)\s+/; if ( $1 eq 'L' ) { # Already locked return { changed => 0 }; } else { my $ret = i_run "passwd --lock $user", fail_ok => 1; if ( $? != 0 ) { die("Error locking account $user: $ret"); } return { changed => 1, ret => $ret, }; } } sub unlock_password { my ( $self, $user ) = @_; # Is the password already unlocked? my $result = i_run "passwd --status $user", fail_ok => 1; die "Unexpected result from passwd: $result" unless $result =~ /^$user\s+(L|NP|P)\s+/; if ( $1 eq 'P' ) { # Already unlocked return { changed => 0 }; } else { # Capture error string on failure (eg. account has no password) my ( $ret, $err ) = i_run "passwd --unlock $user", sub { @_ }, fail_ok => 1; if ( $? != 0 ) { die("Error unlocking account $user: $err"); } return { changed => 1, ret => $ret, }; } } sub create_group { my ( $self, $group, $data ) = @_; my $cmd; my $gid = $self->get_gid($group); if ( !defined $gid ) { Rex::Logger::debug("Creating new group $group"); $cmd = "/usr/sbin/groupadd "; } elsif ( exists $data->{gid} && $data->{gid} == $gid ) { if ( Rex::Config->get_do_reporting ) { return { changed => 0, ret => $gid, }; } return $gid; } else { if ( !defined $data ) { if ( Rex::Config->get_do_reporting ) { return { changed => 0, ret => $gid, }; } return $gid; } Rex::Logger::debug("Group $group already exists. Updating..."); $cmd = "/usr/sbin/groupmod "; } if ( exists $data->{gid} ) { $cmd .= " -g " . $data->{gid}; $gid = undef; } i_run $cmd . " " . $group, fail_ok => 1; if ( $? != 0 ) { die("Error creating/modifying group $group"); } if ( defined $gid ) { return $gid; } return $self->get_gid($group); } sub get_gid { my ( $self, $group ) = @_; my %data = $self->get_group($group); return $data{gid}; } sub get_group { my ( $self, $group ) = @_; Rex::Logger::debug("Getting information for $group"); my @data = split( " ", "" . i_run( "perl -le 'print join(\" \", getgrnam(\$ARGV[0]));' '$group'", fail_ok => 1 ), 4 ); if ( $? != 0 ) { die("Error getting group information"); } return ( name => $data[0], password => $data[1], gid => $data[2], members => $data[3], ); } sub rm_group { my ( $self, $group ) = @_; i_run "/usr/sbin/groupdel $group", fail_ok => 1; if ( $? != 0 ) { die("Error deleting group $group"); } } 1; SunOS.pm100644001751001751 753613232707066 14652 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/User# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::User::SunOS; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Rex::Logger; use Rex::Commands::Run; use Rex::Helper::Run; use Rex::Commands::Fs; use Rex::Commands::File; use Rex::User::OpenBSD; use Rex::Interface::File; use Rex::Interface::Fs; use Rex::Interface::Exec; use Rex::Helper::Path; use base qw(Rex::User::OpenBSD); sub new { my $that = shift; my $proto = ref($that) || $that; my $self = $proto->SUPER::new(@_); bless( $self, $proto ); return $self; } sub create_user { my ( $self, $user, $data ) = @_; my $cmd; my $uid = $self->get_uid($user); my $should_create_home; if ( $data->{'create_home'} || $data->{'create-home'} ) { $should_create_home = 1; } elsif ( $data->{'no_create_home'} || $data->{'no-create-home'} ) { $should_create_home = 0; } elsif ( ( exists $data->{'no_create_home'} && $data->{'no_create_home'} == 0 ) || ( exists $data->{'no-create-home'} && $data->{'no-create-home'} == 0 ) ) { $should_create_home = 1; } if ( !defined $uid ) { Rex::Logger::debug("User $user does not exists. Creating it now."); $cmd = "useradd "; if ( exists $data->{system} ) { $cmd .= " -r"; } } else { Rex::Logger::debug("User $user already exists. Updating..."); $cmd = "usermod "; } if ( exists $data->{uid} ) { $cmd .= " -u " . $data->{uid}; } if ( exists $data->{home} ) { $cmd .= " -d " . $data->{home}; } if ( $should_create_home && !defined $uid ) { #useradd mode $cmd .= " -m "; } if ( exists $data->{shell} ) { $cmd .= " -s " . $data->{shell}; } if ( exists $data->{comment} ) { $cmd .= " -c '" . $data->{comment} . "'"; } if ( exists $data->{expire} ) { $cmd .= " -e '" . $data->{expire} . "'"; } if ( exists $data->{groups} ) { my @groups = @{ $data->{groups} }; my $pri_group = shift @groups; $cmd .= " -g $pri_group"; if (@groups) { $cmd .= " -G " . join( ",", @groups ); } } my $rnd_file = get_tmp_file; my $fh = Rex::Interface::File->create; $fh->open( ">", $rnd_file ); $fh->write("$cmd $user\nexit \$?\n"); $fh->close; i_run "/bin/sh $rnd_file", fail_ok => 1; if ( $? == 0 ) { Rex::Logger::debug("User $user created/updated."); } else { Rex::Logger::info( "Error creating/updating user $user", "warn" ); die("Error creating/updating user $user"); } Rex::Interface::Fs->create()->unlink($rnd_file); if ( exists $data->{password} ) { my $expect_path; if ( can_run("/usr/local/bin/expect") ) { $expect_path = "/usr/local/bin/expect"; } elsif ( can_run("/usr/bin/expect") ) { $expect_path = "/usr/bin/expect"; } if ($expect_path) { my $chpasswd_file = get_tmp_file; my $fh = file_write $chpasswd_file; $fh->write( qq~#!$expect_path -- # Input: username password set USER [lindex \$argv 0] set PASS [lindex \$argv 1] if { \$USER == "" || \$PASS == "" } { puts "Usage: /tmp/chpasswd username password\n" exit 1 } spawn passwd \$USER expect "assword:" send "\$PASS\r" expect "assword:" send "\$PASS\r" expect eof ~ ); $fh->close; $rnd_file = get_tmp_file; $fh = Rex::Interface::File->create; $fh->open( ">", $rnd_file ); $fh->write( "$chpasswd_file $user '" . $data->{"password"} . "'\nexit \$?\n" ); $fh->close; chmod 700, $chpasswd_file; i_run "/bin/sh $rnd_file", fail_ok => 1; if ( $? != 0 ) { die("Error changing user's password."); } rm $chpasswd_file; rm $rnd_file; } else { die( "No expect found in /usr/local/bin or /usr/bin. Can't set user password." ); } } return { changed => 0, ret => $self->get_uid($user), }; } 1; author-pod-syntax.t100644001751001751 45413232707066 15071 0ustar00janjan000000000000Rex-1.6.0/t#!perl BEGIN { unless ($ENV{AUTHOR_TESTING}) { print qq{1..0 # SKIP these tests are for testing by the author\n}; exit } } # This file was automatically generated by Dist::Zilla::Plugin::PodSyntaxTests. use strict; use warnings; use Test::More; use Test::Pod 1.41; all_pod_files_ok(); interface_fs_local.t100644001751001751 112713232707066 15303 0ustar00janjan000000000000Rex-1.6.0/tuse strict; use warnings; use Test::More tests => 7; use Rex::Interface::Fs; my $fs = Rex::Interface::Fs->create("Local"); ok( $fs, "created fs interface object" ); my @files = $fs->ls("."); ok( grep { /^ChangeLog$/ } @files, "found ChangeLog" ); is( $fs->is_file("ChangeLog"), 1, "ChangeLog is a file" ); is( $fs->is_dir("."), 1, ". is a directory" ); $fs->mkdir("foo"); is( $fs->is_dir("foo"), 1, "mkdir" ); $fs->rmdir("foo"); is( $fs->is_dir("foo"), undef, "rmdir" ); is( $fs->stat("some_file_that_does_not_exist"), undef, "stat should return undef for non-existent files" ); ip.out_centos7_alias100644001751001751 133113232707066 15265 0ustar00janjan000000000000Rex-1.6.0/t1: lo: mtu 65536 qdisc noqueue state UNKNOWN link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: eth0: mtu 1500 qdisc pfifo_fast state UP qlen 1000 link/ether 52:54:00:f4:0d:74 brd ff:ff:ff:ff:ff:ff inet 192.168.112.198/24 brd 192.168.112.255 scope global dynamic eth0 valid_lft 3466sec preferred_lft 3466sec inet 1.2.3.4/32 scope global eth0:1 valid_lft forever preferred_lft forever inet6 fe80::5054:ff:fef4:d74/64 scope link valid_lft forever preferred_lft forever default000755001751001751 013232707066 13504 5ustar00janjan000000000000Rex-1.6.0/t/cmdbfoo.yml100644001751001751 17013232707066 15130 0ustar00janjan000000000000Rex-1.6.0/t/cmdb/defaultvhost: name: foohost doc_root: /var/www vhost2: name: vhost2foo newntp: - ntpdefaultfoo01 - ntpdefaultfoo02 tasks000755001751001751 013232707066 13311 5ustar00janjan000000000000Rex-1.6.0/t/lib/talien.pm100644001751001751 16513232707066 15061 0ustar00janjan000000000000Rex-1.6.0/t/lib/t/taskspackage t::tasks::alien; use Rex -base; desc "negotiate with the aliens"; task "negotiate" => sub { return 1 }; 1; Transaction.pm100644001751001751 452013232707066 15200 0ustar00janjan000000000000Rex-1.6.0/lib/Rex# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: =head1 NAME Rex::Transaction - Transaction support. =head1 DESCRIPTION With this module you can define transactions and rollback scenarios on failure. =head1 SYNOPSIS task "do-something", "server01", sub { transaction { on_rollback { rmdir "/tmp/mydata"; }; mkdir "/tmp/mydata"; upload "files/myapp.tar.gz", "/tmp/mydata"; run "cd /tmp/mydata; tar xzf myapp.tar.gz"; if($? != 0) { die("Error extracting myapp.tar.gz"); } }; }; =head1 EXPORTED FUNCTIONS =cut package Rex::Transaction; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION require Exporter; use vars qw(@EXPORT @ROLLBACKS); use base qw(Exporter); use Rex::Logger; use Rex::TaskList; use Data::Dumper; @EXPORT = qw(transaction on_rollback); =head2 transaction($codeRef) Start a transaction for $codeRef. If $codeRef dies it will rollback the transaction. task "deploy", group => "frontend", sub { on_rollback { rmdir "..."; }; deploy "myapp.tar.gz"; }; task "restart_server", group => "frontend", sub { run "/etc/init.d/apache2 restart"; }; task "all", group => "frontend", sub { transaction { do_task [qw/deploy restart_server/]; }; }; =cut sub transaction(&) { my ($code) = @_; my $ret = 1; Rex::Logger::debug("Cleaning ROLLBACKS array"); @ROLLBACKS = (); Rex::TaskList->create()->set_in_transaction(1); eval { &$code(); }; if ($@) { my $err = $@; Rex::Logger::info("Transaction failed. Rolling back."); $ret = 0; for my $rollback_code ( reverse @ROLLBACKS ) { # push the connection of the task back Rex::push_connection( $rollback_code->{"connection"} ); # run the rollback code &{ $rollback_code->{"code"} }($err); # and pop it away Rex::pop_connection(); } Rex::TaskList->create()->set_in_transaction(0); die("Transaction failed. Rollback done."); } Rex::TaskList->create()->set_in_transaction(0); return $ret; } =head2 on_rollback($codeRef) This code will be executed if one step in the transaction fails. See I. =cut sub on_rollback(&) { my ($code) = @_; push( @ROLLBACKS, { code => $code, connection => Rex::get_current_connection() } ); } 1; SSH2.pm100644001751001751 652113232707066 14654 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Helper# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Helper::SSH2; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION require Exporter; use Data::Dumper; require Rex::Commands; use base qw(Exporter); use vars qw(@EXPORT); @EXPORT = qw(net_ssh2_exec net_ssh2_exec_output net_ssh2_shell_exec); our $READ_STDERR = 1; our $EXEC_AND_SLEEP = 0; sub net_ssh2_exec { my ( $ssh, $cmd, $base, $option ) = @_; my $chan = $ssh->channel; # REQUIRE_TTY can be turned off by feature no_tty if ( !Rex::Config->get_no_tty ) { $chan->pty("xterm"); # set to xterm, due to problems with vt100. # if vt100 sometimes the restart of services doesn't work and need a sleep .000001 after the command... # strange bug... $chan->pty_size( 4000, 80 ); } $chan->blocking(1); $chan->exec($cmd); my $in; my $in_err = ""; my $rex_int_conf = Rex::Commands::get("rex_internals") || {}; my $buffer_size = 1024; if ( exists $rex_int_conf->{read_buffer_size} ) { $buffer_size = $rex_int_conf->{read_buffer_size}; } my @lines; my $last_line; my $current_line = ""; while ( my $len = $chan->read( my $buf, $buffer_size ) ) { $in .= $buf; $current_line .= $buf; if ( $buf =~ m/\n/ms ) { @lines = split( /\n/, $current_line ); unshift @lines, $last_line if ($last_line); $last_line = pop @lines; for my $line (@lines) { $line =~ s/[\r\n]//gms; $line .= "\n"; $base->execute_line_based_operation( $line, $option ) && goto END_READ; } $current_line = ""; } } my @lines_err; my $last_line_err = ""; while ( my $len = $chan->read( my $buf_err, $buffer_size, 1 ) ) { $in_err .= $buf_err; @lines_err = split( /\n/, $buf_err ); unshift @lines_err, $last_line_err if ($last_line_err); $last_line_err = pop @lines_err; for my $line (@lines_err) { $line =~ s/[\r\n]//gms; $line .= "\n"; $base->execute_line_based_operation( $line, $option ) && goto END_READ; } } #select undef, undef, undef, 0.002; # wait a little before closing the channel #sleep 1; END_READ: $chan->send_eof; my $wait_c = 0; my $wait_max = $rex_int_conf->{ssh2_channel_closewait_max} || 500; while ( !$chan->eof ) { Rex::Logger::debug("Waiting for eof on ssh channel."); select undef, undef, undef, 0.002; # wait a little for retry $wait_c++; if ( $wait_c >= $wait_max ) { # channel will be force closed. Rex::Logger::debug( "Rex::Helper::SSH2::net_ssh2_exec: force closing channel for command: $cmd" ); last; } } $chan->wait_closed; $? = $chan->exit_status; # if used with $chan->pty() we have to remove \r if ( !Rex::Config->get_no_tty ) { $in =~ s/\r//g if $in; $in_err =~ s/\r//g if $in_err; } if (wantarray) { return ( $in, $in_err ); } return $in; } sub net_ssh2_exec_output { my ( $ssh, $cmd, $callback ) = @_; my $chan = $ssh->channel; $chan->blocking(1); $chan->exec($cmd); while (1) { my $buf; my $buf_err; $chan->read( $buf, 15 ); $chan->read( $buf_err, 15 ); if ($callback) { &$callback( $buf, $buf_err ); } else { print $buf; print $buf_err; } last unless $buf; } $chan->close; $? = $chan->exit_status; } 1; Hash.pm100644001751001751 223413232707066 15015 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Helper# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Helper::Hash; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION require Exporter; use base qw(Exporter); use vars qw(@EXPORT); @EXPORT = qw(hash_flatten); sub hash_flatten { my ( $in, $out, $sep, @super_keys ) = @_; if ( ref($in) eq "HASH" ) { for my $key ( keys %{$in} ) { push @super_keys, $key; if ( ref( $in->{$key} ) ) { hash_flatten( $in->{$key}, $out, $sep, @super_keys ); } else { my $new_key_name = join( $sep, @super_keys ); $new_key_name =~ s/[^A-Za-z0-9_]/_/g; $out->{$new_key_name} = $in->{$key}; } pop @super_keys; } } elsif ( ref($in) eq "ARRAY" ) { my $counter = 0; for my $val ( @{$in} ) { if ( ref($val) ) { push @super_keys, $counter; hash_flatten( $val, $out, $sep, @super_keys ); pop @super_keys; } else { my $new_key_name = join( $sep, @super_keys ) . "_$counter"; $new_key_name =~ s/[^A-Za-z0-9_]/_/g; $out->{$new_key_name} = $val; } $counter++; } } } 1; Path.pm100644001751001751 1133213232707066 15045 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Helper# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Helper::Path; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Rex::Helper::File::Spec; use File::Basename qw(dirname); require Exporter; use base qw(Exporter); use vars qw(@EXPORT); use Cwd 'realpath'; require Rex; use Rex::Commands; require Rex::Config; use Rex::Interface::Exec; @EXPORT = qw(get_file_path get_tmp_file resolv_path parse_path); set "path_map", {}; # # CALL: get_file_path("foo.txt", caller()); # RETURNS: module file # sub get_file_path { my ( $file_name, $caller_package, $caller_file ) = @_; $file_name = resolv_path($file_name); my $ends_with_slash = 0; if ( $file_name =~ m/\/$/ ) { $ends_with_slash = 1; } my $fix_path = sub { my ($path) = @_; $path =~ s:^\./::; if ($ends_with_slash) { if ( $path !~ m/\/$/ ) { return "$path/"; } } return $path; }; if ( !$caller_package ) { ( $caller_package, $caller_file ) = caller(); } # check if a file in $BASE overwrites the module file # first get the absolute path to the rexfile $::rexfile ||= $0; my @path_parts; if ( $^O =~ m/^MSWin/ && !Rex::is_ssh() ) { @path_parts = split( /\//, $::rexfile ); } else { @path_parts = split( /\//, realpath($::rexfile) ); } pop @path_parts; my $real_path = join( '/', @path_parts ); my $map_setting = get("path_map"); my %path_map = ( map { ( ( substr( $_, -1 ) eq '/' ) ? $_ : "$_/" ) => $map_setting->{$_} } keys %$map_setting ); foreach my $prefix ( sort { length($b) <=> length($a) } grep { $file_name =~ m/^$_/ } keys %path_map ) { foreach my $pattern ( @{ $path_map{$prefix} } ) { my $expansion = Rex::Helper::File::Spec->catfile( parse_path($pattern), substr( $file_name, length($prefix) ) ); if ( -e $expansion ) { return $fix_path->($expansion); } $expansion = Rex::Helper::File::Spec->catfile( $real_path, $expansion ); if ( -e $expansion ) { return $fix_path->($expansion); } } } if ( -e $file_name ) { return $fix_path->($file_name); } my $cat_file_name = Rex::Helper::File::Spec->catfile( $real_path, $file_name ); if ( -e $cat_file_name ) { return $fix_path->($cat_file_name); } # walk down the wire to find the file... my ($old_caller_file) = $caller_file; my $i = 0; while ( $caller_package && $i <= 50 ) { ( $caller_package, $caller_file ) = caller($i); if ( !$caller_package ) { last; } my $module_path = Rex::get_module_path($caller_package); $cat_file_name = Rex::Helper::File::Spec->catfile( $module_path, $file_name ); if ( -e $cat_file_name ) { return $fix_path->($cat_file_name); } $i++; } $file_name = Rex::Helper::File::Spec->catfile( dirname($old_caller_file), $file_name ); return $fix_path->($file_name); } sub get_tmp_file { return Rex::Helper::File::Spec->join( Rex::Config->get_tmp_dir(), Rex::Commands::get_random( 12, 'a' .. 'z' ) . '.tmp' ); } sub resolv_path { my ( $path, $local ) = @_; if ( $path !~ m/^~/ ) { # path starts not with ~ so we don't need to expand $HOME. # just return it. return $path; } my $home_path; require Rex::User; my $user_o = Rex::User->get; if ($local) { if ( $^O =~ m/^MSWin/ ) { # windows path: $home_path = $ENV{'USERPROFILE'}; } else { if ( $path =~ m/^~([a-zA-Z0-9_][^\/]+)\// ) { my $user_name = $1; my %user_info = $user_o->get_user($user_name); $home_path = $user_info{home}; $path =~ s/^~$user_name/$home_path/; } else { $home_path = $ENV{'HOME'}; $path =~ s/^~/$home_path/; } } } else { if ( $path =~ m/^~([a-zA-Z0-9_][^\/]+)\// ) { my $user_name = $1; my %user_info = $user_o->get_user($user_name); $home_path = $user_info{home}; $path =~ s/^~$user_name/$home_path/; } else { my $exec = Rex::Interface::Exec->create; my $remote_home = $exec->exec("echo \$HOME"); $remote_home =~ s/[\r\n]//gms; $home_path = $remote_home; $path =~ s/^~/$home_path/; } } return $path; } sub parse_path { my ($path) = @_; my %hw; require Rex::Commands::Gather; $hw{server} = Rex::Commands::connection()->server; $hw{environment} = Rex::Commands::environment(); $path =~ s/\{(server|environment)\}/$hw{$1}/gms; if ( $path =~ m/\{([^\}]+)\}/ ) { # if there are still some variables to replace, we need some information of # the system. %hw = Rex::Commands::Gather::get_system_information(); $path =~ s/\{([^\}]+)\}/$hw{$1}/gms; } return $path; } 1; Misc.pm100644001751001751 55613232707066 15012 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Helper# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Helper::Misc; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION sub get_random { my $count = shift; my @chars = @_; my $ret = ""; for ( 1 .. $count ) { $ret .= $chars[ int( rand( scalar(@chars) - 1 ) ) ]; } return $ret; } 1; Report000755001751001751 013232707066 13467 5ustar00janjan000000000000Rex-1.6.0/lib/RexBase.pm100644001751001751 434313232707066 15043 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Report# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Report::Base; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Data::Dumper; use Rex::Logger; use Time::HiRes qw(time); use Carp; sub new { my $that = shift; my $proto = ref($that) || $that; my $self = {@_}; bless( $self, $proto ); $self->{__reports__} = {}; $self->{__current_resource__} = []; return $self; } sub report { my ( $self, %option ) = @_; confess "not inside a resource." if ( !$self->{__current_resource__}->[-1] ); if ( $option{changed} && !exists $option{message} ) { $option{message} = "Resource updated."; } elsif ( $option{changed} == 0 && !exists $option{message} ) { $option{message} = "Resource already up-to-date."; } # update all stacked resources for my $res ( @{ $self->{__current_resource__} } ) { $self->{__reports__}->{$res}->{changed} ||= $option{changed} || 0; } push @{ $self->{__reports__}->{ $self->{__current_resource__}->[-1] }->{messages} }, $option{message}; } sub report_task_execution { my ( $self, %option ) = @_; $self->{__reports__}->{task} = \%option; } sub report_resource_start { my ( $self, %option ) = @_; push @{ $self->{__current_resource__} }, $self->_gen_res_name(%option); $self->{__reports__}->{ $self->{__current_resource__}->[-1] } = { changed => 0, messages => [], start_time => time, }; } sub report_resource_end { my ( $self, %option ) = @_; confess "not inside a resource." if ( !$self->{__current_resource__}->[-1] ); $self->{__reports__}->{ $self->{__current_resource__}->[-1] }->{end_time} = time; pop @{ $self->{__current_resource__} }; } sub report_resource_failed { my ( $self, %opt ) = @_; return if ( !$self->{__current_resource__}->[-1] ); # update all stacked resources for my $res ( @{ $self->{__current_resource__} } ) { $self->{__reports__}->{$res}->{failed} = 1; } push @{ $self->{__reports__}->{ $self->{__current_resource__} > [-1] } ->{messages} }, $opt{message}; } sub write_report { my ($self) = @_; } sub _gen_res_name { my ( $self, %option ) = @_; return $option{type} . "[" . $option{name} . "]"; } 1; YAML.pm100644001751001751 324013232707066 14726 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Report# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Report::YAML; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Rex; use Data::Dumper; use Rex::Report::Base; require Rex::Commands; use YAML; use base qw(Rex::Report::Base); our $REPORT_PATH = "./reports"; my $report_name_generator = sub { my $str = time(); return $str; }; sub set_report_name { my ( $class, $code ) = @_; if ( ref $class eq "CODE" ) { $code = $class; } if ( ref $code ne "CODE" ) { die "Rex::Report::YAML->set_report_name(\$code_ref) wrong arguments."; } $report_name_generator = $code; } sub new { my $that = shift; my $proto = ref($that) || $that; my $self = $proto->SUPER::new(@_); bless( $self, $proto ); return $self; } sub write_report { my ($self) = @_; $REPORT_PATH = Rex::Commands::get('report_path') || "reports"; if ( !-d $REPORT_PATH ) { mkdir $REPORT_PATH or die( $! . ": $REPORT_PATH" ); } my $server_name = Rex::Commands::connection()->server; if ( $server_name eq "" ) { $server_name = "_local_"; } if ( !-d $REPORT_PATH . "/" . $server_name ) { mkdir "$REPORT_PATH/$server_name"; } open( my $fh, ">", "$REPORT_PATH/$server_name/" . $report_name_generator->($server_name) . ".yml" ) or die($!); print $fh Dump( $self->{__reports__} ); close($fh); $self->{__reports__} = {}; } # $self->report({ # command => $export, # module => "Rex::Commands::$mod", # start_time => $start_time, # end_time => time, # data => [ @_ ], # success => 1, # changed => 1, # message => "", 1; Args000755001751001751 013232707066 13110 5ustar00janjan000000000000Rex-1.6.0/lib/RexString.pm100644001751001751 42013232707066 15030 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Args# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Args::String; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION sub get { my ( $class, $name ) = @_; my $arg = shift @ARGV; return $arg; } 1; Single.pm100644001751001751 32413232707066 15006 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Args# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Args::Single; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION sub get { return 1; } 1; Template000755001751001751 013232707066 13767 5ustar00janjan000000000000Rex-1.6.0/lib/RexNG.pm100644001751001751 1665713232707066 15030 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Template# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Template::NG; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION sub new { my $that = shift; my $proto = ref($that) || $that; my $self = {@_}; bless( $self, $proto ); $self->_init(); return $self; } sub _init { my ($self) = @_; $self->{__output__} = ""; $self->{__code__} = ""; $self->{__raw_data__} = ""; } sub parse { my $self = shift; my $c = shift; my %in_vars; $self->_init(); if ( ref $_[0] eq "HASH" ) { %in_vars = %{ +shift }; } else { %in_vars = @_; } my %vars; for my $key ( keys %in_vars ) { my $new_key = $key; $new_key =~ s/[^a-zA-Z0-9_]/_/gms; $vars{$new_key} = $in_vars{$key}; } # some backward compat. to old template module. $c =~ s/\$::([a-zA-Z0-9_]+)/_replace_var($1, \%vars)/egms; my $code = ""; my $var_data = ' return sub { my ( $self, '; my @code_values; for my $var ( keys %vars ) { $var_data .= '$' . $var . ", \n"; push( @code_values, $vars{$var} ); } $var_data .= '$this_is_really_nothing) = @_;'; $var_data .= "\n"; $code = $var_data; $code .= $self->_parse($c); $code .= "\n}"; my $idx_c = 1; for my $l ( split( /\n/, $code ) ) { $idx_c++; $l ||= ""; Rex::Logger::debug("$idx_c. $l"); } $self->{__code__} = $code; $self->{__raw_data__} = $c; no warnings; my $tpl_code = eval($code); use warnings; if ($@) { my $error = $@; my ($error_line) = ( $error =~ m/line (\d+)[\.,]/ ); my @code_lines = split( /\n/, $code ); my @raw_lines = split( /\n/, $c ); my $idx = $error_line - 5; for my $l ( @code_lines[ $error_line - 5 .. $error_line + 5 ] ) { $idx++; $l ||= ""; Rex::Logger::debug("$idx. $l"); } my $template_line = 0; my $add_to_error_line = -1; # search the error line Rex::Logger::debug("Template-Error-Line: $error_line"); for ( my $bi = $error_line - 1 ; $bi >= 0 ; $bi-- ) { if ( $code_lines[$bi] =~ m/^# LINE: (\d+)$/ ) { $template_line = $1 + $add_to_error_line; last; } $add_to_error_line++; } if ( !$template_line ) { die "Uncatchable error in template: $error ($error_line)"; } my $start_part = $template_line - 5; $start_part = 0 if $start_part <= 0; my $end_part = $template_line + 5; $end_part = scalar @raw_lines if $end_part > scalar @raw_lines; my $idx_t = $start_part; for my $l ( @raw_lines[ $start_part .. $end_part ] ) { $idx_t++; $l ||= ""; Rex::Logger::info("$idx_t. $l"); } my $tpl_error = $error; $tpl_error =~ s/at \(eval \d+\) line \d+/at template line $template_line/; if ( $error =~ m/Global symbol "([^"]+)" requires explicit package name/ ) { $tpl_error = "Unknown variable name $1 in code line: ,,$raw_lines[$template_line-1]'' line: $template_line.\nOriginal Error:\n$error\n"; } # internal parsing error, maybe runaway line without ";" elsif ( $raw_lines[ $template_line - 2 ] =~ m/^%/ && $raw_lines[ $template_line - 2 ] !~ m/[;{("']/ ) { Rex::Logger::debug( "Template Error in compiled line: $code_lines[$error_line-1]"); Rex::Logger::info( "Template Error somewhere around: $raw_lines[$template_line-2]", "error" ); my $template_line_ = $template_line - 1; $tpl_error = "Maybe missing <<;, {, (, \" or '>> in code line: ,,$raw_lines[$template_line-2]'' line $template_line_.\nOriginal Error:\n$error\n"; } else { $tpl_error = "Failed parsing template. Unkown error near $template_line.\nOriginal Error:\n$error\n"; } die $tpl_error; } $tpl_code->( $self, @code_values ); return $self->{__output__}; } sub __out { my ( $self, $str ) = @_; $self->{__output__} .= defined $str ? $str : ""; } sub _parse { my ( $self, $c ) = @_; my $parsed = ""; my @chars = split( //, $c ); my $begin_line = 0; my $code_line = 0; my $code_block = 0; my $code_block_output = 0; my $current_char_idx = -1; my $line_count = 1; my $string_open = 0; my $skip_next = 0; my $skip_next_newline = 0; for my $curr_char (@chars) { $current_char_idx++; if ($skip_next) { $skip_next = 0; next; } my $prev_char = $chars[ $current_char_idx - 1 ] || ""; my $next_char = $chars[ $current_char_idx + 1 ] || ""; if ( $skip_next_newline && $curr_char eq "\n" ) { $skip_next_newline = 0; $curr_char = ""; } if ( $curr_char eq "\n" && $prev_char ne "\n" ) { # count lines, for error messages $line_count++; $parsed .= $curr_char; if ($string_open) { $parsed .= "});\n"; } # reset vars $code_line = 0; $string_open = 0; next; } if ( $curr_char eq "\n" && $prev_char eq "\n" ) { $parsed .= "\$self->__out(q{\n});\n"; $line_count++; next; } if ( $curr_char eq "-" && $next_char eq "%" && ( $prev_char eq " " || $prev_char eq "\n" ) && $chars[ $current_char_idx + 2 ] eq ">" ) { # skip "-" of -%> sequence $skip_next_newline = 1; next; } # catch code line # % some code if ( !$code_block && ( $prev_char eq "\n" || $current_char_idx == 0 ) # first line or new line && $curr_char eq "%" && $next_char eq " " # code block, and no % char escape sequence ) { $code_line = 1; $parsed .= "\n# LINE: $line_count\n"; next; } # catch '<% ' ... if ( $prev_char eq "<" && $curr_char eq "%" && ( $next_char eq " " || $next_char eq "\n" ) ) { $code_block = 1; if ($string_open) { $parsed .= "});\n"; $string_open = 0; } $parsed .= "\n# LINE: $line_count\n"; next; } # catch ' %>' if ( $code_block && ( ( $code_block_output || $prev_char eq " " ) || $prev_char eq "\n" || $prev_char eq "-" ) && $curr_char eq "%" && $next_char eq ">" ) { $code_block = 0; if ($code_block_output) { $parsed .= ");\n"; $code_block_output = 0; } $string_open = 1; $parsed .= "\n\$self->__out(q{"; next; } # catch '<%=' if ( $prev_char eq "<" && $curr_char eq "%" && $next_char eq "=" ) { $code_block = 1; $code_block_output = 1; if ($string_open) { $parsed .= "});\n"; } $parsed .= "\n# LINE: $line_count\n"; $parsed .= "\$self->__out("; $skip_next = 1; next; } if ( $code_line || $code_block ) { $parsed .= $curr_char; next; } if ( !$string_open ) { $string_open = 1; $parsed .= '$self->__out(q{'; } # don't catch opening < if ( $curr_char eq "<" && $next_char eq "%" ) { next; } # don't catch closing > if ( $curr_char eq ">" && $prev_char eq "%" ) { next; } # escaping of % sign if ( $curr_char eq "%" && $prev_char eq "%" ) { next; } $parsed .= $curr_char =~ m/[{}]/ ? "\\$curr_char" : $curr_char; } if ($string_open) { $parsed .= "});\n"; } return $parsed; } sub _replace_var { my ( $var, $t_vars ) = @_; if ( exists $t_vars->{$var} ) { return '$' . $var; } else { return '$::' . $var; } } 1; FreeBSD.pm100644001751001751 461713232707066 14655 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Pkg# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Pkg::FreeBSD; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Rex::Helper::Run; use Rex::Commands::File; use Rex::Pkg::Base; use base qw(Rex::Pkg::Base); sub new { my $that = shift; my $proto = ref($that) || $that; my $self = $proto->SUPER::new(@_); bless( $self, $proto ); # Check if pkg is actually bootstraped (installed and activated) i_run "pkg -N", fail_ok => 1; if ( $? != 0 ) { i_run "pkg bootstrap", env => { 'ASSUME_ALWAYS_YES' => 'true' }; } $self->{commands} = { install => 'pkg install -q -y %s', install_version => 'pkg install -q -y %s', remove => 'pkg remove -q -y %s', query => 'pkg info', update_package_db => 'pkg update -q -f', # pkg can't update system yet, only packages update_system => 'pkg upgrade -q -y', }; return $self; } sub bulk_install { my ( $self, $packages_aref, $option ) = @_; # makes no sense to specify the same version for several packages delete $option->{version}; $self->update( "@{$packages_aref}", $option ); return 1; } sub remove { my ( $self, $pkg ) = @_; my ($pkg_found) = grep { $_->{"name"} eq "$pkg" } $self->get_installed(); my $pkg_version = $pkg_found->{"version"}; return $self->SUPER::remove("$pkg-$pkg_version"); } sub is_installed { my ( $self, $pkg, $option ) = @_; my $version = $option->{version}; Rex::Logger::debug( "Checking if $pkg" . ( $version ? "-$version" : "" ) . " is installed" ); # pkg info -e allow get quick answer about is pkg installed or not. my $command = $self->{commands}->{query} . " -e $pkg" . ( $version ? "-$version" : "" ); i_run $command, fail_ok => 1; if ( $? != 0 ) { Rex::Logger::debug( "$pkg" . ( $version ? "-$version" : "" ) . " is NOT installed." ); return 0; } Rex::Logger::debug( "$pkg" . ( $version ? "-$version" : "" ) . " is installed." ); return 1; } sub get_installed { my ($self) = @_; my @lines = i_run $self->{commands}->{query}; my @pkg; for my $line (@lines) { my ( $pkg_name_v, $descr ) = split( /\s/, $line, 2 ); my ( $pkg_name, $pkg_version ) = ( $pkg_name_v =~ m/^(.*)-(.*?)$/ ); push( @pkg, { name => $pkg_name, version => $pkg_version, } ); } return @pkg; } 1; OpenBSD.pm100644001751001751 61213232707066 14644 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Pkg# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Pkg::OpenBSD; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Rex::Pkg::NetBSD; use base qw(Rex::Pkg::NetBSD); sub new { my $that = shift; my $proto = ref($that) || $that; my $self = $that->SUPER::new(@_); bless( $self, $proto ); return $self; } 1; OpenWrt.pm100644001751001751 357013232707066 15036 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Pkg# OpenWrt.pm # # Copyright 2013 Ferenc Erki # # OpenWrt package management module for (R)?ex # based on Rex::Pkg::Debian package Rex::Pkg::OpenWrt; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION use Rex::Helper::Run; use Rex::Commands::File; use Rex::Pkg::Base; use base qw(Rex::Pkg::Base); sub new { my $that = shift; my $proto = ref($that) || $that; my $self = $proto->SUPER::new(@_); bless( $self, $proto ); $self->{commands} = { install => 'opkg install %s', install_version => 'opkg install %s', remove => 'opkg remove %s', update_package_db => 'opkg update', }; return $self; } sub bulk_install { my ( $self, $packages_aref, $option ) = @_; delete $option->{version}; # makes no sense to specify the same version for several packages $self->update( "@{$packages_aref}", $option ); return 1; } sub update_system { my ($self) = @_; my @pkgs; my @lines = i_run("opkg list-upgradable"); for my $line (@lines) { if ( $line =~ m/^(.*) - .* - .*$/ ) { push( @pkgs, $1 ); } } my $packages_to_upgrade = join( " ", @pkgs ); i_run( "opkg upgrade " . $packages_to_upgrade ); } sub get_installed { my ( $self, $pkg ) = @_; my @pkgs; my $opkg_cmd = 'opkg list-installed'; if ($pkg) { $opkg_cmd .= ' | grep "^' . $pkg . ' "'; } my @lines = i_run $opkg_cmd; for my $line (@lines) { if ( $line =~ m/^(.*) - (.*)$/ ) { push( @pkgs, { name => $1, version => $2, } ); } } return @pkgs; } sub add_repository { my ( $self, %data ) = @_; append_if_no_such_line "/etc/opkg.conf", "src/gz " . $data{"name"} . " " . $data{"url"}, $data{"name"}, $data{"url"}; } sub rm_repository { my ( $self, $name ) = @_; delete_lines_matching "/etc/opkg.conf" => "src/gz " . $name . " "; } 1; Output000755001751001751 013232707066 13514 5ustar00janjan000000000000Rex-1.6.0/lib/RexBase.pm100644001751001751 55713232707066 15053 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Output# # (c) Nathan Abu # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Output::Base; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION sub write { die "Must be implemented by inheriting class" } sub add { die "Must be implemented by inheriting class" } sub error { die "Must be implemented by inheriting class" } 1; Commands000755001751001751 013232707066 13755 5ustar00janjan000000000000Rex-1.6.0/lib/RexFs.pm100644001751001751 6165113232707066 15054 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Commands# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: =head1 NAME Rex::Commands::Fs - Filesystem commands =head1 DESCRIPTION With this module you can do file system tasks like creating a directory, deleting files, moving files, and more. =head1 SYNOPSIS my @files = list_files "/etc"; unlink("/tmp/file"); rmdir("/tmp"); mkdir("/tmp"); my %stat = stat("/etc/passwd"); my $link = readlink("/path/to/a/link"); symlink("/source", "/dest"); rename("oldname", "newname"); chdir("/tmp"); is_file("/etc/passwd"); is_dir("/etc"); is_writeable("/tmp"); is_writable("/tmp"); chmod 755, "/tmp"; chown "user", "/tmp"; chgrp "group", "/tmp"; =head1 EXPORTED FUNCTIONS =cut package Rex::Commands::Fs; use strict; use warnings; our $VERSION = '1.6.0'; # VERSION require Rex::Exporter; use Data::Dumper; use Fcntl; use Rex::Helper::SSH2; use Rex::Helper::Path; use Rex::Commands; use Rex::Interface::Fs; use Rex::Interface::Exec; use Rex::Interface::File; use File::Basename; use Rex::Commands::MD5; use vars qw(@EXPORT); use base qw(Rex::Exporter); @EXPORT = qw(list_files ls unlink rm rmdir mkdir stat readlink symlink ln rename mv chdir cd cp chown chgrp chmod is_file is_dir is_readable is_writeable is_writable is_symlink df du mount umount glob); use vars qw(%file_handles); =head2 list_files("/path"); This function list all entries (files, directories, ...) in a given directory and returns a array. task "ls-etc", "server01", sub { my @tmp_files = grep { /\.tmp$/ } list_files("/etc"); }; This command will not be reported. =cut sub list_files { my $path = shift; $path = resolv_path($path); my $fs = Rex::Interface::Fs->create; my @ret = $fs->ls($path); return @ret; } =head2 ls($path) Just an alias for I =cut sub ls { return list_files(@_); } =head2 symlink($from, $to) This function will create a symlink from $from to $to. task "symlink", "server01", sub { symlink("/var/www/versions/1.0.0", "/var/www/html"); }; =cut sub symlink { my ( $from, $to ) = @_; $from = resolv_path($from); $to = resolv_path($to); Rex::get_current_connection()->{reporter} ->report_resource_start( type => "symlink", name => $to ); my $fs = Rex::Interface::Fs->create; if ( $fs->is_symlink($to) && $fs->readlink($to) eq $from ) { Rex::get_current_connection()->{reporter}->report( changed => 0, ); } else { $fs->ln( $from, $to ) or die("Can't link $from -> $to"); Rex::get_current_connection()->{reporter} ->report( changed => 1, message => "Symlink created: $from -> $to." ); } Rex::get_current_connection()->{reporter} ->report_resource_end( type => "symlink", name => $to ); return 1; } =head2 ln($from, $to) ln is an alias for I =cut sub ln { &symlink(@_); } =head2 unlink($file) This function will remove the given file. task "unlink", "server01", sub { unlink("/tmp/testfile"); }; =cut sub unlink { my @files = @_; my $f; if ( ref $files[0] eq "ARRAY" ) { $f = $files[0]; } else { $f = \@files; } if ( scalar @{$f} == 1 ) { my $file = resolv_path $f->[0]; my $fs = Rex::Interface::Fs->create; Rex::get_current_connection()->{reporter} ->report_resource_start( type => "unlink", name => $file ); if ( $fs->is_file($file) || $fs->is_symlink($file) ) { $fs->unlink($file); if ( $fs->is_file($file) || $fs->is_symlink($file) ) { die "Can't remove $file."; } my $tmp_path = Rex::Config->get_tmp_dir; if ( $file !~ m/^\Q$tmp_path\E[\/\\][a-z]+\.tmp$/ ) { # skip tmp rex files Rex::get_current_connection()->{reporter} ->report( changed => 1, message => "File $file removed." ); } } else { Rex::get_current_connection()->{reporter}->report( changed => 0, ); } Rex::get_current_connection()->{reporter} ->report_resource_end( type => "unlink", name => $file ); } else { &unlink($_) for @{$f}; } } =head2 rm($file) This is an alias for unlink. =cut sub rm { &unlink(@_); } =head2 rmdir($dir) This function will remove the given directory. task "rmdir", "server01", sub { rmdir("/tmp"); }; Since: 0.45 Please use the file() resource instead. task "prepare", sub { file "/tmp", ensure => "absent"; }; =cut sub rmdir { my @dirs = @_; my $d; if ( ref $dirs[0] eq "ARRAY" ) { $d = $dirs[0]; } else { $d = \@dirs; } if ( scalar @{$d} == 1 ) { my $dir = resolv_path $d->[0]; my $fs = Rex::Interface::Fs->create; Rex::get_current_connection()->{reporter} ->report_resource_start( type => "rmdir", name => $dir ); if ( !$fs->is_dir($dir) && $dir !~ m/[\*\[]/ ) { Rex::get_current_connection()->{reporter}->report( changed => 0, ); } else { $fs->rmdir($dir); if ( $fs->is_dir($dir) ) { die "Can't remove $dir."; } Rex::get_current_connection()->{reporter} ->report( changed => 1, message => "Directory $dir removed." ); } Rex::get_current_connection()->{reporter} ->report_resource_end( type => "rmdir", name => $dir ); } else { &rmdir($_) for @{$d}; } } =head2 mkdir($newdir) This function will create a new directory. Since: 0.45 Please use the file() resource instead. task "prepare", sub { file "/tmp", ensure => "directory", owner => "root", group => "root", mode => 1777; }; task "mkdir", "server01", sub { mkdir "/tmp"; mkdir "/tmp", owner => "root", group => "root", mode => 1777; }; =cut sub mkdir { Rex::Logger::debug("Creating directory $_[0]"); my $dir = shift; $dir = resolv_path($dir); my $options = {@_}; Rex::get_current_connection()->{reporter} ->report_resource_start( type => "mkdir", name => $dir ); my $fs = Rex::Interface::Fs->create; my $not_created = 0; my %old_stat; my $changed = 0; if ( $fs->is_dir($dir) ) { $not_created = 1; %old_stat = &stat($dir); } my $mode = $options->{"mode"} || 755; my $owner = $options->{"owner"} || ""; my $group = $options->{"group"} || ""; my $not_recursive = $options->{"not_recursive"} || 0; if ($not_recursive) { if ( !$fs->mkdir($dir) ) { Rex::Logger::debug("Can't create directory $dir"); die("Can't create directory $dir"); } &chown( $owner, $dir ) if $owner; &chgrp( $group, $dir ) if $group; &chmod( $mode, $dir ) if $mode; } else { my @splitted_dir; if ( Rex::is_ssh == 0 && $^O =~ m/^MSWin/ ) { # special case for local windows runs @splitted_dir = map { "\\$_"; } split( /[\\\/]/, $dir ); if ( $splitted_dir[0] =~ m/([a-z]):/i ) { $splitted_dir[0] = "$1:\\"; } else { $splitted_dir[0] =~ s/^\\//; } } else { @splitted_dir = map { "/$_"; } split( /\//, $dir ); unless ( $splitted_dir[0] eq "/" ) { $splitted_dir[0] = "." . $splitted_dir[0]; } else { shift @splitted_dir; } } my $str_part = ""; for my $part (@splitted_dir) { $str_part .= "$part"; if ( !is_dir($str_part) && !is_file($str_part) ) { if ( !$fs->mkdir($str_part) ) { Rex::Logger::debug("Can't create directory $dir"); die("Can't create directory $dir"); } &chown( $owner, $str_part ) if $owner; &chgrp( $group, $str_part ) if $group; &chmod( $mode, $str_part ) if $mode; } } } my %new_stat = &stat($dir); if ( !$not_created ) { Rex::get_current_connection()->{reporter} ->report( changed => 1, message => "Directory created." ); $changed = 1; } if ( %old_stat && $old_stat{uid} != $new_stat{uid} ) { Rex::get_current_connection()->{reporter} ->report( changed => 1, message => "Owner updated." ); $changed = 1; } if ( %old_stat && $old_stat{gid} != $new_stat{gid} ) { Rex::get_current_connection()->{reporter} ->report( changed => 1, message => "Group updated." ); $changed = 1; } if ( %old_stat && $old_stat{mode} ne $new_stat{mode} ) { Rex::get_current_connection()->{reporter} ->report( changed => 1, message => "Mode updated." ); $changed = 1; } if ( $changed == 0 ) { Rex::get_current_connection()->{reporter}->report( changed => 0, ); } Rex::get_current_connection()->{reporter} ->report_resource_end( type => "mkdir", name => $dir ); return 1; } =head2 chown($owner, $file) Change the owner of a file or a directory. chown "www-data", "/var/www/html"; chown "www-data", "/var/www/html", recursive => 1; This command will not be reported. If you want to use reports, please use the file() resource instead. =cut sub chown { my ( $user, $file, @opts ) = @_; $file = resolv_path($file); my $fs = Rex::Interface::Fs->create; $fs->chown( $user, $file, @opts ) or die("Can't chown $file"); } =head2 chgrp($group, $file) Change the group of a file or a directory. chgrp "nogroup", "/var/www/html"; chgrp "nogroup", "/var/www/html", recursive => 1; This command will not be reported. If you want to use reports, please use the file() resource instead. =cut sub chgrp { my ( $group, $file, @opts ) = @_; $file = resolv_path($file); my $fs = Rex::Interface::Fs->create; $fs->chgrp( $group, $file, @opts ) or die("Can't chgrp $file"); } =head2 chmod($mode, $file) Change the permissions of a file or a directory. chmod 755, "/var/www/html"; chmod 755, "/var/www/html", recursive => 1; This command will not be reported. If you want to use reports, please use the file() resource instead. =cut sub chmod { my ( $mode, $file, @opts ) = @_; $file = resolv_path($file); my $fs = Rex::Interface::Fs->create; $fs->chmod( $mode, $file, @opts ) or die("Can't chmod $file"); } =head2 stat($file) This function will return a hash with the following information about a file or directory. =over 4 =item mode =item size =item uid =item gid =item atime =item mtime =back task "stat", "server01", sub { my %file_stat = stat("/etc/passwd"); }; This command will not be reported. =cut sub stat { my ($file) = @_; $file = resolv_path($file); my %ret; Rex::Logger::debug("Getting fs stat from $file"); my $fs = Rex::Interface::Fs->create; # may return undef, so capture into a list first. my @stat = $fs->stat($file); die("Can't stat $file") if ( !defined $stat[0] && scalar @stat == 1 ); if ( scalar @stat % 2 ) { Rex::Logger::debug( 'stat output: ' . join ', ', @stat ); die('stat returned odd number of elements'); } %ret = @stat; return %ret; } =head2 is_file($file) This function tests if $file is a file. Returns 1 if true. 0 if false. task "isfile", "server01", sub { if( is_file("/etc/passwd") ) { say "it is a file."; } else { say "hm, this is not a file."; } }; This command will not be reported. =cut sub is_file { my ($file) = @_; $file = resolv_path($file); my $fs = Rex::Interface::Fs->create; return $fs->is_file($file); } =head2 is_dir($dir) This function tests if $dir is a directory. Returns 1 if true. 0 if false. task "isdir", "server01", sub { if( is_dir("/etc") ) { say "it is a directory."; } else { say "hm, this is not a directory."; } }; This command will not be reported. =cut sub is_dir { my ($path) = @_; $path = resolv_path($path); my $fs = Rex::Interface::Fs->create; return $fs->is_dir($path); } =head2 is_symlink($file) This function tests if $file is a symlink. Returns 1 if true. 0 if false. task "issym", "server01", sub { if( is_symlink("/etc/foo.txt") ) { say "it is a symlink."; } else { say "hm, this is not a symlink."; } }; This command will not be reported. =cut sub is_symlink { my ($path) = @_; $path = resolv_path($path); my $fs = Rex::Interface::Fs->create; return $fs->is_symlink($path); } =head2 is_readable($file) This function tests if $file is readable. It returns 1 if true. 0 if false. task "readable", "server01", sub { if( is_readable("/etc/passwd") ) { say "passwd is readable"; } else { say "not readable."; } }; This command will not be reported. =cut sub is_readable { my ($file) = @_; $file = resolv_path($file); Rex::Logger::debug("Checking if $file is readable"); my $fs = Rex::Interface::Fs->create; return $fs->is_readable($file); } =head2 is_writable($file) This function tests if $file is writable. It returns 1 if true. 0 if false. task "writable", "server01", sub { if( is_writable("/etc/passwd") ) { say "passwd is writable"; } else { say "not writable."; } }; This command will not be reported. =cut sub is_writable { my ($file) = @_; $file = resolv_path($file); Rex::Logger::debug("Checking if $file is writable"); my $fs = Rex::Interface::Fs->create; return $fs->is_writable($file); } =head2 is_writeable($file) This is only an alias for I. This command will not be reported. =cut sub is_writeable { is_writable(@_); } =head2 readlink($link) This function returns the link endpoint if $link is a symlink. If $link is not a symlink it will die. task "islink", "server01", sub { my $link; eval { $link = readlink("/tmp/testlink"); }; say "this is a link" if($link); }; This command will not be reported. =cut sub readlink { my ($file) = @_; $file = resolv_path($file); Rex::Logger::debug("Reading link of $file"); my $fs = Rex::Interface::Fs->create; my $link = $fs->readlink($file); unless ($link) { Rex::Logger::debug("readlink: $file is not a link."); die("readlink: $file is not a link."); } return $link; } =head2 rename($old, $new) This function will rename $old to $new. Will return 1 on success and 0 on failure. task "rename", "server01", sub { rename("/tmp/old", "/tmp/new"); }; =cut sub rename { my ( $old, $new ) = @_; $old = resolv_path($old); $new = resolv_path($new); Rex::get_current_connection()->{reporter} ->report_resource_start( type => "rename", name => "$old -> $new" ); my $fs = Rex::Interface::Fs->create; my $old_present = 0; if ( $fs->is_file($old) || $fs->is_dir($old) || $fs->is_symlink($old) ) { $old_present = 1; } Rex::Logger::debug("Renaming $old to $new"); if ( !$fs->rename( $old, $new ) ) { Rex::Logger::info("Rename failed ($old -> $new)"); die("Rename failed $old -> $new"); } my $new_present = 0; if ( $fs->is_file($new) || $fs->is_dir($new) || $fs->is_symlink($new) ) { $new_present = 1; } my $old_absent = 0; if ( !( $fs->is_file($old) || $fs->is_dir($old) || $fs->is_symlink($old) ) ) { $old_absent = 1; } if ( $old_present == 1 && $new_present == 1 && $old_absent == 1 ) { Rex::get_current_connection()->{reporter}->report( changed => 1 ); } else { Rex::get_current_connection()->{reporter}->report( changed => 0 ); } Rex::get_current_connection()->{reporter} ->report_resource_end( type => "rename", name => "$old -> $new" ); } =head2 mv($old, $new) mv is an alias for I. =cut sub mv { return &rename(@_); } =head2 chdir($newdir) This function will change the current workdirectory to $newdir. This function currently only works local. task "chdir", "server01", sub { chdir("/tmp"); }; This command will not be reported. =cut sub chdir { Rex::Logger::debug("chdir behaviour will be changed in the future."); CORE::chdir( $_[0] ); } =head2 cd($newdir) This is an alias of I. =cut sub cd { &chdir( $_[0] ); } =head2 df([$device]) This function returns a hashRef reflecting the output of I task "df", "server01", sub { my $df = df(); my $df_on_sda1 = df("/dev/sda1"); }; This command will not be reported. =cut sub df { my ($dev) = @_; my $ret = {}; $dev ||= ""; my $exec = Rex::Interface::Exec->create; my ( $out, $err ) = $exec->exec("df $dev 2>/dev/null"); my @lines = split( /\r?\n/, $out ); $ret = _parse_df(@lines); if ($dev) { if ( keys %$ret == 1 ) { ($dev) = keys %$ret; } return $ret->{$dev}; } return $ret; } sub _parse_df { my @lines = @_; chomp @lines; my $ret = {}; shift @lines; my $current_fs = ""; for my $line (@lines) { my ( $fs, $size, $used, $free, $use_per, $mounted_on ) = split( /\s+/, $line, 6 ); $current_fs = $fs if $fs; if ( !$size ) { next; } $ret->{$current_fs} = { size => $size, used => $used, free => $free, used_perc => $use_per, mounted_on => $mounted_on }; } return $ret; } =head2 du($path) Returns the disk usage of $path. task "du", "server01", sub { say "size of /var/www: " . du("/var/www"); }; This command will not be reported. =cut sub du { my ($path) = @_; $path = resolv_path($path); my $exec = Rex::Interface::Exec->create; my @lines = $exec->exec("du -s $path"); my ($du) = ( $lines[0] =~ m/^(\d+)/ ); return $du; } =head2 cp($source, $destination) cp will copy $source to $destination (it is recursive) task "cp", "server01", sub { cp("/var/www", "/var/www.old"); }; =cut sub cp { my ( $source, $dest ) = @_; $source = resolv_path($source); $dest = resolv_path($dest); Rex::get_current_connection()->{reporter} ->report_resource_start( type => "cp", name => "$source -> $dest" ); my $fs = Rex::Interface::Fs->create; my $new_present = 0; if ( $fs->is_file($source) && $fs->is_dir($dest) ) { $dest = "$dest/" . basename $source; } if ( $fs->is_file($dest) || $fs->is_dir($dest) || $fs->is_symlink($dest) ) { $new_present = 1; } if ( !$fs->cp( $source, $dest ) ) { die("Copy failed from $source to $dest"); } if ( $new_present == 0 ) { Rex::get_current_connection()->{reporter}->report( changed => 1, ); } else { Rex::get_current_connection()->{reporter}->report( changed => 0, ); } Rex::get_current_connection()->{reporter} ->report_resource_end( type => "cp", name => "$source -> $dest" ); } =head2 mount($device, $mount_point, @options) Mount devices. task "mount", "server01", sub { mount "/dev/sda5", "/tmp"; mount "/dev/sda6", "/mnt/sda6", ensure => "present", type => "ext3", options => [qw/noatime async/], on_change => sub { say "device mounted"; }; # # mount persistent with entry in /etc/fstab mount "/dev/sda6", "/mnt/sda6", ensure => "persistent", type => "ext3", options => [qw/noatime async/], on_change => sub { say "device mounted"; }; # to umount a device mount "/dev/sda6", "/mnt/sda6", ensure => "absent"; }; In order to be more aligned with `mount` terminology, the previously used `fs` option has been deprecated in favor of the `type` option. The `fs` option is still supported and works as previously, but Rex prints a warning if it is being used. There's also a warning if both `fs` and `type` options are specified, and in this case `type` will be used. =cut sub mount { my ( $device, $mount_point, @options ) = @_; my $option = {@options}; if ( defined $option->{fs} ) { Rex::Logger::info( 'The `fs` option of the mount command has been deprecated in favor of the `type` option. Please update your task.', 'warn' ); if ( !defined $option->{type} ) { $option->{type} = $option->{fs}; } else { Rex::Logger::info( 'Both `fs` and `type` options have been specified for mount command. Preferring `type`.', 'warn' ); } } delete $option->{fs}; Rex::get_current_connection()->{reporter} ->report_resource_start( type => "mount", name => "$mount_point" ); $option->{ensure} ||= "present"; # default if ( $option->{ensure} eq "absent" ) { &umount( $mount_point, device => $device, on_change => ( exists $option->{on_change} ? $option->{on_change} : undef ) ); } else { if ( $option->{ensure} eq "persistent" ) { $option->{persistent} = 1; } my $changed = 0; my $exec = Rex::Interface::Exec->create; my ( $m_out, $m_err ) = $exec->exec("mount"); my @mounted = split( /\r?\n/, $m_out ); my ($already_mounted) = grep { m/$device on $mount_point/ } @mounted; if ($already_mounted) { Rex::Logger::debug("Device ($device) already mounted on $mount_point."); $changed = 0; } my $cmd = sprintf( "mount %s %s %s %s", $option->{type} ? "-t " . $option->{type} : "", # file system $option->{"options"} ? " -o " . join( ",", @{ $option->{"options"} } ) : "", $device, $mount_point ); unless ($already_mounted) { $exec->exec($cmd); if ( $? != 0 ) { die("Mount failed of $mount_point"); } $changed = 1; Rex::get_current_connection()->{reporter}->report( changed => 1, message => "Device $device mounted on $mount_point." ); } if ( exists $option->{persistent} ) { if ( !exists $option->{type} ) { # no fs given, so get it from mount output my ( $out, $err ) = $exec->exec("mount"); my @output = split( /\r?\n/, $out ); my ($line) = grep { /^$device/ } @output; my ( $_d, $_o, $_p, $_t, $fs_type ) = split( /\s+/, $line ); $option->{type} = $fs_type; my ($_options) = ( $line =~ m/\((.+?)\)/ ); $option->{options} = $_options; } my $fh = Rex::Interface::File->create; my $old_md5 = md5("/etc/fstab"); if ( !$fh->open( "<", "/etc/fstab" ) ) { Rex::Logger::debug("Can't open /etc/fstab for reading."); die("Can't open /etc/fstab for reading."); } my $f = Rex::FS::File->new( fh => $fh ); my @content = $f->read_all; $f->close; my @new_content = grep { !/^$device\s/ } @content; $option->{options} ||= "defaults"; if ( ref( $option->{options} ) eq "ARRAY" ) { my $mountops = join( ",", @{ $option->{"options"} } ); if ( $option->{label} ) { push( @new_content, "LABEL=" . $option->{label} . "\t$mount_point\t$option->{type}\t$mountops\t0 0\n" ); } else { push( @new_content, "$device\t$mount_point\t$option->{type}\t$mountops\t0 0\n" ); } } else { if ( $option->{label} ) { push( @new_content, "LABEL=" . $option->{label} . "\t$mount_point\t$option->{type}\t$option->{options}\t0 0\n" ); } else { push( @new_content, "$device\t$mount_point\t$option->{type}\t$option->{options}\t0 0\n" ); } } $fh = Rex::Interface::File->create; if ( !$fh->open( ">", "/etc/fstab" ) ) { Rex::Logger::debug("Can't open /etc/fstab for writing."); die("Can't open /etc/fstab for writing."); } $f = Rex::FS::File->new( fh => $fh ); $f->write( join( "\n", @new_content ) ); $f->close; my $new_md5 = md5("/etc/fstab"); if ( $new_md5 ne $old_md5 ) { Rex::get_current_connection()->{reporter} ->report( changed => 1, message => "File /etc/fstab updated." ); $changed = 1; } } if ( $changed == 1 ) { if ( exists $option->{on_change} && ref $option->{on_change} eq "CODE" ) { $option->{on_change}->( $device, $mount_point ); } } } Rex::get_current_connection()->{reporter} ->report_resource_end( type => "mount", name => "$mount_point" ); } =head2 umount($mount_point) Unmount device. task "umount", "server01", sub { umount "/tmp"; }; =cut sub umount { my ( $mount_point, %option ) = @_; my $device; if ( exists $option{device} ) { $device = $option{device}; } my $exec = Rex::Interface::Exec->create; Rex::get_current_connection()->{reporter} ->report_resource_start( type => "umount", name => "$mount_point" ); my $changed = 0; my ( $m_out, $m_err ) = $exec->exec("mount"); my @mounted = split( /\r?\n/, $m_out ); my $already_mounted; if ($device) { ($already_mounted) = grep { m/$device on $mount_point/ } @mounted; } else { ($already_mounted) = grep { m/on $mount_point/ } @mounted; } if ($already_mounted) { $exec->exec("umount $mount_point"); if ( $? != 0 ) { die("Umount failed of $mount_point"); } $changed = 1; } if ($changed) { if ( exists $option{on_change} && ref $option{on_change} eq "CODE" ) { $option{on_change}->( $mount_point, %option ); } Rex::get_current_connection()->{reporter} ->report( changed => 1, message => "Unmounted $mount_point." ); } Rex::get_current_connection()->{reporter} ->report_resource_end( type => "umount", name => "$mount_point" ); } =head2 glob($glob) task "glob", "server1", sub { my @files_with_p = grep { is_file($_) } glob("/etc/p*"); }; This command will not be reported. =cut sub glob { my ($glob) = @_; $glob = resolv_path($glob); my $fs = Rex::Interface::Fs->create; return $fs->glob($glob); } 1; DB.pm100644001751001751 1071413232707066 14763 0ustar00janjan000000000000Rex-1.6.0/lib/Rex/Commands# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: =head1 NAME Rex::Commands::DB - Simple Database Access =head1 DESCRIPTION This module gives you simple access to a database. Currently I