Rex-1.8.1/0000755000175000017500000000000013616635656011262 5ustar ferkiferkiRex-1.8.1/README0000644000175000017500000000046613616635656012150 0ustar ferkiferkiThis archive contains the distribution Rex, version 1.8.1: the friendly automation framework This software is Copyright (c) 2020 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.012. Rex-1.8.1/t/0000755000175000017500000000000013616635656011525 5ustar ferkiferkiRex-1.8.1/t/df.t0000644000175000017500000000555513616635656012315 0ustar ferkiferkiuse 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.8.1/t/fs.t0000644000175000017500000000044213616635656012322 0ustar ferkiferkiuse 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" ); Rex-1.8.1/t/db.t0000644000175000017500000000523713616635656012306 0ustar ferkiferkiuse 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" ); } Rex-1.8.1/t/01.t0000644000175000017500000000061313616635656012132 0ustar ferkiferkiuse 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; Rex-1.8.1/t/md5.t0000644000175000017500000000034613616635656012402 0ustar ferkiferkiuse 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' ); Rex-1.8.1/t/env.t0000644000175000017500000000037513616635656012507 0ustar ferkiferkiuse 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( q(perl -e 'print $ENV{REX}'), env => { 'REX' => 'XER' } ); like( $s, qr/XER/, "run with env" ); } Rex-1.8.1/t/ini.t0000644000175000017500000000705213616635656012475 0ustar ferkiferkiuse 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); } Rex-1.8.1/t/xml.t0000644000175000017500000000305213616635656012512 0ustar ferkiferkiuse 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); } Rex-1.8.1/t/dmi.t0000644000175000017500000001053213616635656012464 0ustar ferkiferkiuse 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" ); Rex-1.8.1/t/0.54.t0000644000175000017500000000226713616635656012307 0ustar ferkiferkiuse 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" ); Rex-1.8.1/t/case.t0000644000175000017500000000203013616635656012620 0ustar ferkiferkiuse 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" ); Rex-1.8.1/t/path.t0000644000175000017500000000074013616635656012647 0ustar ferkiferkiuse 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" ); } Rex-1.8.1/t/args.t0000644000175000017500000000154713616635656012655 0ustar ferkiferkiuse 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; Rex-1.8.1/t/auth.t0000644000175000017500000000667613616635656012672 0ustar ferkiferkiuse 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" ); Rex-1.8.1/t/base.t0000644000175000017500000000121313616635656012621 0ustar ferkiferkiuse 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 ); } Rex-1.8.1/t/cmdb.t0000644000175000017500000000574413616635656012631 0ustar ferkiferkiuse strict; use warnings; use Test::More tests => 2; use Rex::CMDB; use Rex::Commands; use Rex::Commands::File; foreach my $caching (qw(0 1)) { my $setting = $caching ? 'enabled' : 'disabled'; subtest "Caching ${setting}" => sub { plan tests => 13; Rex::Config->set_use_cache($caching); 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" ); } } Rex-1.8.1/t/file.t0000644000175000017500000002035013616635656012631 0ustar ferkiferkiuse 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" ); Rex-1.8.1/t/task.t0000644000175000017500000001043213616635656012654 0ustar ferkiferkiuse 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" ); Rex-1.8.1/t/0.31.t0000644000175000017500000001270513616635656012300 0ustar ferkiferkiuse 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" ); Rex-1.8.1/t/cron.t0000644000175000017500000005350713616635656012665 0ustar ferkiferkiuse 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" ); } Rex-1.8.1/t/host.t0000644000175000017500000000175513616635656012677 0ustar ferkiferkiuse 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" ); Rex-1.8.1/t/cron.ex0000644000175000017500000000130013616635656013016 0ustar ferkiferki#----------------------------------------------------------------- # 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 #----------------------------------------------------------------- Rex-1.8.1/t/df.out10000644000175000017500000000043613616635656012733 0ustar ferkiferkiFilesystem 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 Rex-1.8.1/t/df.out20000644000175000017500000000037413616635656012735 0ustar ferkiferkiFilesystem 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 Rex-1.8.1/t/ip.out10000644000175000017500000000030613616635656012746 0ustar ferkiferki2: 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 Rex-1.8.1/t/ip.out20000644000175000017500000000017713616635656012755 0ustar ferkiferki3: eth1: mtu 1500 qdisc noop state DOWN qlen 1000 link/ether 00:1c:42:73:ad:3c brd ff:ff:ff:ff:ff:ff Rex-1.8.1/t/ip.out30000644000175000017500000000023213616635656012746 0ustar ferkiferki4: ppp0: mtu 1454 qdisc pfifo_fast qlen 3 link/ppp inet 123.117.251.17 peer 234.165.249.179/32 scope global ppp0 Rex-1.8.1/t/group.t0000644000175000017500000000341713616635656013053 0ustar ferkiferkiuse 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"; Rex-1.8.1/t/batch.t0000644000175000017500000000076613616635656013004 0ustar ferkiferkiuse 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." ); Rex-1.8.1/t/hooks.t0000644000175000017500000000262413616635656013041 0ustar ferkiferkiuse 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'; Rex-1.8.1/t/needs.t0000644000175000017500000000326213616635656013013 0ustar ferkiferkiuse 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; } { package Nested::Module; use strict; use warnings; use Rex::Commands; task "test", sub { open( my $fh, ">", "test.txt" ); close($fh); }; } { package Rex::Module; use strict; use warnings; use Rex::Commands; task "test", sub { open( my $fh, ">", "test.txt" ); close($fh); }; } 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); }; task "test5", sub { needs Nested::Module "test"; if ( -f "test.txt" ) { unlink("test.txt"); return 1; } die; }; task "test6", sub { needs Rex::Module "test"; if ( -f "test.txt" ) { unlink("test.txt"); return 1; } die; }; my $task_list = Rex::TaskList->create; my $run_list = Rex::RunList->instance; $run_list->parse_opts(qw/test test2 test3 test5 test6/); 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; Rex-1.8.1/t/hosts.ex0000644000175000017500000000025513616635656013225 0ustar ferkiferki# 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 Rex-1.8.1/t/test.xml0000644000175000017500000000200213616635656013220 0ustar ferkiferki Rex-1.8.1/t/test.ini0000644000175000017500000000060213616635656013203 0ustar ferkiferki; 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 Rex-1.8.1/t/config.t0000644000175000017500000000220513616635656013156 0ustar ferkiferkiuse 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; Rex-1.8.1/t/logger.t0000644000175000017500000000345313616635656013176 0ustar ferkiferki#!/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; } Rex-1.8.1/t/no_tty.t0000644000175000017500000000077313616635656013235 0ustar ferkiferkiuse 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" ); } Rex-1.8.1/t/shared.t0000644000175000017500000000405513616635656013164 0ustar ferkiferkiuse 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'; } Rex-1.8.1/t/report.t0000644000175000017500000000272013616635656013226 0ustar ferkiferkiuse 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"); } Rex-1.8.1/t/hosts.ex20000644000175000017500000000026213616635656013305 0ustar ferkiferki127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 1.2.3.4 rexify.org Rex-1.8.1/t/package.t0000644000175000017500000000163613616635656013313 0ustar ferkiferkiuse 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; Rex-1.8.1/t/include.t0000644000175000017500000000101213616635656013327 0ustar ferkiferkiuse 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(); Rex-1.8.1/t/can_run.t0000644000175000017500000000155213616635656013342 0ustar ferkiferkiuse 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' ); } Rex-1.8.1/t/do_task.t0000644000175000017500000000106413616635656013337 0ustar ferkiferkiuse 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' ); Rex-1.8.1/t/runlist.t0000644000175000017500000000177313616635656013422 0ustar ferkiferkiuse 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; Rex-1.8.1/t/summary.t0000644000175000017500000000670613616635656013420 0ustar ferkiferkiuse 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 => 1 }, 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 => 1 }, 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/ ? "type" : "cat"; 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; } Rex-1.8.1/t/resource.t0000644000175000017500000000325513616635656013546 0ustar ferkiferkiuse 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(); Rex-1.8.1/t/commands.t0000644000175000017500000000407613616635656013522 0ustar ferkiferkiuse 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" ); Rex-1.8.1/t/fs_files.t0000644000175000017500000000602513616635656013507 0ustar ferkiferki#!/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'; } Rex-1.8.1/t/symlinks.t0000644000175000017500000000477513616635656013600 0ustar ferkiferkiuse strict; use warnings; use Test::More; use File::Spec; use File::Temp qw(tempdir); use Rex -base; use Rex::Helper::Path; if ( $^O =~ m/^MSWin/ ) { plan skip_all => 'No symlink support on Windows'; } $::QUIET = 1; my $tmp_dir = tempdir( CLEANUP => 1 ); my $file = File::Spec->catfile( $tmp_dir, 'file' ); my $symlink = File::Spec->catfile( $tmp_dir, 'symlink' ); my $nested_symlink = File::Spec->catfile( $tmp_dir, 'nested_symlink' ); sub setup { file $file, ensure => 'present'; symlink $file, $symlink; symlink $symlink, $nested_symlink; is( is_file($file), TRUE, 'temp file is a file' ); is( is_symlink($symlink), TRUE, 'temp symlink is a symlink' ); is( is_symlink($nested_symlink), TRUE, 'temp nested symlink is a symlink' ); } sub check_still_symlink { is( is_symlink($symlink), TRUE, 'temp symlink is still a symlink' ); is( resolve_symlink($symlink), $file, 'symlink is still resolved to file' ); } subtest 'resolve symlinks' => sub { setup(); is( resolve_symlink($symlink), $file, 'symlink is resolved to file' ); is( resolve_symlink($nested_symlink), $file, 'nested symlink is resolved to file' ); is( resolve_symlink('not a symlink'), undef, 'non-existing symlink is unresolved' ); }; subtest 'file command with symlinks' => sub { setup(); file $symlink, content => '1'; is( cat($file), "1\n", 'file content written' ); check_still_symlink(); file $symlink, ensure => 'absent'; is( is_file($file), TRUE, 'file is still present' ); ok( !-e $symlink, 'symlink is gone' ); }; subtest 'delete_lines_matching with symlinks' => sub { setup(); delete_lines_matching $symlink, '1'; is( cat($file), "", 'line deleted' ); check_still_symlink(); }; subtest 'append_or_amend_line with symlinks' => sub { setup(); append_or_amend_line $symlink, line => '2', regexp => qr{1}; is( cat($file), "2\n", 'line updated' ); check_still_symlink(); }; subtest 'sed with symlinks' => sub { setup(); sed qr{1}, '2', $symlink; is( cat($file), "2\n", 'line updated' ); check_still_symlink(); }; subtest 'file_write with symlinks' => sub { setup(); my $fh = file_write $symlink; $fh->write(''); $fh->close; is( cat($file), '', 'file is empty' ); check_still_symlink(); }; subtest 'file_append with symlinks' => sub { setup(); my $fh = file_append $symlink; $fh->write("1\n"); $fh->close; is( cat($file), "1\n", 'file has been appended' ); check_still_symlink(); }; done_testing(); Rex-1.8.1/t/template.t0000644000175000017500000000504513616635656013531 0ustar ferkiferkiuse 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} ); } Rex-1.8.1/t/md5test.bin0000644000175000017500000000000113616635656013573 0ustar ferkiferkiRex-1.8.1/t/base_virt.t0000644000175000017500000000056113616635656013672 0ustar ferkiferkiuse 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" ); Rex-1.8.1/t/issue_539.t0000644000175000017500000000276513616635656013454 0ustar ferkiferkiuse 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" ); Rex-1.8.1/t/issue/0000755000175000017500000000000013616635656012655 5ustar ferkiferkiRex-1.8.1/t/issue/513.t0000644000175000017500000000057413616635656013360 0ustar ferkiferkiuse 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." ); Rex-1.8.1/t/issue/949.t0000644000175000017500000000111413616635656013364 0ustar ferkiferkiuse 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."; }; Rex-1.8.1/t/issue/860.t0000644000175000017500000000332213616635656013357 0ustar ferkiferkiuse 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" ); Rex-1.8.1/t/issue/934.t0000644000175000017500000000105013616635656013355 0ustar ferkiferkiuse 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; Rex-1.8.1/t/issue/948.t0000644000175000017500000000307013616635656013366 0ustar ferkiferkiuse 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." ); Rex-1.8.1/t/issue/1008.t0000644000175000017500000000371313616635656013436 0ustar ferkiferkiuse 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" ); Rex-1.8.1/t/issue/513_t1.rex0000644000175000017500000000006213616635656014307 0ustar ferkiferkiuse Rex -base; 0; # simulate false return value Rex-1.8.1/t/issue/513_t2.rex0000644000175000017500000000006113616635656014307 0ustar ferkiferkiuse Rex -base; 1; # simulate true return value Rex-1.8.1/t/issue/948/0000755000175000017500000000000013616635656013201 5ustar ferkiferkiRex-1.8.1/t/issue/948/capabilities.xml0000644000175000017500000002351613616635656016363 0ustar ferkiferki 9d400c30-4e27-11e0-a18b-1c7508ed862e x86_64 SandyBridge Intel tcp rdma 16282820 4070705 0 apparmor 0 dac 0 +121:+129 +121:+129 hvm 32 /usr/bin/qemu-system-i386 ubuntu pc-0.12 pc-1.3 pc-q35-1.6 pc-q35-1.5 pc-i440fx-1.6 pc-q35-2.2 xenpv pc-i440fx-1.7 pc-0.11 pc-q35-2.1 pc pc-0.10 pc-i440fx-trusty pc-1.2 pc-i440fx-2.2 isapc pc-q35-1.4 xenfv pc-0.15 ubuntu pc-i440fx-1.5 pc-0.14 pc-i440fx-1.4 pc-q35-2.0 pc-1.1 pc-q35-1.7 pc-i440fx-2.1 pc-1.0 pc-i440fx-2.0 q35 pc-0.13 /usr/bin/kvm-spice ubuntu pc-1.3 pc-0.12 pc-q35-1.6 pc-q35-1.5 pc-i440fx-1.6 pc-q35-2.2 pc-i440fx-1.7 xenpv pc-q35-2.1 pc-0.11 pc-0.10 pc-i440fx-trusty pc-i440fx-2.2 pc-1.2 isapc pc pc-q35-1.4 xenfv pc-0.15 ubuntu pc-i440fx-1.5 pc-i440fx-1.4 pc-q35-2.0 pc-0.14 pc-1.1 pc-q35-1.7 pc-i440fx-2.1 pc-1.0 pc-i440fx-2.0 q35 pc-0.13 hvm 64 /usr/bin/qemu-system-x86_64 ubuntu pc-1.3 pc-0.12 pc-q35-1.6 pc-q35-1.5 pc-i440fx-1.6 pc-q35-2.2 pc-i440fx-1.7 xenpv pc-q35-2.1 pc-0.11 pc-0.10 pc-i440fx-trusty pc-i440fx-2.2 pc-1.2 isapc pc pc-q35-1.4 xenfv pc-0.15 ubuntu pc-i440fx-1.5 pc-i440fx-1.4 pc-q35-2.0 pc-0.14 pc-1.1 pc-q35-1.7 pc-i440fx-2.1 pc-1.0 pc-i440fx-2.0 q35 pc-0.13 /usr/bin/kvm-spice ubuntu pc-1.3 pc-0.12 pc-q35-1.6 pc-q35-1.5 pc-i440fx-1.6 pc-q35-2.2 pc-i440fx-1.7 xenpv pc-q35-2.1 pc-0.11 pc-0.10 pc-i440fx-trusty pc-i440fx-2.2 pc-1.2 isapc pc pc-q35-1.4 xenfv pc-0.15 ubuntu pc-i440fx-1.5 pc-i440fx-1.4 pc-q35-2.0 pc-0.14 pc-1.1 pc-q35-1.7 pc-i440fx-2.1 pc-1.0 pc-i440fx-2.0 q35 pc-0.13 Rex-1.8.1/t/dmi.fbsd.out0000644000175000017500000001270213616635656013746 0ustar ferkiferki# 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 Rex-1.8.1/t/dmi.obsd.out0000644000175000017500000001270213616635656013757 0ustar ferkiferki# 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 Rex-1.8.1/t/ssh_config.10000644000175000017500000000007713616635656013735 0ustar ferkiferkiHost * StrictHostKeyChecking no UserKnownHostsFile=/dev/null Rex-1.8.1/t/url_encode.t0000644000175000017500000000066413616635656014037 0ustar ferkiferkiuse 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_" ); Rex-1.8.1/t/task_hosts.t0000644000175000017500000000106013616635656014071 0ustar ferkiferkipackage 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'" ); Rex-1.8.1/t/config-ssh.t0000644000175000017500000000405213616635656013753 0ustar ferkiferkiuse 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; Rex-1.8.1/t/cmdb/0000755000175000017500000000000013616635656012432 5ustar ferkiferkiRex-1.8.1/t/cmdb/foo.yml0000644000175000017500000000001213616635656013731 0ustar ferkiferkiname: foo Rex-1.8.1/t/cmdb/default.yml0000644000175000017500000000022213616635656014575 0ustar ferkiferkintp: - ntp1 - ntp2 name: defaultname newntp: - ntp1 - ntp2 users: root: id: 0 password: proot MyTest::foo::mode: '0666' Rex-1.8.1/t/cmdb/default/0000755000175000017500000000000013616635656014056 5ustar ferkiferkiRex-1.8.1/t/cmdb/default/foo.yml0000644000175000017500000000017013616635656015362 0ustar ferkiferkivhost: name: foohost doc_root: /var/www vhost2: name: vhost2foo newntp: - ntpdefaultfoo01 - ntpdefaultfoo02 Rex-1.8.1/t/cmdb/default/default.yml0000644000175000017500000000025713616635656016231 0ustar ferkiferkidns: - 1.1.1.1 - 2.2.2.2 vhost2: name: defaulthost doc_root: /var/www users: user01: id: 500 password: puser01 user02: id: 600 password: puser02 Rex-1.8.1/t/dmi.linux.out0000644000175000017500000001270213616635656014167 0ustar ferkiferki# 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 Rex-1.8.1/t/ifconfig.out10000644000175000017500000000076113616635656014127 0ustar ferkiferkieth0 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) Rex-1.8.1/t/ifconfig.out20000644000175000017500000000066413616635656014132 0ustar ferkiferkivif1.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) Rex-1.8.1/t/ifconfig.out30000644000175000017500000000056513616635656014133 0ustar ferkiferkieth0 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 Rex-1.8.1/t/ifconfig.out40000644000175000017500000000075713616635656014137 0ustar ferkiferkiwlan0 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) Rex-1.8.1/t/ifconfig.out50000644000175000017500000000100013616635656014116 0ustar ferkiferkieth1 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) Rex-1.8.1/t/ifconfig.out60000644000175000017500000000251413616635656014132 0ustar ferkiferkieth0: 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 Rex-1.8.1/t/ifconfig.out70000644000175000017500000000067313616635656014137 0ustar ferkiferkippp0 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) Rex-1.8.1/t/helper_hash.t0000644000175000017500000000201713616635656014174 0ustar ferkiferkiuse 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" ); Rex-1.8.1/t/template_ng.t0000644000175000017500000000574313616635656014222 0ustar ferkiferkiuse 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} ); } Rex-1.8.1/t/helper_path.t0000644000175000017500000000376513616635656014220 0ustar ferkiferkiuse 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' ); Rex-1.8.1/t/ip.out_centos70000644000175000017500000000136313616635656014333 0ustar ferkiferki1: 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 Rex-1.8.1/t/read_buffers.t0000644000175000017500000000156613616635656014351 0ustar ferkiferkiuse 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'; } Rex-1.8.1/t/hooks_in_pkg.t0000644000175000017500000000177313616635656014374 0ustar ferkiferkiuse strict; use warnings; use lib 't/lib'; use Test::More; use Test::Deep; use Rex::Commands; use Rex::Commands::Run; use t::tasks::chicken; $::QUIET = 1; my $task_list = Rex::TaskList->create; my ($task_name) = $task_list->get_tasks; is( $task_name, "t:tasks:chicken:cross_road", "found_task" ); my $task = $task_list->get_task($task_name); my $bts = $task->{before_task_start}; is( @$bts, 2, "found 2 before_task_start hooks" ); is( ref $bts->[0] eq "CODE" ? $bts->[0]->() : undef, "look left", "first before_task_start hook executes" ); is( ref $bts->[1] eq "CODE" ? $bts->[1]->() : undef, "look right", "second before_task_start hook executes" ); my $atf = $task->{after_task_finished}; is( @$atf, 2, "found 2 after_task_finished hooks" ); is( ref $atf->[0] eq "CODE" ? $atf->[0]->() : undef, "got to the other side", "first after_task_finished hook executes" ); is( ref $atf->[1] eq "CODE" ? $atf->[1]->() : undef, "celebrate!", "second after_task_finished hook executes" ); done_testing(); Rex-1.8.1/t/param_lookup.t0000644000175000017500000000150313616635656014402 0ustar ferkiferki 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" } ); Rex-1.8.1/t/network_linux.t0000644000175000017500000001005613616635656014624 0ustar ferkiferkiuse 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" ); Rex-1.8.1/t/ip.out_issue_5390000644000175000017500000000115113616635656014474 0ustar ferkiferki1: 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 Rex-1.8.1/t/virtualization.t0000644000175000017500000000102413616635656014773 0ustar ferkiferkiuse 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" ); Rex-1.8.1/t/before_all_tasks.t0000644000175000017500000000103213616635656015205 0ustar ferkiferkiuse 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(); Rex-1.8.1/t/param_lookup_cmdb.t0000644000175000017500000000053413616635656015372 0ustar ferkiferki 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(); Rex-1.8.1/t/commands/0000755000175000017500000000000013616635656013326 5ustar ferkiferkiRex-1.8.1/t/commands/iptables.t0000644000175000017500000000540213616635656015317 0ustar ferkiferkiuse 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" ); Rex-1.8.1/t/commands/file/0000755000175000017500000000000013616635656014245 5ustar ferkiferkiRex-1.8.1/t/commands/file/test.tpl0000644000175000017500000000002113616635656015736 0ustar ferkiferki<%= $basename %> Rex-1.8.1/t/commands/evaluate_hostnames.t0000644000175000017500000000315313616635656017404 0ustar ferkiferki#!/usr/bin/env perl use strict; use warnings; use Test::More tests => 9; use Rex::Commands; my %tests = ( 'server1.domain.com' => [qw/server1.domain.com/], 'server[9..10].domain.com' => [ qw/ server9.domain.com server10.domain.com / ], 'server[6..10/2].domain.com' => [ qw/ server6.domain.com server8.domain.com server10.domain.com / ], 'server[6,8,10].domain.com' => [ qw/ server6.domain.com server8.domain.com server10.domain.com / ], 'server[4..6,8,10..12].domain.com' => [ qw/ server4.domain.com server5.domain.com server6.domain.com server8.domain.com server10.domain.com server11.domain.com server12.domain.com / ], 'server[4..6,8,10..16/2].domain.com' => [ qw/ server4.domain.com server5.domain.com server6.domain.com server8.domain.com server10.domain.com server12.domain.com server14.domain.com server16.domain.com / ], 'server[1..3,2..4].domain.com' => [ qw/ server1.domain.com server2.domain.com server3.domain.com server2.domain.com server3.domain.com server4.domain.com / ], 'server[01..03].domain.com' => [ qw/ server01.domain.com server02.domain.com server03.domain.com / ], 'server[09..11/2].domain.com' => [ qw/ server09.domain.com server11.domain.com / ], ); for my $test ( sort keys %tests ) { my @result = Rex::Commands::evaluate_hostname($test); is_deeply \@result, $tests{$test}, $test; } Rex-1.8.1/t/ip.out_centos7_alias0000644000175000017500000000133113616635656015477 0ustar ferkiferki1: 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 Rex-1.8.1/t/interface_fs_local.t0000644000175000017500000000112713616635656015515 0ustar ferkiferkiuse 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" ); Rex-1.8.1/t/lib/0000755000175000017500000000000013616635656012273 5ustar ferkiferkiRex-1.8.1/t/lib/t/0000755000175000017500000000000013616635656012536 5ustar ferkiferkiRex-1.8.1/t/lib/t/tasks/0000755000175000017500000000000013616635656013663 5ustar ferkiferkiRex-1.8.1/t/lib/t/tasks/alien.pm0000644000175000017500000000016513616635656015313 0ustar ferkiferkipackage t::tasks::alien; use Rex -base; desc "negotiate with the aliens"; task "negotiate" => sub { return 1 }; 1; Rex-1.8.1/t/lib/t/tasks/cowboy.pm0000644000175000017500000000021213616635656015516 0ustar ferkiferkipackage t::tasks::cowboy; use Rex -base; desc "bring in the cattle"; task "roundup" => sub { return 1 }; include 't::tasks::alien'; 1; Rex-1.8.1/t/lib/t/tasks/chicken.pm0000644000175000017500000000062013616635656015623 0ustar ferkiferkipackage t::tasks::chicken; use Rex -base; desc "cross the road"; task "cross_road" => sub { return "make a break for it!" }; before_task_start "cross_road" => sub { return "look left" }; before_task_start "cross_road" => sub { return "look right" }; after_task_finished "cross_road" => sub { return "got to the other side" }; after_task_finished "cross_road" => sub { return "celebrate!" }; 1; Rex-1.8.1/t/lib/t/tasks/cowbefore.pm0000644000175000017500000000022413616635656016172 0ustar ferkiferkipackage t::tasks::cowbefore; use Rex -base; desc "bring in the cattle"; task "roundup" => sub { return 1 }; before ALL => sub { return 'yo' }; 1; Rex-1.8.1/t/last_command_output.t0000644000175000017500000000052013616635656015770 0ustar ferkiferkiuse Test::More tests => 3; use Rex::Commands; use Rex::Commands::Run; $::QUIET = 1; my $command = ( $^O =~ /MSWin/ ) ? 'dir' : 'ls -l'; run($command); my $s = last_command_output(); like( $s, qr/ChangeLog/ms ); $command .= ' t'; run($command); $s = last_command_output(); unlike( $s, qr/ChangeLog/ms ); like( $s, qr/auth\.t/ms ); Rex-1.8.1/t/net_interface_centos7.t0000644000175000017500000000130613616635656016162 0ustar ferkiferkiuse Test::More tests => 6; use Rex::Hardware::Network::Linux; my @in = eval { local (@ARGV) = ("t/ip.out_centos7"); <>; }; my $info = Rex::Hardware::Network::Linux::_parse_ip(@in); is( $info->{lo}->{netmask}, '255.0.0.0', 'loopback netmask' ); is( $info->{lo}->{ip}, '127.0.0.1', 'loopback ip' ); is( $info->{eth0}->{ip}, '10.211.55.171', 'eth0 ip' ); is( $info->{eth0}->{netmask}, '255.255.255.0', 'eth0 netmask' ); is( $info->{eth0}->{broadcast}, '10.211.55.255', 'eth0 broadcast' ); is( $info->{eth0}->{mac}, '00:1c:42:fe:5a:b5', 'eth0 mac' ); @in = eval { local (@ARGV) = ("t/ip.out_centos7_alias"); <>; }; $info = Rex::Hardware::Network::Linux::_parse_ip(@in); 1; Rex-1.8.1/t/param_lookup_resource.t0000644000175000017500000000112013616635656016304 0ustar ferkiferkipackage MyTest; use Rex -base; use Rex::Resource::Common; use Test::More; $::QUIET = 1; resource "foo", sub { my $test_name = resource_name; my $mode = param_lookup "mode", "0755"; is( $test_name, "testname", "resource name is testname" ); is( $mode, "0666", "got mode 0666" ); }; 1; package main; use Rex -base; use Test::More; use Rex::CMDB; use Rex::Commands; import MyTest; set( cmdb => { type => "YAML", path => "t/cmdb", } ); use Test::More tests => 2; use Rex -base; task( "test1", sub { MyTest::foo("testname"); } ); test1(); Rex-1.8.1/t/commands_file_template.t0000644000175000017500000000175113616635656016411 0ustar ferkiferki#!/usr/bin/perl use strict; use warnings; use Test::More tests => 4; use File::Basename; use Rex::Commands::File; my $basename = basename __FILE__; { # templates from file my $tpl = Rex::Helper::File::Spec->catfile( dirname(__FILE__), 'commands', 'file', 'test.tpl', ); my $content = template $tpl, basename => $basename; is $content, $basename . "\n", "template from file"; } { # test templates from __DATA__ my $content = template '@second.tpl', basename => $basename; is $content, $basename . "\n", "second template from __DATA__"; my $name = 'Rex'; my $content_first = template '@first.tpl', name => { test => $name }; is $content_first, $name . "\n", "first template from __DATA__"; } { # passing template content my $content = template \'<%= $basename %>', basename => $basename; is $content, $basename, "passing template content"; } __DATA__ @first.tpl <%= $name->{test} %> @end @second.tpl <%= $basename %> @end Rex-1.8.1/LICENSE0000644000175000017500000002635313616635656012300 0ustar ferkiferkiThis software is Copyright (c) 2020 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. Rex-1.8.1/bin/0000755000175000017500000000000013616635656012032 5ustar ferkiferkiRex-1.8.1/bin/rex0000755000175000017500000000774313616635656012571 0ustar ferkiferki#!perl # # (c) Jan Gehring # # vim: set ts=3 sw=3 tw=0: # vim: set expandtab: use strict; use warnings; our $VERSION = '1.8.1'; # 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 Rex - the friendly automation framework =head1 DESCRIPTION Rex is an automation framework that is friendly to any combinations of local and remote execution, push and pull style of management, or imperative and declarative approach. Its flexibility makes it a great fit for many different use cases, but most commonly Rex is used to automate application deployment and data center infrastructure management tasks. =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 -Ta List all tasks, including hidden -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 Rex-1.8.1/bin/rexify0000755000175000017500000010124613616635656013272 0ustar ferkiferki#!perl # # (c) Jan Gehring # # vim: set ts=3 sw=3 tw=0: # vim: set expandtab: use strict; use warnings; our $VERSION = '1.8.1'; # 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/^(ssh|https|git):\/\// ) { # seems to be a git link download_recipe_git($name); return; } if ( !-f "Rexfile" ) { print STDERR "This is not a Rex project directory. There is no Rexfile.\n"; exit 1; } if ( !-d "lib" ) { mkdir "lib"; } print "Getting dependencies for $name...\n"; my $deps = decode_json( get( sprintf( $DEPEND_SERVER, $name ) ) ); if ( scalar( @{$deps} ) > 0 ) { print " Found: \n - " . join( "\n - ", @{$deps} ) . "\n"; for my $dep ( @{$deps} ) { download_recipe($dep); } } else { print " None found.\n"; } if ( !exists $opts->{"no-install-perl-deps"} ) { print "Getting perl dependencies for $name...\n"; my $perl_deps = decode_json( get( sprintf( $PERL_DEPEND_SERVER, $name ) ) ); if ( scalar( @{$perl_deps} ) > 0 ) { print " Found: \n - " . join( "\n - ", @{$perl_deps} ) . "\n"; for my $dep ( @{$perl_deps} ) { install_perl_module($dep); } } else { print " None found.\n"; } } print "Downloading $name... "; $name =~ s/::/\//g; my $content = get( sprintf( $RECIPE_SERVER, $name ) ); open( my $fh, ">", "tmp-mod.tar.gz" ) or die($!); binmode $fh; print $fh $content; close($fh); chdir("lib"); system "tar", "xzf", "../tmp-mod.tar.gz"; chdir(".."); unlink("tmp-mod.tar.gz"); print "done.\n"; } sub resolve_deps { my ($file) = @_; $Rex::Logger::silent = 0; $file ||= "meta.yml"; if ( !-f $file ) { return; } my $ref = YAML::LoadFile($file); my ( %deps, %perl_deps ); if ( exists $ref->{Require} ) { if ( ref $ref->{Require} eq "ARRAY" ) { do { ref $_ eq "HASH" ? $deps{ $_->{name} } = $_ : $deps{$_} = $_ } for @{ $ref->{Require} }; } else { %deps = %{ $ref->{Require} }; } } if ( exists $ref->{PerlRequire} ) { if ( ref $ref->{PerlRequire} eq "ARRAY" ) { do { ref $_ eq "HASH" ? $perl_deps{ $_->{name} } = $_ : $perl_deps{$_} = $_; } for @{ $ref->{PerlRequire} }; } else { %perl_deps = %{ $ref->{PerlRequire} }; } } Rex::Logger::debug("Found dependencies: "); Rex::Logger::debug( Dumper( \%deps ) ); for my $req ( keys %deps ) { if ( ref $deps{$req} ) { if ( exists $deps{$req}->{git} ) { # git dep my $branch = "master"; if ( exists $deps{$req}->{branch} ) { $branch = $deps{$req}->{branch}; } my @path_parts = split /::/, $req; my $path = File::Spec->catdir( File::Spec->rel2abs( File::Spec->curdir() ), "lib", @path_parts ); my $parent_path = dirname $path; if ( !-d $parent_path ) { mkdir $parent_path; } if ( -d $path && !-d "$path/.git" ) { Rex::Logger::info( "$req not under git control. Skipping.", "warn" ); next; } if ( -d "$path/.git" ) { rmdir $path; } Rex::Logger::info("Cloning $deps{$req}->{git}#$branch to $path"); system "git", "clone", $deps{$req}->{git}, "-b", $branch, $path; resolve_deps("$path/meta.yml"); } } else { download_recipe($req); } } Rex::Logger::debug("Found perl dependencies: "); Rex::Logger::debug( Dumper( \%perl_deps ) ); for my $req ( keys %perl_deps ) { if ( ref $perl_deps{$req} ) { if ( exists $perl_deps{$req}->{git} ) { # git dep my $branch = "master"; if ( exists $perl_deps{$req}->{branch} ) { $branch = $perl_deps{$req}->{branch}; } my $curdir = getcwd; my $path = File::Spec->catdir( File::Spec->tmpdir, "tmp-build-$$" ); my $lib_path = File::Spec->catdir( File::Spec->rel2abs( File::Spec->curdir() ), "lib", "perl" ); my $parent_path = dirname $lib_path; if ( !-d $parent_path ) { mkdir $parent_path; } Rex::Logger::info("Cloning $perl_deps{$req}->{git}#$branch to $path"); system "git", "clone", $perl_deps{$req}->{git}, "-b", $branch, $path; chdir $path; system "cpanm", "-l", $lib_path, "."; chdir $curdir; rmdir $path; } } else { # we need relative directories, because auf a cpanm bug on windows. my $lib_path = File::Spec->catdir( File::Spec->curdir(), "lib", "perl" ); eval "use $req"; if ($@) { system "cpanm", "-l", $lib_path, $req; } } } } sub install_perl_module { my ($mod) = @_; print "Checking $mod: "; eval "use $mod"; if ($@) { print "[failed]\n"; } else { print "[ok]\n"; return; } print "Trying to install $mod... "; my $cmd = "cpanm"; my $out = qx($cmd --help 2>&1); if ( $? != 0 ) { $cmd = "cpan"; $out = qx($cmd -h 2>&1); if ( $? != 0 ) { print "[failed]\n"; print "Can't find cpanm or cpan. Please install $mod manually.\n"; return; } } my $cpanm_opts = ""; if ( exists $opts->{sudo} ) { $cmd = "sudo $cmd"; } $out = qx($cmd $cpanm_opts $mod 2>&1); open( my $log, ">>", "rexify-install.log" ) or die($!); print $log $out; close($log); if ( $? != 0 ) { print "[failed]\n"; print "!! Please install $mod manually. See rexify-install.log for more details.\n"; } else { print "[ok]\n"; } } if ( exists $opts->{bundle} ) { # bundle everything to an application CORE::mkdir("rex.payload"); system "cp -vR * rex.payload/ 2>/dev/null"; system "rm -rf rex.payload/rex.payload"; my $rex = File::Spec->catfile( dirname($0), "rex" ); my $tmp_args_file = File::Spec->catfile( File::Spec->tmpdir(), "pp.args.$$.tmp" ); open( my $fh, ">", $tmp_args_file ) or die($!); print $fh template('@pp.args.tpl'); close($fh); # build the perl and application if ( -d '/usr/lib/rex/lib' ) { system "pp -I /usr/lib/rex/lib \@$tmp_args_file -o rex.payload/rex.bin $rex"; } else { system "pp -I /usr/lib/rex/lib \@$tmp_args_file -o rex.payload/rex.bin $rex"; } # find libssh2.so and libexpat.so my $arch = qx{uname -m}; chomp $arch; my $elf = "ELF32"; if ( $arch eq "x86_64" ) { $elf = "ELF64"; } CORE::mkdir( File::Spec->catdir( "rex.payload", "lib.bin" ) ); my @libexpat = qx{find /usr/lib /usr/lib32 /usr/lib64 /lib -name 'libexpat.so*' -type f 2>/dev/null}; my @libssh2 = qx{find /usr/lib /usr/lib32 /usr/lib64 /lib -name 'libssh2.so*' -type f 2>/dev/null}; chomp @libexpat; chomp @libssh2; for my $f ( @libexpat, @libssh2 ) { my $elf_class = qx{readelf -h $f | grep Class:}; # only copy architecture specific files # currently no multi arch support if ( $elf_class =~ m/$elf/ ) { system "cp -va $f " . File::Spec->catdir( "rex.payload", "lib.bin" ); } } system "cd rex.payload/lib.bin ; ln -s " . basename( $libssh2[0] ) . " libssh2.so.1"; system "cd rex.payload/lib.bin ; ln -s " . basename( $libexpat[0] ) . " libexpat.so.1"; # create wrapper script. open( my $fh2, ">", "rex.sh" ) or die($!); print $fh2 template( '@bundle.sh.tpl', task => 'setup' ); close($fh2); system "cd rex.payload ; tar czf ../rex.payload.tar.gz *"; system "cat rex.sh rex.payload.tar.gz >rex.selfextract.sbx ; chmod 755 rex.selfextract.sbx"; CORE::unlink($tmp_args_file); CORE::unlink("rex.sh"); system "rm -rf rex.payload"; CORE::unlink("rex.payload.tar.gz"); exit 0; } if ( exists $opts->{upload} && exists $opts->{server} && exists $opts->{username} && exists $opts->{password} && exists $opts->{project} && exists $opts->{name} ) { $opts->{description} ||= ""; upload_rexfile( %{$opts} ); exit 0; } if ( exists $opts->{execute} && exists $opts->{server} && exists $opts->{username} && exists $opts->{password} && exists $opts->{project} && exists $opts->{hosts} && exists $opts->{job} ) { dispatch_execute_job( %{$opts} ); exit 0; } if ( exists $opts->{search} ) { my $search_string = $opts->{search}; # only a search print "Downloading recipes.yml ... "; my $recipes = get($SEARCH_SERVER); print " done.\n"; print "Searching...\n\n"; my $data = Load($recipes); for my $mod ( keys %{$data} ) { if ( $mod =~ qr{$search_string}i ) { print_found( $mod, $data->{$mod} ); next; } if ( $data->{$mod}->{Description} =~ qr{$search_string}i ) { print_found( $mod, $data->{$mod} ); } } exit 0; } if ( exists $opts->{"update-from-git"} ) { update_from_git(); exit 0; } if ( exists $opts->{init} ) { if ( -f $opts->{init} ) { download_recipe_local_tar_gz( $opts->{init} ); } else { download_recipe_git( $opts->{init} ); } exit 0; } if ( exists $opts->{use} && $ARGV[0] =~ /^\-\-use/ ) { if ( $opts->{use} ) { if ( !ref $opts->{use} ) { $opts->{use} = [ $opts->{use} ]; } for my $use_mod ( @{ $opts->{use} } ) { download_recipe($use_mod); } } exit 0; } if ( exists $opts->{"resolve-deps"} && $opts->{"resolve-deps"} ) { resolve_deps("meta.yml"); exit 0; } if ( exists $opts->{"create-module"} ) { my $dir = $ARGV[0]; my $module_name = $dir; if ( $dir !~ m/^[a-zA-Z][a-zA-Z_:0-9]+$/ ) { print "USAGE: $0 Module::Name --create-module\n"; print " Allowed characters: a-z, A-Z, _, 0-9, '::'.\n"; exit 1; } if ( -f "Rexfile" && $opts->{"create-module"} ) { $dir = "lib/$dir"; } if ( $dir =~ m/\-\-create\-module/ ) { print "USAGE: $0 Module::Name --create-module\n"; print " Allowed characters: a-z, A-Z, _, 0-9, '::'.\n"; exit 1; } $dir =~ s/::/\//g; print "Creating module $module_name...\n"; print " mkdir $dir\n"; mkdir $dir, recursive => 1; chdir $dir; print " Creating template file: __module__.pm\n"; file "__module__.pm", content => template( '@module.pm.tpl', dir => $dir, module_name => $module_name ); print " Creating template file: meta.yml\n"; file "meta.yml", content => template( '@meta.yml.tpl', dir => $dir, module_name => $module_name ); print "\n"; print "Your module has been created in $dir.\n"; exit 0; } my $dir = $ARGV[0]; if ( defined $ARGV[1] && $ARGV[1] !~ m/^\-\-/ ) { $dir = $ARGV[1]; } if ( $dir !~ m/^[a-zA-Z][a-zA-Z_:0-9]+$/ ) { print "USAGE: $0 Project::Name\n"; print " Allowed characters: a-z, A-Z, 0-9, _, '::'.\n"; exit 1; } if ( exists $opts->{template} && -f $opts->{template} && $opts->{template} !~ m/^\// ) { my $cwd = getcwd; $opts->{template} = "$cwd/" . $opts->{template}; } elsif ( exists $opts->{template} && $opts->{template} !~ m/^\// ) { $opts->{template} = '@' . $opts->{template}; } unless ( -d $dir ) { print "Created $dir\n"; mkdir($dir); } print "chdir to $dir\n"; chdir($dir); unless ( -d 'lib' ) { mkdir('lib'); } unless ( -f 'lib' . $ARGV[0] . '.pm' ) { open( my $fh, ">", "lib/$ARGV[0].pm" ) or die($!); print $fh template( '@libfile', lib => $ARGV[0] ); close($fh); print STDERR "Created lib/Rex/$ARGV[0].pm\n"; if ( $opts->{template} ) { open( $fh, ">", "Rexfile" ) or die($!); print $fh template( $opts->{template}, lib => $ARGV[0] ); close($fh); } else { open( $fh, ">", "Rexfile" ) or die($!); print $fh template( '@rexfile', lib => $ARGV[0] ); close($fh); } if ( $opts->{use} ) { if ( !ref $opts->{use} ) { $opts->{use} = [ $opts->{use} ]; } for my $use_mod ( @{ $opts->{use} } ) { download_recipe($use_mod); } } print STDERR "Created Rexfile.\n"; print STDERR "Done.\n\nNow edit Rexfile to 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 Rex-1.8.1/META.yml0000644000175000017500000010711113616635656012534 0ustar ferkiferki--- abstract: 'the friendly automation framework' author: - 'Jan Gehring ' build_requires: File::Temp: '0' Parallel::ForkManager: '0' String::Escape: '0' Test::Deep: '0' Test::More: '0' Test::UseAllModules: '0.15' configure_requires: ExtUtils::MakeMaker: '7.1101' dynamic_config: 1 generated_by: 'Dist::Zilla version 6.012, 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.8.1 Rex::Args: file: lib/Rex/Args.pm version: v1.8.1 Rex::Args::Integer: file: lib/Rex/Args/Integer.pm version: v1.8.1 Rex::Args::Single: file: lib/Rex/Args/Single.pm version: v1.8.1 Rex::Args::String: file: lib/Rex/Args/String.pm version: v1.8.1 Rex::Batch: file: lib/Rex/Batch.pm version: v1.8.1 Rex::Box: file: lib/Rex/Box.pm version: v1.8.1 Rex::Box::Amazon: file: lib/Rex/Box/Amazon.pm version: v1.8.1 Rex::Box::Base: file: lib/Rex/Box/Base.pm version: v1.8.1 Rex::Box::Docker: file: lib/Rex/Box/Docker.pm version: v1.8.1 Rex::Box::KVM: file: lib/Rex/Box/KVM.pm version: v1.8.1 Rex::Box::VBox: file: lib/Rex/Box/VBox.pm version: v1.8.1 Rex::CLI: file: lib/Rex/CLI.pm version: v1.8.1 Rex::CMDB: file: lib/Rex/CMDB.pm version: v1.8.1 Rex::CMDB::Base: file: lib/Rex/CMDB/Base.pm version: v1.8.1 Rex::CMDB::YAML: file: lib/Rex/CMDB/YAML.pm version: v1.8.1 Rex::Cloud: file: lib/Rex/Cloud.pm version: v1.8.1 Rex::Cloud::Amazon: file: lib/Rex/Cloud/Amazon.pm version: v1.8.1 Rex::Cloud::Base: file: lib/Rex/Cloud/Base.pm version: v1.8.1 Rex::Cloud::Jiffybox: file: lib/Rex/Cloud/Jiffybox.pm version: v1.8.1 Rex::Cloud::OpenStack: file: lib/Rex/Cloud/OpenStack.pm version: v1.8.1 Rex::Commands: file: lib/Rex/Commands.pm version: v1.8.1 Rex::Commands::Augeas: file: lib/Rex/Commands/Augeas.pm version: v1.8.1 Rex::Commands::Box: file: lib/Rex/Commands/Box.pm version: v1.8.1 Rex::Commands::Cloud: file: lib/Rex/Commands/Cloud.pm version: v1.8.1 Rex::Commands::Cron: file: lib/Rex/Commands/Cron.pm version: v1.8.1 Rex::Commands::DB: file: lib/Rex/Commands/DB.pm version: v1.8.1 Rex::Commands::Download: file: lib/Rex/Commands/Download.pm version: v1.8.1 Rex::Commands::File: file: lib/Rex/Commands/File.pm version: v1.8.1 Rex::Commands::Fs: file: lib/Rex/Commands/Fs.pm version: v1.8.1 Rex::Commands::Gather: file: lib/Rex/Commands/Gather.pm version: v1.8.1 Rex::Commands::Host: file: lib/Rex/Commands/Host.pm version: v1.8.1 Rex::Commands::Inventory: file: lib/Rex/Commands/Inventory.pm version: v1.8.1 Rex::Commands::Iptables: file: lib/Rex/Commands/Iptables.pm version: v1.8.1 Rex::Commands::JobControl: file: lib/Rex/Commands/JobControl.pm version: v1.8.1 Rex::Commands::Kernel: file: lib/Rex/Commands/Kernel.pm version: v1.8.1 Rex::Commands::LVM: file: lib/Rex/Commands/LVM.pm version: v1.8.1 Rex::Commands::MD5: file: lib/Rex/Commands/MD5.pm version: v1.8.1 Rex::Commands::Mkfs: file: lib/Rex/Commands/Mkfs.pm version: v1.8.1 Rex::Commands::Network: file: lib/Rex/Commands/Network.pm version: v1.8.1 Rex::Commands::Notify: file: lib/Rex/Commands/Notify.pm version: v1.8.1 Rex::Commands::Partition: file: lib/Rex/Commands/Partition.pm version: v1.8.1 Rex::Commands::Pkg: file: lib/Rex/Commands/Pkg.pm version: v1.8.1 Rex::Commands::PkgConf: file: lib/Rex/Commands/PkgConf.pm version: v1.8.1 Rex::Commands::Process: file: lib/Rex/Commands/Process.pm version: v1.8.1 Rex::Commands::Rsync: file: lib/Rex/Commands/Rsync.pm version: v1.8.1 Rex::Commands::Run: file: lib/Rex/Commands/Run.pm version: v1.8.1 Rex::Commands::SCM: file: lib/Rex/Commands/SCM.pm version: v1.8.1 Rex::Commands::Service: file: lib/Rex/Commands/Service.pm version: v1.8.1 Rex::Commands::SimpleCheck: file: lib/Rex/Commands/SimpleCheck.pm version: v1.8.1 Rex::Commands::Sync: file: lib/Rex/Commands/Sync.pm version: v1.8.1 Rex::Commands::Sysctl: file: lib/Rex/Commands/Sysctl.pm version: v1.8.1 Rex::Commands::Tail: file: lib/Rex/Commands/Tail.pm version: v1.8.1 Rex::Commands::Upload: file: lib/Rex/Commands/Upload.pm version: v1.8.1 Rex::Commands::User: file: lib/Rex/Commands/User.pm version: v1.8.1 Rex::Commands::Virtualization: file: lib/Rex/Commands/Virtualization.pm version: v1.8.1 Rex::Config: file: lib/Rex/Config.pm version: v1.8.1 Rex::Constants: file: lib/Rex/Constants.pm version: v1.8.1 Rex::Cron: file: lib/Rex/Cron.pm version: v1.8.1 Rex::Cron::Base: file: lib/Rex/Cron/Base.pm version: v1.8.1 Rex::Cron::FreeBSD: file: lib/Rex/Cron/FreeBSD.pm version: v1.8.1 Rex::Cron::Linux: file: lib/Rex/Cron/Linux.pm version: v1.8.1 Rex::Cron::SunOS: file: lib/Rex/Cron/SunOS.pm version: v1.8.1 Rex::Exporter: file: lib/Rex/Exporter.pm version: v1.8.1 Rex::FS::File: file: lib/Rex/FS/File.pm version: v1.8.1 Rex::File::Parser::Data: file: lib/Rex/File/Parser/Data.pm version: v1.8.1 Rex::File::Parser::Ini: file: lib/Rex/File/Parser/Ini.pm version: v1.8.1 Rex::Fork::Manager: file: lib/Rex/Fork/Manager.pm version: v1.8.1 Rex::Fork::Task: file: lib/Rex/Fork/Task.pm version: v1.8.1 Rex::Group: file: lib/Rex/Group.pm version: v1.8.1 Rex::Group::Entry::Server: file: lib/Rex/Group/Entry/Server.pm version: v1.8.1 Rex::Group::Lookup::Command: file: lib/Rex/Group/Lookup/Command.pm version: v1.8.1 Rex::Group::Lookup::DBI: file: lib/Rex/Group/Lookup/DBI.pm version: v1.8.1 Rex::Group::Lookup::File: file: lib/Rex/Group/Lookup/File.pm version: v1.8.1 Rex::Group::Lookup::INI: file: lib/Rex/Group/Lookup/INI.pm version: v1.8.1 Rex::Group::Lookup::XML: file: lib/Rex/Group/Lookup/XML.pm version: v1.8.1 Rex::Group::Lookup::YAML: file: lib/Rex/Group/Lookup/YAML.pm version: v1.8.1 Rex::Hardware: file: lib/Rex/Hardware.pm version: v1.8.1 Rex::Hardware::Host: file: lib/Rex/Hardware/Host.pm version: v1.8.1 Rex::Hardware::Kernel: file: lib/Rex/Hardware/Kernel.pm version: v1.8.1 Rex::Hardware::Memory: file: lib/Rex/Hardware/Memory.pm version: v1.8.1 Rex::Hardware::Network: file: lib/Rex/Hardware/Network.pm version: v1.8.1 Rex::Hardware::Network::Darwin: file: lib/Rex/Hardware/Network/Darwin.pm version: v1.8.1 Rex::Hardware::Network::FreeBSD: file: lib/Rex/Hardware/Network/FreeBSD.pm version: v1.8.1 Rex::Hardware::Network::Linux: file: lib/Rex/Hardware/Network/Linux.pm version: v1.8.1 Rex::Hardware::Network::NetBSD: file: lib/Rex/Hardware/Network/NetBSD.pm version: v1.8.1 Rex::Hardware::Network::OpenBSD: file: lib/Rex/Hardware/Network/OpenBSD.pm version: v1.8.1 Rex::Hardware::Network::Solaris: file: lib/Rex/Hardware/Network/Solaris.pm version: v1.8.1 Rex::Hardware::Swap: file: lib/Rex/Hardware/Swap.pm version: v1.8.1 Rex::Hardware::VirtInfo: file: lib/Rex/Hardware/VirtInfo.pm version: v1.8.1 Rex::Helper::Array: file: lib/Rex/Helper/Array.pm version: v1.8.1 Rex::Helper::DBI: file: lib/Rex/Helper/DBI.pm version: v1.8.1 Rex::Helper::Encode: file: lib/Rex/Helper/Encode.pm version: v1.8.1 Rex::Helper::File::Spec: file: lib/Rex/Helper/File/Spec.pm version: v1.8.1 Rex::Helper::File::Stat: file: lib/Rex/Helper/File/Stat.pm version: v1.8.1 Rex::Helper::File::Stat::Unix: file: lib/Rex/Helper/File/Stat/Unix.pm version: v1.8.1 Rex::Helper::File::Stat::Win32: file: lib/Rex/Helper/File/Stat/Win32.pm version: v1.8.1 Rex::Helper::Hash: file: lib/Rex/Helper/Hash.pm version: v1.8.1 Rex::Helper::INI: file: lib/Rex/Helper/INI.pm version: v1.8.1 Rex::Helper::IP: file: lib/Rex/Helper/IP.pm version: v1.8.1 Rex::Helper::Misc: file: lib/Rex/Helper/Misc.pm version: v1.8.1 Rex::Helper::Path: file: lib/Rex/Helper/Path.pm version: v1.8.1 Rex::Helper::Rexfile::ParamLookup: file: lib/Rex/Helper/Rexfile/ParamLookup.pm version: v1.8.1 Rex::Helper::Run: file: lib/Rex/Helper/Run.pm version: v1.8.1 Rex::Helper::SSH2: file: lib/Rex/Helper/SSH2.pm version: v1.8.1 Rex::Helper::SSH2::Expect: file: lib/Rex/Helper/SSH2/Expect.pm version: v1.8.1 Rex::Helper::System: file: lib/Rex/Helper/System.pm version: v1.8.1 Rex::Helper::URI: file: lib/Rex/Helper/URI.pm version: v1.8.1 Rex::Helper::UserAgent: file: lib/Rex/Helper/UserAgent.pm version: v1.8.1 Rex::Hook: file: lib/Rex/Hook.pm version: v1.8.1 Rex::Interface::Cache: file: lib/Rex/Interface/Cache.pm version: v1.8.1 Rex::Interface::Cache::Base: file: lib/Rex/Interface/Cache/Base.pm version: v1.8.1 Rex::Interface::Cache::YAML: file: lib/Rex/Interface/Cache/YAML.pm version: v1.8.1 Rex::Interface::Connection: file: lib/Rex/Interface/Connection.pm version: v1.8.1 Rex::Interface::Connection::Base: file: lib/Rex/Interface/Connection/Base.pm version: v1.8.1 Rex::Interface::Connection::Fake: file: lib/Rex/Interface/Connection/Fake.pm version: v1.8.1 Rex::Interface::Connection::HTTP: file: lib/Rex/Interface/Connection/HTTP.pm version: v1.8.1 Rex::Interface::Connection::HTTPS: file: lib/Rex/Interface/Connection/HTTPS.pm version: v1.8.1 Rex::Interface::Connection::Local: file: lib/Rex/Interface/Connection/Local.pm version: v1.8.1 Rex::Interface::Connection::OpenSSH: file: lib/Rex/Interface/Connection/OpenSSH.pm version: v1.8.1 Rex::Interface::Connection::SSH: file: lib/Rex/Interface/Connection/SSH.pm version: v1.8.1 Rex::Interface::Exec: file: lib/Rex/Interface/Exec.pm version: v1.8.1 Rex::Interface::Exec::Base: file: lib/Rex/Interface/Exec/Base.pm version: v1.8.1 Rex::Interface::Exec::HTTP: file: lib/Rex/Interface/Exec/HTTP.pm version: v1.8.1 Rex::Interface::Exec::IOReader: file: lib/Rex/Interface/Exec/IOReader.pm version: v1.8.1 Rex::Interface::Exec::Local: file: lib/Rex/Interface/Exec/Local.pm version: v1.8.1 Rex::Interface::Exec::OpenSSH: file: lib/Rex/Interface/Exec/OpenSSH.pm version: v1.8.1 Rex::Interface::Exec::SSH: file: lib/Rex/Interface/Exec/SSH.pm version: v1.8.1 Rex::Interface::Exec::Sudo: file: lib/Rex/Interface/Exec/Sudo.pm version: v1.8.1 Rex::Interface::Executor: file: lib/Rex/Interface/Executor.pm version: v1.8.1 Rex::Interface::Executor::Base: file: lib/Rex/Interface/Executor/Base.pm version: v1.8.1 Rex::Interface::Executor::Default: file: lib/Rex/Interface/Executor/Default.pm version: v1.8.1 Rex::Interface::File: file: lib/Rex/Interface/File.pm version: v1.8.1 Rex::Interface::File::Base: file: lib/Rex/Interface/File/Base.pm version: v1.8.1 Rex::Interface::File::HTTP: file: lib/Rex/Interface/File/HTTP.pm version: v1.8.1 Rex::Interface::File::Local: file: lib/Rex/Interface/File/Local.pm version: v1.8.1 Rex::Interface::File::OpenSSH: file: lib/Rex/Interface/File/OpenSSH.pm version: v1.8.1 Rex::Interface::File::SSH: file: lib/Rex/Interface/File/SSH.pm version: v1.8.1 Rex::Interface::File::Sudo: file: lib/Rex/Interface/File/Sudo.pm version: v1.8.1 Rex::Interface::Fs: file: lib/Rex/Interface/Fs.pm version: v1.8.1 Rex::Interface::Fs::Base: file: lib/Rex/Interface/Fs/Base.pm version: v1.8.1 Rex::Interface::Fs::HTTP: file: lib/Rex/Interface/Fs/HTTP.pm version: v1.8.1 Rex::Interface::Fs::Local: file: lib/Rex/Interface/Fs/Local.pm version: v1.8.1 Rex::Interface::Fs::OpenSSH: file: lib/Rex/Interface/Fs/OpenSSH.pm version: v1.8.1 Rex::Interface::Fs::SSH: file: lib/Rex/Interface/Fs/SSH.pm version: v1.8.1 Rex::Interface::Fs::Sudo: file: lib/Rex/Interface/Fs/Sudo.pm version: v1.8.1 Rex::Interface::Shell: file: lib/Rex/Interface/Shell.pm version: v1.8.1 Rex::Interface::Shell::Ash: file: lib/Rex/Interface/Shell/Ash.pm version: v1.8.1 Rex::Interface::Shell::Base: file: lib/Rex/Interface/Shell/Base.pm version: v1.8.1 Rex::Interface::Shell::Bash: file: lib/Rex/Interface/Shell/Bash.pm version: v1.8.1 Rex::Interface::Shell::Csh: file: lib/Rex/Interface/Shell/Csh.pm version: v1.8.1 Rex::Interface::Shell::Default: file: lib/Rex/Interface/Shell/Default.pm version: v1.8.1 Rex::Interface::Shell::Idrac: file: lib/Rex/Interface/Shell/Idrac.pm version: v1.8.1 Rex::Interface::Shell::Ksh: file: lib/Rex/Interface/Shell/Ksh.pm version: v1.8.1 Rex::Interface::Shell::Sh: file: lib/Rex/Interface/Shell/Sh.pm version: v1.8.1 Rex::Interface::Shell::Tcsh: file: lib/Rex/Interface/Shell/Tcsh.pm version: v1.8.1 Rex::Interface::Shell::Zsh: file: lib/Rex/Interface/Shell/Zsh.pm version: v1.8.1 Rex::Inventory: file: lib/Rex/Inventory.pm version: v1.8.1 Rex::Inventory::Bios: file: lib/Rex/Inventory/Bios.pm version: v1.8.1 Rex::Inventory::DMIDecode: file: lib/Rex/Inventory/DMIDecode.pm version: v1.8.1 Rex::Inventory::DMIDecode::BaseBoard: file: lib/Rex/Inventory/DMIDecode/BaseBoard.pm version: v1.8.1 Rex::Inventory::DMIDecode::Bios: file: lib/Rex/Inventory/DMIDecode/Bios.pm version: v1.8.1 Rex::Inventory::DMIDecode::CPU: file: lib/Rex/Inventory/DMIDecode/CPU.pm version: v1.8.1 Rex::Inventory::DMIDecode::Memory: file: lib/Rex/Inventory/DMIDecode/Memory.pm version: v1.8.1 Rex::Inventory::DMIDecode::MemoryArray: file: lib/Rex/Inventory/DMIDecode/MemoryArray.pm version: v1.8.1 Rex::Inventory::DMIDecode::Section: file: lib/Rex/Inventory/DMIDecode/Section.pm version: v1.8.1 Rex::Inventory::DMIDecode::SystemInformation: file: lib/Rex/Inventory/DMIDecode/SystemInformation.pm version: v1.8.1 Rex::Inventory::HP::ACU: file: lib/Rex/Inventory/HP/ACU.pm version: v1.8.1 Rex::Inventory::Hal: file: lib/Rex/Inventory/Hal.pm version: v1.8.1 Rex::Inventory::Hal::Object: file: lib/Rex/Inventory/Hal/Object.pm version: v1.8.1 Rex::Inventory::Hal::Object::Net: file: lib/Rex/Inventory/Hal/Object/Net.pm version: v1.8.1 Rex::Inventory::Hal::Object::Storage: file: lib/Rex/Inventory/Hal/Object/Storage.pm version: v1.8.1 Rex::Inventory::Hal::Object::Volume: file: lib/Rex/Inventory/Hal/Object/Volume.pm version: v1.8.1 Rex::Inventory::Proc: file: lib/Rex/Inventory/Proc.pm version: v1.8.1 Rex::Inventory::Proc::Cpuinfo: file: lib/Rex/Inventory/Proc/Cpuinfo.pm version: v1.8.1 Rex::Inventory::SMBios: file: lib/Rex/Inventory/SMBios.pm version: v1.8.1 Rex::Inventory::SMBios::BaseBoard: file: lib/Rex/Inventory/SMBios/BaseBoard.pm version: v1.8.1 Rex::Inventory::SMBios::Bios: file: lib/Rex/Inventory/SMBios/Bios.pm version: v1.8.1 Rex::Inventory::SMBios::CPU: file: lib/Rex/Inventory/SMBios/CPU.pm version: v1.8.1 Rex::Inventory::SMBios::Memory: file: lib/Rex/Inventory/SMBios/Memory.pm version: v1.8.1 Rex::Inventory::SMBios::MemoryArray: file: lib/Rex/Inventory/SMBios/MemoryArray.pm version: v1.8.1 Rex::Inventory::SMBios::Section: file: lib/Rex/Inventory/SMBios/Section.pm version: v1.8.1 Rex::Inventory::SMBios::SystemInformation: file: lib/Rex/Inventory/SMBios/SystemInformation.pm version: v1.8.1 Rex::Logger: file: lib/Rex/Logger.pm version: v1.8.1 Rex::Notify: file: lib/Rex/Notify.pm version: v1.8.1 Rex::Output: file: lib/Rex/Output.pm version: v1.8.1 Rex::Output::Base: file: lib/Rex/Output/Base.pm version: v1.8.1 Rex::Output::JUnit: file: lib/Rex/Output/JUnit.pm version: v1.8.1 Rex::Pkg: file: lib/Rex/Pkg.pm version: v1.8.1 Rex::Pkg::ALT: file: lib/Rex/Pkg/ALT.pm version: v1.8.1 Rex::Pkg::Arch: file: lib/Rex/Pkg/Arch.pm version: v1.8.1 Rex::Pkg::Base: file: lib/Rex/Pkg/Base.pm version: v1.8.1 Rex::Pkg::Debian: file: lib/Rex/Pkg/Debian.pm version: v1.8.1 Rex::Pkg::FreeBSD: file: lib/Rex/Pkg/FreeBSD.pm version: v1.8.1 Rex::Pkg::Gentoo: file: lib/Rex/Pkg/Gentoo.pm version: v1.8.1 Rex::Pkg::Mageia: file: lib/Rex/Pkg/Mageia.pm version: v1.8.1 Rex::Pkg::NetBSD: file: lib/Rex/Pkg/NetBSD.pm version: v1.8.1 Rex::Pkg::OpenBSD: file: lib/Rex/Pkg/OpenBSD.pm version: v1.8.1 Rex::Pkg::OpenWrt: file: lib/Rex/Pkg/OpenWrt.pm version: v1.8.1 Rex::Pkg::Redhat: file: lib/Rex/Pkg/Redhat.pm version: v1.8.1 Rex::Pkg::SuSE: file: lib/Rex/Pkg/SuSE.pm version: v1.8.1 Rex::Pkg::SunOS: file: lib/Rex/Pkg/SunOS.pm version: v1.8.1 Rex::Pkg::SunOS::OpenCSW: file: lib/Rex/Pkg/SunOS/OpenCSW.pm version: v1.8.1 Rex::Pkg::SunOS::pkg: file: lib/Rex/Pkg/SunOS/pkg.pm version: v1.8.1 Rex::Pkg::Ubuntu: file: lib/Rex/Pkg/Ubuntu.pm version: v1.8.1 Rex::Pkg::VoidLinux: file: lib/Rex/Pkg/VoidLinux.pm version: v1.8.1 Rex::PkgConf: file: lib/Rex/PkgConf.pm version: v1.8.1 Rex::PkgConf::Base: file: lib/Rex/PkgConf/Base.pm version: v1.8.1 Rex::PkgConf::Debian: file: lib/Rex/PkgConf/Debian.pm version: v1.8.1 Rex::Profiler: file: lib/Rex/Profiler.pm version: v1.8.1 Rex::Report: file: lib/Rex/Report.pm version: v1.8.1 Rex::Report::Base: file: lib/Rex/Report/Base.pm version: v1.8.1 Rex::Report::YAML: file: lib/Rex/Report/YAML.pm version: v1.8.1 Rex::Require: file: lib/Rex/Require.pm version: v1.8.1 Rex::Resource: file: lib/Rex/Resource.pm version: v1.8.1 Rex::Resource::Common: file: lib/Rex/Resource/Common.pm version: v1.8.1 Rex::Resource::firewall: file: lib/Rex/Resource/firewall.pm version: v1.8.1 Rex::Resource::firewall::Provider::base: file: lib/Rex/Resource/firewall/Provider/base.pm version: v1.8.1 Rex::Resource::firewall::Provider::iptables: file: lib/Rex/Resource/firewall/Provider/iptables.pm version: v1.8.1 Rex::Resource::firewall::Provider::ufw: file: lib/Rex/Resource/firewall/Provider/ufw.pm version: v1.8.1 Rex::RunList: file: lib/Rex/RunList.pm version: v1.8.1 Rex::SCM::Git: file: lib/Rex/SCM/Git.pm version: v1.8.1 Rex::SCM::Subversion: file: lib/Rex/SCM/Subversion.pm version: v1.8.1 Rex::Service: file: lib/Rex/Service.pm version: v1.8.1 Rex::Service::ALT: file: lib/Rex/Service/ALT.pm version: v1.8.1 Rex::Service::ALT::systemd: file: lib/Rex/Service/ALT/systemd.pm version: v1.8.1 Rex::Service::Arch::systemd: file: lib/Rex/Service/Arch/systemd.pm version: v1.8.1 Rex::Service::Base: file: lib/Rex/Service/Base.pm version: v1.8.1 Rex::Service::Debian: file: lib/Rex/Service/Debian.pm version: v1.8.1 Rex::Service::Debian::systemd: file: lib/Rex/Service/Debian/systemd.pm version: v1.8.1 Rex::Service::FreeBSD: file: lib/Rex/Service/FreeBSD.pm version: v1.8.1 Rex::Service::Gentoo: file: lib/Rex/Service/Gentoo.pm version: v1.8.1 Rex::Service::Gentoo::systemd: file: lib/Rex/Service/Gentoo/systemd.pm version: v1.8.1 Rex::Service::Mageia: file: lib/Rex/Service/Mageia.pm version: v1.8.1 Rex::Service::Mageia::systemd: file: lib/Rex/Service/Mageia/systemd.pm version: v1.8.1 Rex::Service::NetBSD: file: lib/Rex/Service/NetBSD.pm version: v1.8.1 Rex::Service::OpenBSD: file: lib/Rex/Service/OpenBSD.pm version: v1.8.1 Rex::Service::OpenWrt: file: lib/Rex/Service/OpenWrt.pm version: v1.8.1 Rex::Service::Redhat: file: lib/Rex/Service/Redhat.pm version: v1.8.1 Rex::Service::Redhat::systemd: file: lib/Rex/Service/Redhat/systemd.pm version: v1.8.1 Rex::Service::SuSE: file: lib/Rex/Service/SuSE.pm version: v1.8.1 Rex::Service::SuSE::systemd: file: lib/Rex/Service/SuSE/systemd.pm version: v1.8.1 Rex::Service::SunOS: file: lib/Rex/Service/SunOS.pm version: v1.8.1 Rex::Service::SunOS::svcadm: file: lib/Rex/Service/SunOS/svcadm.pm version: v1.8.1 Rex::Service::Ubuntu: file: lib/Rex/Service/Ubuntu.pm version: v1.8.1 Rex::Service::VoidLinux: file: lib/Rex/Service/VoidLinux.pm version: v1.8.1 Rex::Shared::Var: file: lib/Rex/Shared/Var.pm version: v1.8.1 Rex::Shared::Var::Array: file: lib/Rex/Shared/Var/Array.pm version: v1.8.1 Rex::Shared::Var::Common: file: lib/Rex/Shared/Var/Common.pm version: v1.8.1 Rex::Shared::Var::Hash: file: lib/Rex/Shared/Var/Hash.pm version: v1.8.1 Rex::Shared::Var::Scalar: file: lib/Rex/Shared/Var/Scalar.pm version: v1.8.1 Rex::Sudo::File: file: lib/Rex/Sudo/File.pm version: v1.8.1 Rex::Task: file: lib/Rex/Task.pm version: v1.8.1 Rex::TaskList: file: lib/Rex/TaskList.pm version: v1.8.1 Rex::TaskList::Base: file: lib/Rex/TaskList/Base.pm version: v1.8.1 Rex::TaskList::Parallel_ForkManager: file: lib/Rex/TaskList/Parallel_ForkManager.pm version: v1.8.1 Rex::Template: file: lib/Rex/Template.pm version: v1.8.1 Rex::Template::NG: file: lib/Rex/Template/NG.pm version: v1.8.1 Rex::Test: file: lib/Rex/Test.pm version: v1.8.1 Rex::Test::Base: file: lib/Rex/Test/Base.pm version: v1.8.1 Rex::Test::Base::has_content: file: lib/Rex/Test/Base/has_content.pm version: v1.8.1 Rex::Test::Base::has_cron: file: lib/Rex/Test/Base/has_cron.pm version: v1.8.1 Rex::Test::Base::has_cron_env: file: lib/Rex/Test/Base/has_cron_env.pm version: v1.8.1 Rex::Test::Base::has_dir: file: lib/Rex/Test/Base/has_dir.pm version: v1.8.1 Rex::Test::Base::has_file: file: lib/Rex/Test/Base/has_file.pm version: v1.8.1 Rex::Test::Base::has_file_content: file: lib/Rex/Test/Base/has_file_content.pm version: v1.8.1 Rex::Test::Base::has_output: file: lib/Rex/Test/Base/has_output.pm version: v1.8.1 Rex::Test::Base::has_output_matching: file: lib/Rex/Test/Base/has_output_matching.pm version: v1.8.1 Rex::Test::Base::has_package: file: lib/Rex/Test/Base/has_package.pm version: v1.8.1 Rex::Test::Base::has_service_running: file: lib/Rex/Test/Base/has_service_running.pm version: v1.8.1 Rex::Test::Base::has_service_stopped: file: lib/Rex/Test/Base/has_service_stopped.pm version: v1.8.1 Rex::Test::Base::has_stat: file: lib/Rex/Test/Base/has_stat.pm version: v1.8.1 Rex::Transaction: file: lib/Rex/Transaction.pm version: v1.8.1 Rex::User: file: lib/Rex/User.pm version: v1.8.1 Rex::User::Base: file: lib/Rex/User/Base.pm version: v1.8.1 Rex::User::FreeBSD: file: lib/Rex/User/FreeBSD.pm version: v1.8.1 Rex::User::Linux: file: lib/Rex/User/Linux.pm version: v1.8.1 Rex::User::NetBSD: file: lib/Rex/User/NetBSD.pm version: v1.8.1 Rex::User::OpenBSD: file: lib/Rex/User/OpenBSD.pm version: v1.8.1 Rex::User::OpenWrt: file: lib/Rex/User/OpenWrt.pm version: v1.8.1 Rex::User::SunOS: file: lib/Rex/User/SunOS.pm version: v1.8.1 Rex::Value: file: lib/Rex/Value.pm version: v1.8.1 Rex::Virtualization: file: lib/Rex/Virtualization.pm version: v1.8.1 Rex::Virtualization::Base: file: lib/Rex/Virtualization/Base.pm version: v1.8.1 Rex::Virtualization::Docker: file: lib/Rex/Virtualization/Docker.pm version: v1.8.1 Rex::Virtualization::Docker::create: file: lib/Rex/Virtualization/Docker/create.pm version: v1.8.1 Rex::Virtualization::Docker::daemon: file: lib/Rex/Virtualization/Docker/daemon.pm version: v1.8.1 Rex::Virtualization::Docker::delete: file: lib/Rex/Virtualization/Docker/delete.pm version: v1.8.1 Rex::Virtualization::Docker::destroy: file: lib/Rex/Virtualization/Docker/destroy.pm version: v1.8.1 Rex::Virtualization::Docker::guestinfo: file: lib/Rex/Virtualization/Docker/guestinfo.pm version: v1.8.1 Rex::Virtualization::Docker::images: file: lib/Rex/Virtualization/Docker/images.pm version: v1.8.1 Rex::Virtualization::Docker::import: file: lib/Rex/Virtualization/Docker/import.pm version: v1.8.1 Rex::Virtualization::Docker::info: file: lib/Rex/Virtualization/Docker/info.pm version: v1.8.1 Rex::Virtualization::Docker::list: file: lib/Rex/Virtualization/Docker/list.pm version: v1.8.1 Rex::Virtualization::Docker::reboot: file: lib/Rex/Virtualization/Docker/reboot.pm version: v1.8.1 Rex::Virtualization::Docker::shutdown: file: lib/Rex/Virtualization/Docker/shutdown.pm version: v1.8.1 Rex::Virtualization::Docker::start: file: lib/Rex/Virtualization/Docker/start.pm version: v1.8.1 Rex::Virtualization::Docker::status: file: lib/Rex/Virtualization/Docker/status.pm version: v1.8.1 Rex::Virtualization::LibVirt: file: lib/Rex/Virtualization/LibVirt.pm version: v1.8.1 Rex::Virtualization::LibVirt::blklist: file: lib/Rex/Virtualization/LibVirt/blklist.pm version: v1.8.1 Rex::Virtualization::LibVirt::clone: file: lib/Rex/Virtualization/LibVirt/clone.pm version: v1.8.1 Rex::Virtualization::LibVirt::create: file: lib/Rex/Virtualization/LibVirt/create.pm version: v1.8.1 Rex::Virtualization::LibVirt::delete: file: lib/Rex/Virtualization/LibVirt/delete.pm version: v1.8.1 Rex::Virtualization::LibVirt::destroy: file: lib/Rex/Virtualization/LibVirt/destroy.pm version: v1.8.1 Rex::Virtualization::LibVirt::dumpxml: file: lib/Rex/Virtualization/LibVirt/dumpxml.pm version: v1.8.1 Rex::Virtualization::LibVirt::guestinfo: file: lib/Rex/Virtualization/LibVirt/guestinfo.pm version: v1.8.1 Rex::Virtualization::LibVirt::hypervisor: file: lib/Rex/Virtualization/LibVirt/hypervisor.pm version: v1.8.1 Rex::Virtualization::LibVirt::iflist: file: lib/Rex/Virtualization/LibVirt/iflist.pm version: v1.8.1 Rex::Virtualization::LibVirt::import: file: lib/Rex/Virtualization/LibVirt/import.pm version: v1.8.1 Rex::Virtualization::LibVirt::info: file: lib/Rex/Virtualization/LibVirt/info.pm version: v1.8.1 Rex::Virtualization::LibVirt::list: file: lib/Rex/Virtualization/LibVirt/list.pm version: v1.8.1 Rex::Virtualization::LibVirt::option: file: lib/Rex/Virtualization/LibVirt/option.pm version: v1.8.1 Rex::Virtualization::LibVirt::reboot: file: lib/Rex/Virtualization/LibVirt/reboot.pm version: v1.8.1 Rex::Virtualization::LibVirt::shutdown: file: lib/Rex/Virtualization/LibVirt/shutdown.pm version: v1.8.1 Rex::Virtualization::LibVirt::start: file: lib/Rex/Virtualization/LibVirt/start.pm version: v1.8.1 Rex::Virtualization::LibVirt::status: file: lib/Rex/Virtualization/LibVirt/status.pm version: v1.8.1 Rex::Virtualization::LibVirt::vncdisplay: file: lib/Rex/Virtualization/LibVirt/vncdisplay.pm version: v1.8.1 Rex::Virtualization::Lxc: file: lib/Rex/Virtualization/Lxc.pm version: v1.8.1 Rex::Virtualization::Lxc::attach: file: lib/Rex/Virtualization/Lxc/attach.pm version: v1.8.1 Rex::Virtualization::Lxc::copy: file: lib/Rex/Virtualization/Lxc/copy.pm version: v1.8.1 Rex::Virtualization::Lxc::create: file: lib/Rex/Virtualization/Lxc/create.pm version: v1.8.1 Rex::Virtualization::Lxc::destroy: file: lib/Rex/Virtualization/Lxc/destroy.pm version: v1.8.1 Rex::Virtualization::Lxc::info: file: lib/Rex/Virtualization/Lxc/info.pm version: v1.8.1 Rex::Virtualization::Lxc::list: file: lib/Rex/Virtualization/Lxc/list.pm version: v1.8.1 Rex::Virtualization::Lxc::start: file: lib/Rex/Virtualization/Lxc/start.pm version: v1.8.1 Rex::Virtualization::Lxc::stop: file: lib/Rex/Virtualization/Lxc/stop.pm version: v1.8.1 Rex::Virtualization::VBox: file: lib/Rex/Virtualization/VBox.pm version: v1.8.1 Rex::Virtualization::VBox::bridge: file: lib/Rex/Virtualization/VBox/bridge.pm version: v1.8.1 Rex::Virtualization::VBox::create: file: lib/Rex/Virtualization/VBox/create.pm version: v1.8.1 Rex::Virtualization::VBox::delete: file: lib/Rex/Virtualization/VBox/delete.pm version: v1.8.1 Rex::Virtualization::VBox::destroy: file: lib/Rex/Virtualization/VBox/destroy.pm version: v1.8.1 Rex::Virtualization::VBox::forward_port: file: lib/Rex/Virtualization/VBox/forward_port.pm version: v1.8.1 Rex::Virtualization::VBox::guestinfo: file: lib/Rex/Virtualization/VBox/guestinfo.pm version: v1.8.1 Rex::Virtualization::VBox::import: file: lib/Rex/Virtualization/VBox/import.pm version: v1.8.1 Rex::Virtualization::VBox::info: file: lib/Rex/Virtualization/VBox/info.pm version: v1.8.1 Rex::Virtualization::VBox::list: file: lib/Rex/Virtualization/VBox/list.pm version: v1.8.1 Rex::Virtualization::VBox::option: file: lib/Rex/Virtualization/VBox/option.pm version: v1.8.1 Rex::Virtualization::VBox::reboot: file: lib/Rex/Virtualization/VBox/reboot.pm version: v1.8.1 Rex::Virtualization::VBox::share_folder: file: lib/Rex/Virtualization/VBox/share_folder.pm version: v1.8.1 Rex::Virtualization::VBox::shutdown: file: lib/Rex/Virtualization/VBox/shutdown.pm version: v1.8.1 Rex::Virtualization::VBox::start: file: lib/Rex/Virtualization/VBox/start.pm version: v1.8.1 Rex::Virtualization::VBox::status: file: lib/Rex/Virtualization/VBox/status.pm version: v1.8.1 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, != 1.25' attributes: '0' base: '0' constant: '0' lib: '0' overload: '0' perl: '5.010001' 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: https://www.rexify.org repository: https://github.com/RexOps/Rex.git version: 1.8.1 x_contributors: - 'A Happy User ' - 'Alexander Romanenko ' - 'Alexandr Ciornii ' - 'Ali Polatel ' - 'alx542 ' - 'Anders Ossowicki ' - 'Andrej Zverev ' - 'Andrew Solomon ' - 'Andy Beverley ' - 'Arnold Bechtoldt ' - 'Boris Däppen ' - 'Brian Manning ' - 'Cameron Daniel ' - 'Chris Steigmeier ' - 'Christophe Wolfhugel ' - 'Crimson Thompson ' - 'Daniel Bäurer ' - 'Daniel Cesario ' - 'Daniel Dico ' - 'Denis Silakov ' - 'Dmitry Kopytov ' - 'Dominik Schulz ' - 'Eduardo J ' - 'Eivin Giske Skaaren ' - 'elisdg ' - 'Elmer Quintanilla ' - 'Eric Johnson ' - 'Erik Huelsmann ' - 'Ferenc Erki ' - 'Franky Van Liedekerke ' - 'Fran Rodriguez ' - 'Graham Todd ' - 'Harm Müller ' - 'Hayato Imai ' - 'Hiroaki Nakamura ' - 'Hiroki Matsuo ' - 'iblinder ' - 'James D Bearden ' - 'Jan Gehring ' - 'Jean Charles Passard ' - 'Jean-Marie Renouard ' - 'Jeen Lee ' - 'Jens Berthold ' - 'Joachim Bargsten ' - 'John Karr ' - 'Jonathan Delgado ' - 'Jon Gentle ' - 'Joris DE POOTER ' - 'Jose Luis Martinez ' - 'Jose Luis Perez Diez ' - 'Kasim Tuman ' - 'Keedi Kim ' - 'Ken Crowell ' - 'Kent Fredric ' - 'Kirill Babikhin ' - 'labbeduddel ' - 'Leah Neukirchen ' - 'LeMerP ' - 'Mario Domgoergen ' - 'Max E. Aubrey <35892750+maxeaubrey@users.noreply.github.com>' - 'Mitch Broadhead ' - 'Nathan Abu ' - 'Naveed Massjouni ' - 'necrophcodr ' - 'Nicolas Leclercq ' - 'Nikolay A. Fetisov ' - 'Nils Domrose ' - 'okaoka ' - 'Oleg Hardt ' - 'Olivier Cherrier ' - 'Orange ' - 'Paco Esteban ' - 'Patrick Lauer ' - 'Pavel Timofeev ' - 'perlancar ' - 'Peter H. Ezetta ' - 'Peter Manthey ' - 'petersonchen ' - 'Pierrick DINTRAT ' - 'Piotr Karbowski ' - 'Prajithp ' - 'Renée Bäcker ' - 'Robert Abraham ' - 'Roy Storey ' - 'Samuele Tognini ' - 'Sascha Askani ' - 'Sascha Guenther ' - 'Simon Bertrang ' - 'Solène Rapenne ' - 'Stephane Benoit ' - 'Steve Dondley ' - 'Sven Dowideit ' - 'Tamas Molnar ' - 'Tianon Gravi ' - 'Tokuhiro Matsuno ' - 'Tomohiro Hosaka ' - 'Volker Kroll ' - 'Walery Wysotsky ' - 'Yanick Champoux ' - 'Сергей Романов ' - '范野人 ' - '饶琛琳 ' - 'Alex Mestiashvili ' - 'Cuong Manh Le ' - 'David Golovan ' - 'Dominik Danter ' - 'Ilya Evseev ' - 'Niklas Larsson ' - 'Qiao Liu ' - 'Renato CRON ' x_generated_by_perl: v5.30.1 x_serialization_backend: 'YAML::Tiny version 1.73' Rex-1.8.1/MANIFEST0000644000175000017500000002735513616635656012427 0ustar ferkiferki# This file was automatically generated by Dist::Zilla::Plugin::Manifest v6.012. .perlcriticrc .perltidyrc CONTRIBUTING.md 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/Pkg/VoidLinux.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/Service/VoidLinux.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/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/hooks_in_pkg.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/chicken.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/report.t t/resource.t t/runlist.t t/shared.t t/ssh_config.1 t/summary.t t/symlinks.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 xt/author/critic.t xt/author/minimum-version.t xt/author/perltidy.t xt/author/pod-syntax.t xt/release/cpan-changes.t xt/release/kwalitee.t Rex-1.8.1/dist.ini0000644000175000017500000000440113616635656012725 0ustar ferkiferkiname = Rex author = Jan Gehring license = Apache_2_0 copyright_holder = Jan Gehring [@Filter] -bundle = @Basic -remove = MakeMaker -remove = GatherDir -remove = PruneCruft -remove = ExtraTests [GatherDir] include_dotfiles = 1 [PruneCruft] except = \.perltidyrc except = \.perlcriticrc [AutoPrereqs] skip = __Rexfile__ [MakeMaker::Awesome] eumm_version = 7.1101 header = die 'OS unsupported' if ( $^O eq 'MSWin32' && scalar((Win32::GetOSVersion())[1]) < 6 ); [ManifestSkip] [MetaProvides::Package] [MetaResources] homepage = https://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] [Git::Contributors] [Meta::Contributors] contributor = Alex Mestiashvili contributor = Cuong Manh Le contributor = David Golovan contributor = Dominik Danter contributor = Ilya Evseev contributor = Niklas Larsson contributor = Qiao Liu contributor = Renato CRON [ContributorsFile] [OSPrereqs / !~MSWin] IO::Pty = 0 Net::OpenSSH = 0 Net::SFTP::Foreign = 0 [OSPrereqs / ~MSWin] Net::SSH2 = 0 [OurPkgVersion] ; [PodCoverageTests] [PodSyntaxTests] [RunExtraTests] [Prereqs] perl = 5.010001 YAML = != 1.25 [Prereqs / BuildRequires] Test::UseAllModules = 0.15 [Prereqs / DevelopRequires] Test::Kwalitee = 0 Test::PerlTidy = 0 Test::Pod = 0 [Test::MinimumVersion] max_target_perl = 5.10.1 [Test::Perl::Critic] [Test::Kwalitee] [Test::CPAN::Changes] changelog = ChangeLog [NextRelease] filename = ChangeLog time_zone = UTC format = %v %{yyyy-MM-dd}d %U <%E> [PreviousVersion::Changelog] filename = ChangeLog [NextVersion::Semantic] change_file = ChangeLog major = API CHANGES, MAJOR minor = NEW FEATURES, ENHANCEMENTS, MINOR revision = BUG FIXES, DOCUMENTATION, REVISION [Run::AfterRelease] run = encoding=$(file -bi ChangeLog | awk -F charset= '{print $2}'); piconv -f $encoding -t utf8 ChangeLog > ChangeLog.utf8 run = mv ChangeLog.utf8 ChangeLog Rex-1.8.1/ChangeLog0000644000175000017500000023272413616635656013046 0ustar ferkiferkiRevision history for Rex 1.8.1 2020-02-05 Ferenc Erki [DOCUMENTATION] - Document embedded templates [REVISION] - Reformat changelog to match CPAN specifications - Use NextRelease to update release info - Use NextVersion::Semantic for versioning 1.8.0 2020-01-05 Ferenc Erki - Map commit authors to their canonical name and email - Ferenc Erki - Add contributors without commits on master branch - Ferenc Erki - Generate CONTRIBUTORS file - Ferenc Erki - Added Rex::Pkg::VoidLinux using XBPS - Leah Neukirchen - Added Rex::Service::VoidLinux using runit - Leah Neukirchen - Added Void Linux - Leah Neukirchen - Make waitpid blocking sleep time configurable - Ferenc Erki - Match waitpid blocking sleep between fork managers - Ferenc Erki - Add Kwalitee tests - Ferenc Erki - Update Perl::Tidy on each Travis build - Ferenc Erki - Skip bin tidiness check on Windows and Mac - Ferenc Erki 1.7.1 2019-12-05 Ferenc Erki - Update supported Perl versions - Ferenc Erki - Don't skip all markdown files - Ferenc Erki - Update synopsis - Ferenc Erki - Update CPAN metadata - Ferenc Erki - Update name and description in POD - Ferenc Erki - Clarify POD - Ferenc Erki - Fix POD formatting - Ferenc Erki - Fix CMDB examples (fix #1151) - Ferenc Erki - Test on OS X with Travis CI - Ferenc Erki - Fix getting current directory in Windows - Ferenc Erki - Skip symlink tests on Windows - Ferenc Erki - Test on Windows with Travis CI - Ferenc Erki - Update description - Ferenc Erki 1.7.0 2019-11-05 Ferenc Erki - Fix test dependency - Ferenc Erki - Use perl to query environment - Ferenc Erki - Test run failures with alternative commands - Ferenc Erki - Cache CMDB lookups (fix #1239) - Ferenc Erki - Test CMDB both with and without caching - Ferenc Erki - Instantiate CMDB object during initialization - Ferenc Erki - Remove redundant argument to rsync command - Steve Dondley - Detect upstart (fix #1190) - Ferenc Erki - Simplify quoting (close #1206) - Ferenc Erki - Better message and remove unused variable (ref #1206) - Roy Storey - Quote parameters that might have spaces (fix #807) - Roy Storey - Resolve symlinks for file management commands (fix #1195, close #1199) - Ferenc Erki - Add resolve_symlink helper - Ferenc Erki - Add symlink tests for file management commands - Ferenc Erki - Update link of installation intructions - Ferenc Erki - Update testing instructions - Ferenc Erki - Update list of useful resources - Ferenc Erki - Fix typos - Ferenc Erki - Update supported Perl versions - Ferenc Erki - Convert namespace separators from module to task (fix #1193) - Ferenc Erki - Add tests for package hooks (ref #1194) - Mitch Broadhead - create_host: drop useless get_host invocation (close #1181) - Ali Polatel - Run extra tests on Travis - Ferenc Erki - Ignore tidyness checks for generated extra tests - Ferenc Erki - Rename perltidy test - Ferenc Erki - Replace [ExtraTests] with [RunExtraTests] (fix #964) - Kent Fredric - Use file command for file operations - Ferenc Erki - Fix fallback method for getting username - Ferenc Erki - Return username for local connections - Ferenc Erki - only source ~/.profile if it really exists - Joachim Bargsten - Accept critic for ProhibitExplicitReturnUndef - Ferenc Erki - Accept critic for ProhibitNoStrict - Ferenc Erki - Accept critic for ProhibitSubroutinePrototypes - Ferenc Erki - Remove unnecessary subroutine prototype - Ferenc Erki - Fix critic for ProhibitMutatingListFunctions - Ferenc Erki - Fix critic for ProhibitSleepViaSelect - Erik Huelsmann - Disable tidyness checks in perlcritic - Ferenc Erki - improved error handling in run_task if task does not exist - Joachim Bargsten - Fixes virtualization type,role for Digital Ocean Droplets (close #1192) - Mitch Broadhead - Fix UID handling for OpenBSD (fix #1213) - Olivier Cherrier - Added the Option 'continuous_read' to the documentation - elisdg - Test for code tidyness - Ferenc Erki - Fix placeholder matching for %h in ssh config. (#1214) - Max E. Aubrey - Set minimum required Perl version to 5.10.1 - Ferenc Erki - Use Travis CI for testing - Ferenc Erki - added option to list all tasks, including hidden - Joachim Bargsten - errorless git checkout if on checkout branch - Joachim Bargsten - added clone_args as extra option for git checkout - Joachim Bargsten - git checkout also supports environment settings for e.g. proxies - Joachim Bargsten - added ssh as possible protocols for git-based rex modules - Joachim Bargsten - Improve package handling on OpenBSD - Olivier Cherrier - Enhance the OpenBSD User handling support - Olivier Cherrier - Document the 'requiretty' setting with the sudo command - Erik Huelsmann - delete_host: fix regex so it does not match on prefixes - Ali Polatel - Added Oracle Linux (fix #1168, close #1174) - labbeduddel - Ignore YAML-1.25 (fix #1197) - Ferenc Erki - Adds SLE 15 / Leap 15.0 support - LeMerP - Use virsh connect URI from config - Ferenc Erki - Update README about contributing guide - Ferenc Erki - Initial version of the contributing guide - Ferenc Erki - Convert namespace separators from module to task (fix #1188) - Ferenc Erki - Add test for using needs with nested modules (ref #1188) - Ferenc Erki - Added Virtuozzo as Red Hat clone. - iblinder - Print warning instead of dying when delete_user called on non existing user - Crimson Thompson - Fix Pkgconf for Debian - Andy Beverley 1.6.0 2017-12-03 Jan Gehring - 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 1.5.0 2016-10-09 Jan Gehring - 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 1.4.1 2016-07-16 Jan Gehring - 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 1.4.0 2016-03-06 Ferenc Erki - 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 1.3.3 2015-09-04 Ferenc Erki - 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 1.3.2 2015-06-17 Ferenc Erki - 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 1.3.1 2015-06-08 - Cleanup db tests (fix #714) - Ferenc Erki - Update parallelism docs - Ferenc Erki 1.3.0 2015-06-03 - 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 1.2.1 2015-05-04 - first detect if uname and md5sum can be run, then use it. fixed #665 - Jan 1.2.0 2015-05-02 - 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 1.1.0 2015-03-29 - 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 1.0.0 2015-03-08 - 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 0.18 2011-09-01 Jan Gehring - added network support for Solaris, NetBSD, FreeBSD and OpenBSD - added is_solaris, is_bsd and is_linux function 0.17 2011-09-01 Jan Gehring - 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 0.16 2011-08-28 Jan Gehring - added NetBSD support - added OpenBSD support - fixed a bug in the gentoo pkg management module 0.15 2011-08-07 Jan Gehring - 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 0.57.0 2015-01-11 Jan Gehring - 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 0.56.1 2015-01-06 Jan Gehring - tasks doesn't return a value when called as a sub (#523) - Jan 0.56.0 2014-12-26 Jan Gehring - 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 0.55.3 2014-11-02 Ferenc Erki - Fix @INC compilation for Windows - Ferenc Erki 0.55.2 2014-11-01 Ferenc Erki - 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 0.55.1 2014-10-25 Jan Gehring - 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 0.55.0 2014-10-19 Ferenc Erki - 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 0.54.3 2014-10-03 Jan Gehring - 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 0.53.1 2014-09-13 Jan Gehring - 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. 0.52.0 2014-08-30 Jan Gehring - 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' 0.51.2 2014-07-29 Jan Gehring - 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 0.50.0 2014-07-20 Jan Gehring - 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 0.49.0 2014-07-12 Jan Gehring - Added FreeBSD 10 Support for pkgng. #280 0.48.0 2014-07-10 Jan Gehring - Added CentOS 7 support 0.47.0 2014-07-05 Jan Gehring - 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 0.46.2 2014-05-22 Jan Gehring - fixed tmp_dir configuration - load Rex::Commands::Box if Rex::Test is loaded, so that set(box => '') work - fixed a problem with Test:run 0.46.1 2014-05-19 Jan Gehring - 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 0.46.0 2014-05-01 Jan Gehring - 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. 0.45.3 2014-04-13 Jan Gehring - fixed jiffybox endless loop on creating instances. #344 - reneeb 0.45.2 2014-04-12 Jan Gehring - fixed special mkdir() case on local windows runs. 0.45.1 2014-04-11 Jan Gehring - 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 0.44.6 2014-03-02 Jan Gehring - 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 0.44.5 2014-02-25 Jan Gehring - fixed shell gathering 0.44.4 2014-02-17 Jan Gehring - fixed manifest file 0.44.3 2014-02-14 Jan Gehring - 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 0.44.2 2014-02-08 Jan Gehring - fixed sudo_without_sh - #305 - added warning if no perl interpreter was found on the remote system - #302 0.44.1 2014-02-02 Jan Gehring - 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 0.44.0 2014-01-25 Jan Gehring - 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 0.43.7 2013-10-03 Jan Gehring - fixed problem with unconfigured network devices - fixed return of complete cmdb - fixed bug with the reporting initialization 0.43.3 2013-09-17 Jan Gehring - fixed return of string '0' on stdout - fixed loading of report via env variable - fixed manifest 0.43.2 2013-09-17 Jan Gehring - #234 - Silent yum operations - Chris Steigmeier 0.43.0 2013-09-16 Jan Gehring - #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 0.42.4 2013-07-04 Jan Gehring - fixed an issue with append_if_no_such_line when searching for a string containing a quote. 0.42.3 2013-06-29 Jan Gehring - #189 fixed zero values in crontab - ferki - fixed ownership problem with sudo mode and file manipulation 0.42.2 2013-06-23 Jan Gehring - fixed local run of run_with helper command 0.42.1 2013-06-22 Jan Gehring - #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 0.42.0 2013-06-15 Jan Gehring - 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 0.41.3 2013-05-03 Jan Gehring - 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 0.41.2 2013-04-19 Jan Gehring - 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 0.41.1 2013-03-30 Jan Gehring - fixed a parsing bug in df output - fixed mount command with persistend option 0.41.0 2013-03-30 Jan Gehring - 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 0.40.4 2013-03-10 Jan Gehring - fixed get_box() command if task is run on a remote host - fixed sudo without password 0.40.3 2013-03-09 Jan Gehring - fixed loading of files in lib directory @INC sometime got populated too late 0.40.2 2013-03-02 Jan Gehring - fixed #117 - encode everything except a-z, 0-9 and _ 0.40.1 2013-02-27 Jan Gehring - fixed #114 - used only once warnings - fixed #115 - passwordless sudo didn't work 0.40.0 2013-02-23 Jan Gehring - 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 0.39.0 2013-02-07 Jan Gehring - 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 0.38.0 2013-01-27 Jan Gehring - 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 0.37.1 2013-01-15 Jan Gehring - fixed template bug in modules 0.37.0 2013-01-05 Jan Gehring - 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 0.36.0 2012-12-22 Jan Gehring - 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. 0.35.1 2012-12-22 Jan Gehring - 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 0.34.2 2012-12-14 Jan Gehring - fixed login for Rex::connect 0.34.1 2012-11-25 Jan Gehring - fixed #77. added iptables tests - Dominik Danter - Relax ssh config file parsing - Dominik Schulz 0.34.0 2012-11-02 Jan Gehring - 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 0.33.3 2012-10-04 Jan Gehring - fixed rename() bug, #67 0.33.2 2012-10-02 Jan Gehring - fixed windows bug 0.33.1 2012-09-22 Jan Gehring - 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 0.32.1 2012-08-30 Jan Gehring - fixed a bug in the transaction module 0.32.0 2012-08-21 Jan Gehring - 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 0.31.5 2012-08-16 Jan Gehring - fixed cli parameter bug (-G) - fixed logging bug with %h 0.31.0 2012-08-04 Jan Gehring - fixed a bug for task with the no_ssh attribute - added http transport layer - added possibility to modify task and server authentication 0.30.1 2012-07-21 Jan Gehring - fixed a cli parameter bug for custom user authentication - fixed a batch display bug 0.30.0 2012-06-15 Jan Gehring - 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 0.29.0 2012-05-17 Jan Gehring - 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 0.28.0 2012-05-08 Jan Gehring - 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) 0.27.0 2012-05-04 Jan Gehring - 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 0.26.3 2012-04-26 Jan Gehring - fixed a notification bug in the on_change event of the file function. - fixed a cli parameter bug in the rexify command. 0.26.2 2012-03-28 Jan Gehring - fixed a cli parameter bug (-G) 0.26.1 2012-03-13 Jan Gehring - fixed a bug in the libvirt module 0.26.0 2012-02-19 Jan Gehring - 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 0.25.3 2012-02-16 Jan Gehring - 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 0.25.2 2012-02-15 Jan Gehring - display the correct module name in rexify --search command 0.25.1 2012-02-15 Jan Gehring - don't use github for recipes query 0.25.0 2012-02-15 Jan Gehring - added public repository commands to rexify - added patch from JEEN Lee for gpgcheck on yum repositories 0.24.1 2012-02-13 Jan Gehring - fixed a dependeny bug 0.24.0 2012-02-10 Jan Gehring - 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 0.23.0 2012-01-14 Jan Gehring - Redhat Enterprise Linux Support (5/6) - read ssh_config file - rsync now automatically accept keys 0.22.0 2012-01-04 Jan Gehring - 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) 0.21.1 2011-10-28 Jan Gehring - fix for #8 - HOME environment variable on Windows - fix for #5 - hostname evaluation with ips 0.21.0 2011-10-10 Jan Gehring - 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 0.20.0 2011-09-16 Jan Gehring - 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 0.19.0 2011-09-01 Jan Gehring - added JUnit output module - added environment support - load Rex::Commands::Process as default 0.18.1 2011-09-01 Jan Gehring - fixed a bug registering tasks as functions 0.14.0 2011-08-07 Jan Gehring - Extended API to allow passing of arguments to Rex::Task->run - FreeBSD support - Ubuntu support 0.13.0 2011-08-07 Jan Gehring - 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) 0.12.0 2011-07-23 Jan Gehring - 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 0.11.1 2011-07-26 Jan Gehring - 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) 0.11.0 2011-07-22 Jan Gehring - 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 0.10.1 2011-07-17 Jan Gehring - fixed db disconnect on forks - fixed some typos 0.10.0 2011-07-12 Jan Gehring - 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 0.9.0 2011-07-03 Jan Gehring - 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 0.8.1 2011-07-03 Jan Gehring - fixed mageia detection - fixed bug if dnsdomainname returns no domainname - fixed mkdir bug on setting permissions, caused by a wrong merge 0.8.0 2011-06-26 Jan Gehring - 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 0.7.1 2011-06-25 Jan Gehring - restored the backward compatibility with perl 5.8.x - suppress warning if no parameter is given - fixed mkdir function 0.7.0 2011-06-23 Jan Gehring - 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. 0.6.1 2011-06-19 Jan Gehring - fixed documentation bugs (thanks to djill) - fixed #68827, rewrote is_readable/is_writable - handle auth failure correctly - mkdir now created directories recursive Rex-1.8.1/META.json0000644000175000017500000014546313616635656012720 0ustar ferkiferki{ "abstract" : "the friendly automation framework", "author" : [ "Jan Gehring " ], "dynamic_config" : 1, "generated_by" : "Dist::Zilla version 6.012, 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::UseAllModules" : "0.15" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "7.1101" } }, "develop" : { "requires" : { "Test::CPAN::Changes" : "0.19", "Test::Kwalitee" : "1.21", "Test::MinimumVersion" : "0", "Test::More" : "0.96", "Test::Perl::Critic" : "0", "Test::PerlTidy" : "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, != 1.25", "attributes" : "0", "base" : "0", "constant" : "0", "lib" : "0", "overload" : "0", "perl" : "5.010001", "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.15" } } }, "provides" : { "Rex" : { "file" : "lib/Rex.pm", "version" : "v1.8.1" }, "Rex::Args" : { "file" : "lib/Rex/Args.pm", "version" : "v1.8.1" }, "Rex::Args::Integer" : { "file" : "lib/Rex/Args/Integer.pm", "version" : "v1.8.1" }, "Rex::Args::Single" : { "file" : "lib/Rex/Args/Single.pm", "version" : "v1.8.1" }, "Rex::Args::String" : { "file" : "lib/Rex/Args/String.pm", "version" : "v1.8.1" }, "Rex::Batch" : { "file" : "lib/Rex/Batch.pm", "version" : "v1.8.1" }, "Rex::Box" : { "file" : "lib/Rex/Box.pm", "version" : "v1.8.1" }, "Rex::Box::Amazon" : { "file" : "lib/Rex/Box/Amazon.pm", "version" : "v1.8.1" }, "Rex::Box::Base" : { "file" : "lib/Rex/Box/Base.pm", "version" : "v1.8.1" }, "Rex::Box::Docker" : { "file" : "lib/Rex/Box/Docker.pm", "version" : "v1.8.1" }, "Rex::Box::KVM" : { "file" : "lib/Rex/Box/KVM.pm", "version" : "v1.8.1" }, "Rex::Box::VBox" : { "file" : "lib/Rex/Box/VBox.pm", "version" : "v1.8.1" }, "Rex::CLI" : { "file" : "lib/Rex/CLI.pm", "version" : "v1.8.1" }, "Rex::CMDB" : { "file" : "lib/Rex/CMDB.pm", "version" : "v1.8.1" }, "Rex::CMDB::Base" : { "file" : "lib/Rex/CMDB/Base.pm", "version" : "v1.8.1" }, "Rex::CMDB::YAML" : { "file" : "lib/Rex/CMDB/YAML.pm", "version" : "v1.8.1" }, "Rex::Cloud" : { "file" : "lib/Rex/Cloud.pm", "version" : "v1.8.1" }, "Rex::Cloud::Amazon" : { "file" : "lib/Rex/Cloud/Amazon.pm", "version" : "v1.8.1" }, "Rex::Cloud::Base" : { "file" : "lib/Rex/Cloud/Base.pm", "version" : "v1.8.1" }, "Rex::Cloud::Jiffybox" : { "file" : "lib/Rex/Cloud/Jiffybox.pm", "version" : "v1.8.1" }, "Rex::Cloud::OpenStack" : { "file" : "lib/Rex/Cloud/OpenStack.pm", "version" : "v1.8.1" }, "Rex::Commands" : { "file" : "lib/Rex/Commands.pm", "version" : "v1.8.1" }, "Rex::Commands::Augeas" : { "file" : "lib/Rex/Commands/Augeas.pm", "version" : "v1.8.1" }, "Rex::Commands::Box" : { "file" : "lib/Rex/Commands/Box.pm", "version" : "v1.8.1" }, "Rex::Commands::Cloud" : { "file" : "lib/Rex/Commands/Cloud.pm", "version" : "v1.8.1" }, "Rex::Commands::Cron" : { "file" : "lib/Rex/Commands/Cron.pm", "version" : "v1.8.1" }, "Rex::Commands::DB" : { "file" : "lib/Rex/Commands/DB.pm", "version" : "v1.8.1" }, "Rex::Commands::Download" : { "file" : "lib/Rex/Commands/Download.pm", "version" : "v1.8.1" }, "Rex::Commands::File" : { "file" : "lib/Rex/Commands/File.pm", "version" : "v1.8.1" }, "Rex::Commands::Fs" : { "file" : "lib/Rex/Commands/Fs.pm", "version" : "v1.8.1" }, "Rex::Commands::Gather" : { "file" : "lib/Rex/Commands/Gather.pm", "version" : "v1.8.1" }, "Rex::Commands::Host" : { "file" : "lib/Rex/Commands/Host.pm", "version" : "v1.8.1" }, "Rex::Commands::Inventory" : { "file" : "lib/Rex/Commands/Inventory.pm", "version" : "v1.8.1" }, "Rex::Commands::Iptables" : { "file" : "lib/Rex/Commands/Iptables.pm", "version" : "v1.8.1" }, "Rex::Commands::JobControl" : { "file" : "lib/Rex/Commands/JobControl.pm", "version" : "v1.8.1" }, "Rex::Commands::Kernel" : { "file" : "lib/Rex/Commands/Kernel.pm", "version" : "v1.8.1" }, "Rex::Commands::LVM" : { "file" : "lib/Rex/Commands/LVM.pm", "version" : "v1.8.1" }, "Rex::Commands::MD5" : { "file" : "lib/Rex/Commands/MD5.pm", "version" : "v1.8.1" }, "Rex::Commands::Mkfs" : { "file" : "lib/Rex/Commands/Mkfs.pm", "version" : "v1.8.1" }, "Rex::Commands::Network" : { "file" : "lib/Rex/Commands/Network.pm", "version" : "v1.8.1" }, "Rex::Commands::Notify" : { "file" : "lib/Rex/Commands/Notify.pm", "version" : "v1.8.1" }, "Rex::Commands::Partition" : { "file" : "lib/Rex/Commands/Partition.pm", "version" : "v1.8.1" }, "Rex::Commands::Pkg" : { "file" : "lib/Rex/Commands/Pkg.pm", "version" : "v1.8.1" }, "Rex::Commands::PkgConf" : { "file" : "lib/Rex/Commands/PkgConf.pm", "version" : "v1.8.1" }, "Rex::Commands::Process" : { "file" : "lib/Rex/Commands/Process.pm", "version" : "v1.8.1" }, "Rex::Commands::Rsync" : { "file" : "lib/Rex/Commands/Rsync.pm", "version" : "v1.8.1" }, "Rex::Commands::Run" : { "file" : "lib/Rex/Commands/Run.pm", "version" : "v1.8.1" }, "Rex::Commands::SCM" : { "file" : "lib/Rex/Commands/SCM.pm", "version" : "v1.8.1" }, "Rex::Commands::Service" : { "file" : "lib/Rex/Commands/Service.pm", "version" : "v1.8.1" }, "Rex::Commands::SimpleCheck" : { "file" : "lib/Rex/Commands/SimpleCheck.pm", "version" : "v1.8.1" }, "Rex::Commands::Sync" : { "file" : "lib/Rex/Commands/Sync.pm", "version" : "v1.8.1" }, "Rex::Commands::Sysctl" : { "file" : "lib/Rex/Commands/Sysctl.pm", "version" : "v1.8.1" }, "Rex::Commands::Tail" : { "file" : "lib/Rex/Commands/Tail.pm", "version" : "v1.8.1" }, "Rex::Commands::Upload" : { "file" : "lib/Rex/Commands/Upload.pm", "version" : "v1.8.1" }, "Rex::Commands::User" : { "file" : "lib/Rex/Commands/User.pm", "version" : "v1.8.1" }, "Rex::Commands::Virtualization" : { "file" : "lib/Rex/Commands/Virtualization.pm", "version" : "v1.8.1" }, "Rex::Config" : { "file" : "lib/Rex/Config.pm", "version" : "v1.8.1" }, "Rex::Constants" : { "file" : "lib/Rex/Constants.pm", "version" : "v1.8.1" }, "Rex::Cron" : { "file" : "lib/Rex/Cron.pm", "version" : "v1.8.1" }, "Rex::Cron::Base" : { "file" : "lib/Rex/Cron/Base.pm", "version" : "v1.8.1" }, "Rex::Cron::FreeBSD" : { "file" : "lib/Rex/Cron/FreeBSD.pm", "version" : "v1.8.1" }, "Rex::Cron::Linux" : { "file" : "lib/Rex/Cron/Linux.pm", "version" : "v1.8.1" }, "Rex::Cron::SunOS" : { "file" : "lib/Rex/Cron/SunOS.pm", "version" : "v1.8.1" }, "Rex::Exporter" : { "file" : "lib/Rex/Exporter.pm", "version" : "v1.8.1" }, "Rex::FS::File" : { "file" : "lib/Rex/FS/File.pm", "version" : "v1.8.1" }, "Rex::File::Parser::Data" : { "file" : "lib/Rex/File/Parser/Data.pm", "version" : "v1.8.1" }, "Rex::File::Parser::Ini" : { "file" : "lib/Rex/File/Parser/Ini.pm", "version" : "v1.8.1" }, "Rex::Fork::Manager" : { "file" : "lib/Rex/Fork/Manager.pm", "version" : "v1.8.1" }, "Rex::Fork::Task" : { "file" : "lib/Rex/Fork/Task.pm", "version" : "v1.8.1" }, "Rex::Group" : { "file" : "lib/Rex/Group.pm", "version" : "v1.8.1" }, "Rex::Group::Entry::Server" : { "file" : "lib/Rex/Group/Entry/Server.pm", "version" : "v1.8.1" }, "Rex::Group::Lookup::Command" : { "file" : "lib/Rex/Group/Lookup/Command.pm", "version" : "v1.8.1" }, "Rex::Group::Lookup::DBI" : { "file" : "lib/Rex/Group/Lookup/DBI.pm", "version" : "v1.8.1" }, "Rex::Group::Lookup::File" : { "file" : "lib/Rex/Group/Lookup/File.pm", "version" : "v1.8.1" }, "Rex::Group::Lookup::INI" : { "file" : "lib/Rex/Group/Lookup/INI.pm", "version" : "v1.8.1" }, "Rex::Group::Lookup::XML" : { "file" : "lib/Rex/Group/Lookup/XML.pm", "version" : "v1.8.1" }, "Rex::Group::Lookup::YAML" : { "file" : "lib/Rex/Group/Lookup/YAML.pm", "version" : "v1.8.1" }, "Rex::Hardware" : { "file" : "lib/Rex/Hardware.pm", "version" : "v1.8.1" }, "Rex::Hardware::Host" : { "file" : "lib/Rex/Hardware/Host.pm", "version" : "v1.8.1" }, "Rex::Hardware::Kernel" : { "file" : "lib/Rex/Hardware/Kernel.pm", "version" : "v1.8.1" }, "Rex::Hardware::Memory" : { "file" : "lib/Rex/Hardware/Memory.pm", "version" : "v1.8.1" }, "Rex::Hardware::Network" : { "file" : "lib/Rex/Hardware/Network.pm", "version" : "v1.8.1" }, "Rex::Hardware::Network::Darwin" : { "file" : "lib/Rex/Hardware/Network/Darwin.pm", "version" : "v1.8.1" }, "Rex::Hardware::Network::FreeBSD" : { "file" : "lib/Rex/Hardware/Network/FreeBSD.pm", "version" : "v1.8.1" }, "Rex::Hardware::Network::Linux" : { "file" : "lib/Rex/Hardware/Network/Linux.pm", "version" : "v1.8.1" }, "Rex::Hardware::Network::NetBSD" : { "file" : "lib/Rex/Hardware/Network/NetBSD.pm", "version" : "v1.8.1" }, "Rex::Hardware::Network::OpenBSD" : { "file" : "lib/Rex/Hardware/Network/OpenBSD.pm", "version" : "v1.8.1" }, "Rex::Hardware::Network::Solaris" : { "file" : "lib/Rex/Hardware/Network/Solaris.pm", "version" : "v1.8.1" }, "Rex::Hardware::Swap" : { "file" : "lib/Rex/Hardware/Swap.pm", "version" : "v1.8.1" }, "Rex::Hardware::VirtInfo" : { "file" : "lib/Rex/Hardware/VirtInfo.pm", "version" : "v1.8.1" }, "Rex::Helper::Array" : { "file" : "lib/Rex/Helper/Array.pm", "version" : "v1.8.1" }, "Rex::Helper::DBI" : { "file" : "lib/Rex/Helper/DBI.pm", "version" : "v1.8.1" }, "Rex::Helper::Encode" : { "file" : "lib/Rex/Helper/Encode.pm", "version" : "v1.8.1" }, "Rex::Helper::File::Spec" : { "file" : "lib/Rex/Helper/File/Spec.pm", "version" : "v1.8.1" }, "Rex::Helper::File::Stat" : { "file" : "lib/Rex/Helper/File/Stat.pm", "version" : "v1.8.1" }, "Rex::Helper::File::Stat::Unix" : { "file" : "lib/Rex/Helper/File/Stat/Unix.pm", "version" : "v1.8.1" }, "Rex::Helper::File::Stat::Win32" : { "file" : "lib/Rex/Helper/File/Stat/Win32.pm", "version" : "v1.8.1" }, "Rex::Helper::Hash" : { "file" : "lib/Rex/Helper/Hash.pm", "version" : "v1.8.1" }, "Rex::Helper::INI" : { "file" : "lib/Rex/Helper/INI.pm", "version" : "v1.8.1" }, "Rex::Helper::IP" : { "file" : "lib/Rex/Helper/IP.pm", "version" : "v1.8.1" }, "Rex::Helper::Misc" : { "file" : "lib/Rex/Helper/Misc.pm", "version" : "v1.8.1" }, "Rex::Helper::Path" : { "file" : "lib/Rex/Helper/Path.pm", "version" : "v1.8.1" }, "Rex::Helper::Rexfile::ParamLookup" : { "file" : "lib/Rex/Helper/Rexfile/ParamLookup.pm", "version" : "v1.8.1" }, "Rex::Helper::Run" : { "file" : "lib/Rex/Helper/Run.pm", "version" : "v1.8.1" }, "Rex::Helper::SSH2" : { "file" : "lib/Rex/Helper/SSH2.pm", "version" : "v1.8.1" }, "Rex::Helper::SSH2::Expect" : { "file" : "lib/Rex/Helper/SSH2/Expect.pm", "version" : "v1.8.1" }, "Rex::Helper::System" : { "file" : "lib/Rex/Helper/System.pm", "version" : "v1.8.1" }, "Rex::Helper::URI" : { "file" : "lib/Rex/Helper/URI.pm", "version" : "v1.8.1" }, "Rex::Helper::UserAgent" : { "file" : "lib/Rex/Helper/UserAgent.pm", "version" : "v1.8.1" }, "Rex::Hook" : { "file" : "lib/Rex/Hook.pm", "version" : "v1.8.1" }, "Rex::Interface::Cache" : { "file" : "lib/Rex/Interface/Cache.pm", "version" : "v1.8.1" }, "Rex::Interface::Cache::Base" : { "file" : "lib/Rex/Interface/Cache/Base.pm", "version" : "v1.8.1" }, "Rex::Interface::Cache::YAML" : { "file" : "lib/Rex/Interface/Cache/YAML.pm", "version" : "v1.8.1" }, "Rex::Interface::Connection" : { "file" : "lib/Rex/Interface/Connection.pm", "version" : "v1.8.1" }, "Rex::Interface::Connection::Base" : { "file" : "lib/Rex/Interface/Connection/Base.pm", "version" : "v1.8.1" }, "Rex::Interface::Connection::Fake" : { "file" : "lib/Rex/Interface/Connection/Fake.pm", "version" : "v1.8.1" }, "Rex::Interface::Connection::HTTP" : { "file" : "lib/Rex/Interface/Connection/HTTP.pm", "version" : "v1.8.1" }, "Rex::Interface::Connection::HTTPS" : { "file" : "lib/Rex/Interface/Connection/HTTPS.pm", "version" : "v1.8.1" }, "Rex::Interface::Connection::Local" : { "file" : "lib/Rex/Interface/Connection/Local.pm", "version" : "v1.8.1" }, "Rex::Interface::Connection::OpenSSH" : { "file" : "lib/Rex/Interface/Connection/OpenSSH.pm", "version" : "v1.8.1" }, "Rex::Interface::Connection::SSH" : { "file" : "lib/Rex/Interface/Connection/SSH.pm", "version" : "v1.8.1" }, "Rex::Interface::Exec" : { "file" : "lib/Rex/Interface/Exec.pm", "version" : "v1.8.1" }, "Rex::Interface::Exec::Base" : { "file" : "lib/Rex/Interface/Exec/Base.pm", "version" : "v1.8.1" }, "Rex::Interface::Exec::HTTP" : { "file" : "lib/Rex/Interface/Exec/HTTP.pm", "version" : "v1.8.1" }, "Rex::Interface::Exec::IOReader" : { "file" : "lib/Rex/Interface/Exec/IOReader.pm", "version" : "v1.8.1" }, "Rex::Interface::Exec::Local" : { "file" : "lib/Rex/Interface/Exec/Local.pm", "version" : "v1.8.1" }, "Rex::Interface::Exec::OpenSSH" : { "file" : "lib/Rex/Interface/Exec/OpenSSH.pm", "version" : "v1.8.1" }, "Rex::Interface::Exec::SSH" : { "file" : "lib/Rex/Interface/Exec/SSH.pm", "version" : "v1.8.1" }, "Rex::Interface::Exec::Sudo" : { "file" : "lib/Rex/Interface/Exec/Sudo.pm", "version" : "v1.8.1" }, "Rex::Interface::Executor" : { "file" : "lib/Rex/Interface/Executor.pm", "version" : "v1.8.1" }, "Rex::Interface::Executor::Base" : { "file" : "lib/Rex/Interface/Executor/Base.pm", "version" : "v1.8.1" }, "Rex::Interface::Executor::Default" : { "file" : "lib/Rex/Interface/Executor/Default.pm", "version" : "v1.8.1" }, "Rex::Interface::File" : { "file" : "lib/Rex/Interface/File.pm", "version" : "v1.8.1" }, "Rex::Interface::File::Base" : { "file" : "lib/Rex/Interface/File/Base.pm", "version" : "v1.8.1" }, "Rex::Interface::File::HTTP" : { "file" : "lib/Rex/Interface/File/HTTP.pm", "version" : "v1.8.1" }, "Rex::Interface::File::Local" : { "file" : "lib/Rex/Interface/File/Local.pm", "version" : "v1.8.1" }, "Rex::Interface::File::OpenSSH" : { "file" : "lib/Rex/Interface/File/OpenSSH.pm", "version" : "v1.8.1" }, "Rex::Interface::File::SSH" : { "file" : "lib/Rex/Interface/File/SSH.pm", "version" : "v1.8.1" }, "Rex::Interface::File::Sudo" : { "file" : "lib/Rex/Interface/File/Sudo.pm", "version" : "v1.8.1" }, "Rex::Interface::Fs" : { "file" : "lib/Rex/Interface/Fs.pm", "version" : "v1.8.1" }, "Rex::Interface::Fs::Base" : { "file" : "lib/Rex/Interface/Fs/Base.pm", "version" : "v1.8.1" }, "Rex::Interface::Fs::HTTP" : { "file" : "lib/Rex/Interface/Fs/HTTP.pm", "version" : "v1.8.1" }, "Rex::Interface::Fs::Local" : { "file" : "lib/Rex/Interface/Fs/Local.pm", "version" : "v1.8.1" }, "Rex::Interface::Fs::OpenSSH" : { "file" : "lib/Rex/Interface/Fs/OpenSSH.pm", "version" : "v1.8.1" }, "Rex::Interface::Fs::SSH" : { "file" : "lib/Rex/Interface/Fs/SSH.pm", "version" : "v1.8.1" }, "Rex::Interface::Fs::Sudo" : { "file" : "lib/Rex/Interface/Fs/Sudo.pm", "version" : "v1.8.1" }, "Rex::Interface::Shell" : { "file" : "lib/Rex/Interface/Shell.pm", "version" : "v1.8.1" }, "Rex::Interface::Shell::Ash" : { "file" : "lib/Rex/Interface/Shell/Ash.pm", "version" : "v1.8.1" }, "Rex::Interface::Shell::Base" : { "file" : "lib/Rex/Interface/Shell/Base.pm", "version" : "v1.8.1" }, "Rex::Interface::Shell::Bash" : { "file" : "lib/Rex/Interface/Shell/Bash.pm", "version" : "v1.8.1" }, "Rex::Interface::Shell::Csh" : { "file" : "lib/Rex/Interface/Shell/Csh.pm", "version" : "v1.8.1" }, "Rex::Interface::Shell::Default" : { "file" : "lib/Rex/Interface/Shell/Default.pm", "version" : "v1.8.1" }, "Rex::Interface::Shell::Idrac" : { "file" : "lib/Rex/Interface/Shell/Idrac.pm", "version" : "v1.8.1" }, "Rex::Interface::Shell::Ksh" : { "file" : "lib/Rex/Interface/Shell/Ksh.pm", "version" : "v1.8.1" }, "Rex::Interface::Shell::Sh" : { "file" : "lib/Rex/Interface/Shell/Sh.pm", "version" : "v1.8.1" }, "Rex::Interface::Shell::Tcsh" : { "file" : "lib/Rex/Interface/Shell/Tcsh.pm", "version" : "v1.8.1" }, "Rex::Interface::Shell::Zsh" : { "file" : "lib/Rex/Interface/Shell/Zsh.pm", "version" : "v1.8.1" }, "Rex::Inventory" : { "file" : "lib/Rex/Inventory.pm", "version" : "v1.8.1" }, "Rex::Inventory::Bios" : { "file" : "lib/Rex/Inventory/Bios.pm", "version" : "v1.8.1" }, "Rex::Inventory::DMIDecode" : { "file" : "lib/Rex/Inventory/DMIDecode.pm", "version" : "v1.8.1" }, "Rex::Inventory::DMIDecode::BaseBoard" : { "file" : "lib/Rex/Inventory/DMIDecode/BaseBoard.pm", "version" : "v1.8.1" }, "Rex::Inventory::DMIDecode::Bios" : { "file" : "lib/Rex/Inventory/DMIDecode/Bios.pm", "version" : "v1.8.1" }, "Rex::Inventory::DMIDecode::CPU" : { "file" : "lib/Rex/Inventory/DMIDecode/CPU.pm", "version" : "v1.8.1" }, "Rex::Inventory::DMIDecode::Memory" : { "file" : "lib/Rex/Inventory/DMIDecode/Memory.pm", "version" : "v1.8.1" }, "Rex::Inventory::DMIDecode::MemoryArray" : { "file" : "lib/Rex/Inventory/DMIDecode/MemoryArray.pm", "version" : "v1.8.1" }, "Rex::Inventory::DMIDecode::Section" : { "file" : "lib/Rex/Inventory/DMIDecode/Section.pm", "version" : "v1.8.1" }, "Rex::Inventory::DMIDecode::SystemInformation" : { "file" : "lib/Rex/Inventory/DMIDecode/SystemInformation.pm", "version" : "v1.8.1" }, "Rex::Inventory::HP::ACU" : { "file" : "lib/Rex/Inventory/HP/ACU.pm", "version" : "v1.8.1" }, "Rex::Inventory::Hal" : { "file" : "lib/Rex/Inventory/Hal.pm", "version" : "v1.8.1" }, "Rex::Inventory::Hal::Object" : { "file" : "lib/Rex/Inventory/Hal/Object.pm", "version" : "v1.8.1" }, "Rex::Inventory::Hal::Object::Net" : { "file" : "lib/Rex/Inventory/Hal/Object/Net.pm", "version" : "v1.8.1" }, "Rex::Inventory::Hal::Object::Storage" : { "file" : "lib/Rex/Inventory/Hal/Object/Storage.pm", "version" : "v1.8.1" }, "Rex::Inventory::Hal::Object::Volume" : { "file" : "lib/Rex/Inventory/Hal/Object/Volume.pm", "version" : "v1.8.1" }, "Rex::Inventory::Proc" : { "file" : "lib/Rex/Inventory/Proc.pm", "version" : "v1.8.1" }, "Rex::Inventory::Proc::Cpuinfo" : { "file" : "lib/Rex/Inventory/Proc/Cpuinfo.pm", "version" : "v1.8.1" }, "Rex::Inventory::SMBios" : { "file" : "lib/Rex/Inventory/SMBios.pm", "version" : "v1.8.1" }, "Rex::Inventory::SMBios::BaseBoard" : { "file" : "lib/Rex/Inventory/SMBios/BaseBoard.pm", "version" : "v1.8.1" }, "Rex::Inventory::SMBios::Bios" : { "file" : "lib/Rex/Inventory/SMBios/Bios.pm", "version" : "v1.8.1" }, "Rex::Inventory::SMBios::CPU" : { "file" : "lib/Rex/Inventory/SMBios/CPU.pm", "version" : "v1.8.1" }, "Rex::Inventory::SMBios::Memory" : { "file" : "lib/Rex/Inventory/SMBios/Memory.pm", "version" : "v1.8.1" }, "Rex::Inventory::SMBios::MemoryArray" : { "file" : "lib/Rex/Inventory/SMBios/MemoryArray.pm", "version" : "v1.8.1" }, "Rex::Inventory::SMBios::Section" : { "file" : "lib/Rex/Inventory/SMBios/Section.pm", "version" : "v1.8.1" }, "Rex::Inventory::SMBios::SystemInformation" : { "file" : "lib/Rex/Inventory/SMBios/SystemInformation.pm", "version" : "v1.8.1" }, "Rex::Logger" : { "file" : "lib/Rex/Logger.pm", "version" : "v1.8.1" }, "Rex::Notify" : { "file" : "lib/Rex/Notify.pm", "version" : "v1.8.1" }, "Rex::Output" : { "file" : "lib/Rex/Output.pm", "version" : "v1.8.1" }, "Rex::Output::Base" : { "file" : "lib/Rex/Output/Base.pm", "version" : "v1.8.1" }, "Rex::Output::JUnit" : { "file" : "lib/Rex/Output/JUnit.pm", "version" : "v1.8.1" }, "Rex::Pkg" : { "file" : "lib/Rex/Pkg.pm", "version" : "v1.8.1" }, "Rex::Pkg::ALT" : { "file" : "lib/Rex/Pkg/ALT.pm", "version" : "v1.8.1" }, "Rex::Pkg::Arch" : { "file" : "lib/Rex/Pkg/Arch.pm", "version" : "v1.8.1" }, "Rex::Pkg::Base" : { "file" : "lib/Rex/Pkg/Base.pm", "version" : "v1.8.1" }, "Rex::Pkg::Debian" : { "file" : "lib/Rex/Pkg/Debian.pm", "version" : "v1.8.1" }, "Rex::Pkg::FreeBSD" : { "file" : "lib/Rex/Pkg/FreeBSD.pm", "version" : "v1.8.1" }, "Rex::Pkg::Gentoo" : { "file" : "lib/Rex/Pkg/Gentoo.pm", "version" : "v1.8.1" }, "Rex::Pkg::Mageia" : { "file" : "lib/Rex/Pkg/Mageia.pm", "version" : "v1.8.1" }, "Rex::Pkg::NetBSD" : { "file" : "lib/Rex/Pkg/NetBSD.pm", "version" : "v1.8.1" }, "Rex::Pkg::OpenBSD" : { "file" : "lib/Rex/Pkg/OpenBSD.pm", "version" : "v1.8.1" }, "Rex::Pkg::OpenWrt" : { "file" : "lib/Rex/Pkg/OpenWrt.pm", "version" : "v1.8.1" }, "Rex::Pkg::Redhat" : { "file" : "lib/Rex/Pkg/Redhat.pm", "version" : "v1.8.1" }, "Rex::Pkg::SuSE" : { "file" : "lib/Rex/Pkg/SuSE.pm", "version" : "v1.8.1" }, "Rex::Pkg::SunOS" : { "file" : "lib/Rex/Pkg/SunOS.pm", "version" : "v1.8.1" }, "Rex::Pkg::SunOS::OpenCSW" : { "file" : "lib/Rex/Pkg/SunOS/OpenCSW.pm", "version" : "v1.8.1" }, "Rex::Pkg::SunOS::pkg" : { "file" : "lib/Rex/Pkg/SunOS/pkg.pm", "version" : "v1.8.1" }, "Rex::Pkg::Ubuntu" : { "file" : "lib/Rex/Pkg/Ubuntu.pm", "version" : "v1.8.1" }, "Rex::Pkg::VoidLinux" : { "file" : "lib/Rex/Pkg/VoidLinux.pm", "version" : "v1.8.1" }, "Rex::PkgConf" : { "file" : "lib/Rex/PkgConf.pm", "version" : "v1.8.1" }, "Rex::PkgConf::Base" : { "file" : "lib/Rex/PkgConf/Base.pm", "version" : "v1.8.1" }, "Rex::PkgConf::Debian" : { "file" : "lib/Rex/PkgConf/Debian.pm", "version" : "v1.8.1" }, "Rex::Profiler" : { "file" : "lib/Rex/Profiler.pm", "version" : "v1.8.1" }, "Rex::Report" : { "file" : "lib/Rex/Report.pm", "version" : "v1.8.1" }, "Rex::Report::Base" : { "file" : "lib/Rex/Report/Base.pm", "version" : "v1.8.1" }, "Rex::Report::YAML" : { "file" : "lib/Rex/Report/YAML.pm", "version" : "v1.8.1" }, "Rex::Require" : { "file" : "lib/Rex/Require.pm", "version" : "v1.8.1" }, "Rex::Resource" : { "file" : "lib/Rex/Resource.pm", "version" : "v1.8.1" }, "Rex::Resource::Common" : { "file" : "lib/Rex/Resource/Common.pm", "version" : "v1.8.1" }, "Rex::Resource::firewall" : { "file" : "lib/Rex/Resource/firewall.pm", "version" : "v1.8.1" }, "Rex::Resource::firewall::Provider::base" : { "file" : "lib/Rex/Resource/firewall/Provider/base.pm", "version" : "v1.8.1" }, "Rex::Resource::firewall::Provider::iptables" : { "file" : "lib/Rex/Resource/firewall/Provider/iptables.pm", "version" : "v1.8.1" }, "Rex::Resource::firewall::Provider::ufw" : { "file" : "lib/Rex/Resource/firewall/Provider/ufw.pm", "version" : "v1.8.1" }, "Rex::RunList" : { "file" : "lib/Rex/RunList.pm", "version" : "v1.8.1" }, "Rex::SCM::Git" : { "file" : "lib/Rex/SCM/Git.pm", "version" : "v1.8.1" }, "Rex::SCM::Subversion" : { "file" : "lib/Rex/SCM/Subversion.pm", "version" : "v1.8.1" }, "Rex::Service" : { "file" : "lib/Rex/Service.pm", "version" : "v1.8.1" }, "Rex::Service::ALT" : { "file" : "lib/Rex/Service/ALT.pm", "version" : "v1.8.1" }, "Rex::Service::ALT::systemd" : { "file" : "lib/Rex/Service/ALT/systemd.pm", "version" : "v1.8.1" }, "Rex::Service::Arch::systemd" : { "file" : "lib/Rex/Service/Arch/systemd.pm", "version" : "v1.8.1" }, "Rex::Service::Base" : { "file" : "lib/Rex/Service/Base.pm", "version" : "v1.8.1" }, "Rex::Service::Debian" : { "file" : "lib/Rex/Service/Debian.pm", "version" : "v1.8.1" }, "Rex::Service::Debian::systemd" : { "file" : "lib/Rex/Service/Debian/systemd.pm", "version" : "v1.8.1" }, "Rex::Service::FreeBSD" : { "file" : "lib/Rex/Service/FreeBSD.pm", "version" : "v1.8.1" }, "Rex::Service::Gentoo" : { "file" : "lib/Rex/Service/Gentoo.pm", "version" : "v1.8.1" }, "Rex::Service::Gentoo::systemd" : { "file" : "lib/Rex/Service/Gentoo/systemd.pm", "version" : "v1.8.1" }, "Rex::Service::Mageia" : { "file" : "lib/Rex/Service/Mageia.pm", "version" : "v1.8.1" }, "Rex::Service::Mageia::systemd" : { "file" : "lib/Rex/Service/Mageia/systemd.pm", "version" : "v1.8.1" }, "Rex::Service::NetBSD" : { "file" : "lib/Rex/Service/NetBSD.pm", "version" : "v1.8.1" }, "Rex::Service::OpenBSD" : { "file" : "lib/Rex/Service/OpenBSD.pm", "version" : "v1.8.1" }, "Rex::Service::OpenWrt" : { "file" : "lib/Rex/Service/OpenWrt.pm", "version" : "v1.8.1" }, "Rex::Service::Redhat" : { "file" : "lib/Rex/Service/Redhat.pm", "version" : "v1.8.1" }, "Rex::Service::Redhat::systemd" : { "file" : "lib/Rex/Service/Redhat/systemd.pm", "version" : "v1.8.1" }, "Rex::Service::SuSE" : { "file" : "lib/Rex/Service/SuSE.pm", "version" : "v1.8.1" }, "Rex::Service::SuSE::systemd" : { "file" : "lib/Rex/Service/SuSE/systemd.pm", "version" : "v1.8.1" }, "Rex::Service::SunOS" : { "file" : "lib/Rex/Service/SunOS.pm", "version" : "v1.8.1" }, "Rex::Service::SunOS::svcadm" : { "file" : "lib/Rex/Service/SunOS/svcadm.pm", "version" : "v1.8.1" }, "Rex::Service::Ubuntu" : { "file" : "lib/Rex/Service/Ubuntu.pm", "version" : "v1.8.1" }, "Rex::Service::VoidLinux" : { "file" : "lib/Rex/Service/VoidLinux.pm", "version" : "v1.8.1" }, "Rex::Shared::Var" : { "file" : "lib/Rex/Shared/Var.pm", "version" : "v1.8.1" }, "Rex::Shared::Var::Array" : { "file" : "lib/Rex/Shared/Var/Array.pm", "version" : "v1.8.1" }, "Rex::Shared::Var::Common" : { "file" : "lib/Rex/Shared/Var/Common.pm", "version" : "v1.8.1" }, "Rex::Shared::Var::Hash" : { "file" : "lib/Rex/Shared/Var/Hash.pm", "version" : "v1.8.1" }, "Rex::Shared::Var::Scalar" : { "file" : "lib/Rex/Shared/Var/Scalar.pm", "version" : "v1.8.1" }, "Rex::Sudo::File" : { "file" : "lib/Rex/Sudo/File.pm", "version" : "v1.8.1" }, "Rex::Task" : { "file" : "lib/Rex/Task.pm", "version" : "v1.8.1" }, "Rex::TaskList" : { "file" : "lib/Rex/TaskList.pm", "version" : "v1.8.1" }, "Rex::TaskList::Base" : { "file" : "lib/Rex/TaskList/Base.pm", "version" : "v1.8.1" }, "Rex::TaskList::Parallel_ForkManager" : { "file" : "lib/Rex/TaskList/Parallel_ForkManager.pm", "version" : "v1.8.1" }, "Rex::Template" : { "file" : "lib/Rex/Template.pm", "version" : "v1.8.1" }, "Rex::Template::NG" : { "file" : "lib/Rex/Template/NG.pm", "version" : "v1.8.1" }, "Rex::Test" : { "file" : "lib/Rex/Test.pm", "version" : "v1.8.1" }, "Rex::Test::Base" : { "file" : "lib/Rex/Test/Base.pm", "version" : "v1.8.1" }, "Rex::Test::Base::has_content" : { "file" : "lib/Rex/Test/Base/has_content.pm", "version" : "v1.8.1" }, "Rex::Test::Base::has_cron" : { "file" : "lib/Rex/Test/Base/has_cron.pm", "version" : "v1.8.1" }, "Rex::Test::Base::has_cron_env" : { "file" : "lib/Rex/Test/Base/has_cron_env.pm", "version" : "v1.8.1" }, "Rex::Test::Base::has_dir" : { "file" : "lib/Rex/Test/Base/has_dir.pm", "version" : "v1.8.1" }, "Rex::Test::Base::has_file" : { "file" : "lib/Rex/Test/Base/has_file.pm", "version" : "v1.8.1" }, "Rex::Test::Base::has_file_content" : { "file" : "lib/Rex/Test/Base/has_file_content.pm", "version" : "v1.8.1" }, "Rex::Test::Base::has_output" : { "file" : "lib/Rex/Test/Base/has_output.pm", "version" : "v1.8.1" }, "Rex::Test::Base::has_output_matching" : { "file" : "lib/Rex/Test/Base/has_output_matching.pm", "version" : "v1.8.1" }, "Rex::Test::Base::has_package" : { "file" : "lib/Rex/Test/Base/has_package.pm", "version" : "v1.8.1" }, "Rex::Test::Base::has_service_running" : { "file" : "lib/Rex/Test/Base/has_service_running.pm", "version" : "v1.8.1" }, "Rex::Test::Base::has_service_stopped" : { "file" : "lib/Rex/Test/Base/has_service_stopped.pm", "version" : "v1.8.1" }, "Rex::Test::Base::has_stat" : { "file" : "lib/Rex/Test/Base/has_stat.pm", "version" : "v1.8.1" }, "Rex::Transaction" : { "file" : "lib/Rex/Transaction.pm", "version" : "v1.8.1" }, "Rex::User" : { "file" : "lib/Rex/User.pm", "version" : "v1.8.1" }, "Rex::User::Base" : { "file" : "lib/Rex/User/Base.pm", "version" : "v1.8.1" }, "Rex::User::FreeBSD" : { "file" : "lib/Rex/User/FreeBSD.pm", "version" : "v1.8.1" }, "Rex::User::Linux" : { "file" : "lib/Rex/User/Linux.pm", "version" : "v1.8.1" }, "Rex::User::NetBSD" : { "file" : "lib/Rex/User/NetBSD.pm", "version" : "v1.8.1" }, "Rex::User::OpenBSD" : { "file" : "lib/Rex/User/OpenBSD.pm", "version" : "v1.8.1" }, "Rex::User::OpenWrt" : { "file" : "lib/Rex/User/OpenWrt.pm", "version" : "v1.8.1" }, "Rex::User::SunOS" : { "file" : "lib/Rex/User/SunOS.pm", "version" : "v1.8.1" }, "Rex::Value" : { "file" : "lib/Rex/Value.pm", "version" : "v1.8.1" }, "Rex::Virtualization" : { "file" : "lib/Rex/Virtualization.pm", "version" : "v1.8.1" }, "Rex::Virtualization::Base" : { "file" : "lib/Rex/Virtualization/Base.pm", "version" : "v1.8.1" }, "Rex::Virtualization::Docker" : { "file" : "lib/Rex/Virtualization/Docker.pm", "version" : "v1.8.1" }, "Rex::Virtualization::Docker::create" : { "file" : "lib/Rex/Virtualization/Docker/create.pm", "version" : "v1.8.1" }, "Rex::Virtualization::Docker::daemon" : { "file" : "lib/Rex/Virtualization/Docker/daemon.pm", "version" : "v1.8.1" }, "Rex::Virtualization::Docker::delete" : { "file" : "lib/Rex/Virtualization/Docker/delete.pm", "version" : "v1.8.1" }, "Rex::Virtualization::Docker::destroy" : { "file" : "lib/Rex/Virtualization/Docker/destroy.pm", "version" : "v1.8.1" }, "Rex::Virtualization::Docker::guestinfo" : { "file" : "lib/Rex/Virtualization/Docker/guestinfo.pm", "version" : "v1.8.1" }, "Rex::Virtualization::Docker::images" : { "file" : "lib/Rex/Virtualization/Docker/images.pm", "version" : "v1.8.1" }, "Rex::Virtualization::Docker::import" : { "file" : "lib/Rex/Virtualization/Docker/import.pm", "version" : "v1.8.1" }, "Rex::Virtualization::Docker::info" : { "file" : "lib/Rex/Virtualization/Docker/info.pm", "version" : "v1.8.1" }, "Rex::Virtualization::Docker::list" : { "file" : "lib/Rex/Virtualization/Docker/list.pm", "version" : "v1.8.1" }, "Rex::Virtualization::Docker::reboot" : { "file" : "lib/Rex/Virtualization/Docker/reboot.pm", "version" : "v1.8.1" }, "Rex::Virtualization::Docker::shutdown" : { "file" : "lib/Rex/Virtualization/Docker/shutdown.pm", "version" : "v1.8.1" }, "Rex::Virtualization::Docker::start" : { "file" : "lib/Rex/Virtualization/Docker/start.pm", "version" : "v1.8.1" }, "Rex::Virtualization::Docker::status" : { "file" : "lib/Rex/Virtualization/Docker/status.pm", "version" : "v1.8.1" }, "Rex::Virtualization::LibVirt" : { "file" : "lib/Rex/Virtualization/LibVirt.pm", "version" : "v1.8.1" }, "Rex::Virtualization::LibVirt::blklist" : { "file" : "lib/Rex/Virtualization/LibVirt/blklist.pm", "version" : "v1.8.1" }, "Rex::Virtualization::LibVirt::clone" : { "file" : "lib/Rex/Virtualization/LibVirt/clone.pm", "version" : "v1.8.1" }, "Rex::Virtualization::LibVirt::create" : { "file" : "lib/Rex/Virtualization/LibVirt/create.pm", "version" : "v1.8.1" }, "Rex::Virtualization::LibVirt::delete" : { "file" : "lib/Rex/Virtualization/LibVirt/delete.pm", "version" : "v1.8.1" }, "Rex::Virtualization::LibVirt::destroy" : { "file" : "lib/Rex/Virtualization/LibVirt/destroy.pm", "version" : "v1.8.1" }, "Rex::Virtualization::LibVirt::dumpxml" : { "file" : "lib/Rex/Virtualization/LibVirt/dumpxml.pm", "version" : "v1.8.1" }, "Rex::Virtualization::LibVirt::guestinfo" : { "file" : "lib/Rex/Virtualization/LibVirt/guestinfo.pm", "version" : "v1.8.1" }, "Rex::Virtualization::LibVirt::hypervisor" : { "file" : "lib/Rex/Virtualization/LibVirt/hypervisor.pm", "version" : "v1.8.1" }, "Rex::Virtualization::LibVirt::iflist" : { "file" : "lib/Rex/Virtualization/LibVirt/iflist.pm", "version" : "v1.8.1" }, "Rex::Virtualization::LibVirt::import" : { "file" : "lib/Rex/Virtualization/LibVirt/import.pm", "version" : "v1.8.1" }, "Rex::Virtualization::LibVirt::info" : { "file" : "lib/Rex/Virtualization/LibVirt/info.pm", "version" : "v1.8.1" }, "Rex::Virtualization::LibVirt::list" : { "file" : "lib/Rex/Virtualization/LibVirt/list.pm", "version" : "v1.8.1" }, "Rex::Virtualization::LibVirt::option" : { "file" : "lib/Rex/Virtualization/LibVirt/option.pm", "version" : "v1.8.1" }, "Rex::Virtualization::LibVirt::reboot" : { "file" : "lib/Rex/Virtualization/LibVirt/reboot.pm", "version" : "v1.8.1" }, "Rex::Virtualization::LibVirt::shutdown" : { "file" : "lib/Rex/Virtualization/LibVirt/shutdown.pm", "version" : "v1.8.1" }, "Rex::Virtualization::LibVirt::start" : { "file" : "lib/Rex/Virtualization/LibVirt/start.pm", "version" : "v1.8.1" }, "Rex::Virtualization::LibVirt::status" : { "file" : "lib/Rex/Virtualization/LibVirt/status.pm", "version" : "v1.8.1" }, "Rex::Virtualization::LibVirt::vncdisplay" : { "file" : "lib/Rex/Virtualization/LibVirt/vncdisplay.pm", "version" : "v1.8.1" }, "Rex::Virtualization::Lxc" : { "file" : "lib/Rex/Virtualization/Lxc.pm", "version" : "v1.8.1" }, "Rex::Virtualization::Lxc::attach" : { "file" : "lib/Rex/Virtualization/Lxc/attach.pm", "version" : "v1.8.1" }, "Rex::Virtualization::Lxc::copy" : { "file" : "lib/Rex/Virtualization/Lxc/copy.pm", "version" : "v1.8.1" }, "Rex::Virtualization::Lxc::create" : { "file" : "lib/Rex/Virtualization/Lxc/create.pm", "version" : "v1.8.1" }, "Rex::Virtualization::Lxc::destroy" : { "file" : "lib/Rex/Virtualization/Lxc/destroy.pm", "version" : "v1.8.1" }, "Rex::Virtualization::Lxc::info" : { "file" : "lib/Rex/Virtualization/Lxc/info.pm", "version" : "v1.8.1" }, "Rex::Virtualization::Lxc::list" : { "file" : "lib/Rex/Virtualization/Lxc/list.pm", "version" : "v1.8.1" }, "Rex::Virtualization::Lxc::start" : { "file" : "lib/Rex/Virtualization/Lxc/start.pm", "version" : "v1.8.1" }, "Rex::Virtualization::Lxc::stop" : { "file" : "lib/Rex/Virtualization/Lxc/stop.pm", "version" : "v1.8.1" }, "Rex::Virtualization::VBox" : { "file" : "lib/Rex/Virtualization/VBox.pm", "version" : "v1.8.1" }, "Rex::Virtualization::VBox::bridge" : { "file" : "lib/Rex/Virtualization/VBox/bridge.pm", "version" : "v1.8.1" }, "Rex::Virtualization::VBox::create" : { "file" : "lib/Rex/Virtualization/VBox/create.pm", "version" : "v1.8.1" }, "Rex::Virtualization::VBox::delete" : { "file" : "lib/Rex/Virtualization/VBox/delete.pm", "version" : "v1.8.1" }, "Rex::Virtualization::VBox::destroy" : { "file" : "lib/Rex/Virtualization/VBox/destroy.pm", "version" : "v1.8.1" }, "Rex::Virtualization::VBox::forward_port" : { "file" : "lib/Rex/Virtualization/VBox/forward_port.pm", "version" : "v1.8.1" }, "Rex::Virtualization::VBox::guestinfo" : { "file" : "lib/Rex/Virtualization/VBox/guestinfo.pm", "version" : "v1.8.1" }, "Rex::Virtualization::VBox::import" : { "file" : "lib/Rex/Virtualization/VBox/import.pm", "version" : "v1.8.1" }, "Rex::Virtualization::VBox::info" : { "file" : "lib/Rex/Virtualization/VBox/info.pm", "version" : "v1.8.1" }, "Rex::Virtualization::VBox::list" : { "file" : "lib/Rex/Virtualization/VBox/list.pm", "version" : "v1.8.1" }, "Rex::Virtualization::VBox::option" : { "file" : "lib/Rex/Virtualization/VBox/option.pm", "version" : "v1.8.1" }, "Rex::Virtualization::VBox::reboot" : { "file" : "lib/Rex/Virtualization/VBox/reboot.pm", "version" : "v1.8.1" }, "Rex::Virtualization::VBox::share_folder" : { "file" : "lib/Rex/Virtualization/VBox/share_folder.pm", "version" : "v1.8.1" }, "Rex::Virtualization::VBox::shutdown" : { "file" : "lib/Rex/Virtualization/VBox/shutdown.pm", "version" : "v1.8.1" }, "Rex::Virtualization::VBox::start" : { "file" : "lib/Rex/Virtualization/VBox/start.pm", "version" : "v1.8.1" }, "Rex::Virtualization::VBox::status" : { "file" : "lib/Rex/Virtualization/VBox/status.pm", "version" : "v1.8.1" } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/RexOps/Rex/issues" }, "homepage" : "https://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.8.1", "x_contributors" : [ "A Happy User ", "Alexander Romanenko ", "Alexandr Ciornii ", "Ali Polatel ", "alx542 ", "Anders Ossowicki ", "Andrej Zverev ", "Andrew Solomon ", "Andy Beverley ", "Arnold Bechtoldt ", "Boris D\u00e4ppen ", "Brian Manning ", "Cameron Daniel ", "Chris Steigmeier ", "Christophe Wolfhugel ", "Crimson Thompson ", "Daniel B\u00e4urer ", "Daniel Cesario ", "Daniel Dico ", "Denis Silakov ", "Dmitry Kopytov ", "Dominik Schulz ", "Eduardo J ", "Eivin Giske Skaaren ", "elisdg ", "Elmer Quintanilla ", "Eric Johnson ", "Erik Huelsmann ", "Ferenc Erki ", "Franky Van Liedekerke ", "Fran Rodriguez ", "Graham Todd ", "Harm M\u00fcller ", "Hayato Imai ", "Hiroaki Nakamura ", "Hiroki Matsuo ", "iblinder ", "James D Bearden ", "Jan Gehring ", "Jean Charles Passard ", "Jean-Marie Renouard ", "Jeen Lee ", "Jens Berthold ", "Joachim Bargsten ", "John Karr ", "Jonathan Delgado ", "Jon Gentle ", "Joris DE POOTER ", "Jose Luis Martinez ", "Jose Luis Perez Diez ", "Kasim Tuman ", "Keedi Kim ", "Ken Crowell ", "Kent Fredric ", "Kirill Babikhin ", "labbeduddel ", "Leah Neukirchen ", "LeMerP ", "Mario Domgoergen ", "Max E. Aubrey <35892750+maxeaubrey@users.noreply.github.com>", "Mitch Broadhead ", "Nathan Abu ", "Naveed Massjouni ", "necrophcodr ", "Nicolas Leclercq ", "Nikolay A. Fetisov ", "Nils Domrose ", "okaoka ", "Oleg Hardt ", "Olivier Cherrier ", "Orange ", "Paco Esteban ", "Patrick Lauer ", "Pavel Timofeev ", "perlancar ", "Peter H. Ezetta ", "Peter Manthey ", "petersonchen ", "Pierrick DINTRAT ", "Piotr Karbowski ", "Prajithp ", "Ren\u00e9e B\u00e4cker ", "Robert Abraham ", "Roy Storey ", "Samuele Tognini ", "Sascha Askani ", "Sascha Guenther ", "Simon Bertrang ", "Sol\u00e8ne Rapenne ", "Stephane Benoit ", "Steve Dondley ", "Sven Dowideit ", "Tamas Molnar ", "Tianon Gravi ", "Tokuhiro Matsuno ", "Tomohiro Hosaka ", "Volker Kroll ", "Walery Wysotsky ", "Yanick Champoux ", "\u0421\u0435\u0440\u0433\u0435\u0439 \u0420\u043e\u043c\u0430\u043d\u043e\u0432 ", "\u8303\u91ce\u4eba ", "\u9976\u741b\u7433 ", "Alex Mestiashvili ", "Cuong Manh Le ", "David Golovan ", "Dominik Danter ", "Ilya Evseev ", "Niklas Larsson ", "Qiao Liu ", "Renato CRON " ], "x_generated_by_perl" : "v5.30.1", "x_serialization_backend" : "Cpanel::JSON::XS version 4.18" } Rex-1.8.1/lib/0000755000175000017500000000000013616635656012030 5ustar ferkiferkiRex-1.8.1/lib/Rex.pm0000644000175000017500000005622113616635656013132 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: =encoding UTF-8 =head1 NAME Rex - the friendly automation framework =head1 DESCRIPTION Rex is an automation framework that is friendly to any combinations of local and remote execution, push and pull style of management, or imperative and declarative approach. Its flexibility makes it a great fit for many different use cases, but most commonly Rex is used to automate application deployment and data center infrastructure management tasks. See L for a starting point of available built-in commands. See L for more information about how to use rex on the command line. =head1 SYNOPSIS # In a Rexfile: use Rex -feature => [qw/1.4/]; user "root"; password "ch4ngem3"; desc "Show system information"; task "sysinfo", sub { say run "uname -a"; }; 1; # On the command line: $ rex -H server[01..10] sysinfo =head1 CLASS METHODS =cut package Rex; use strict; use warnings; our $VERSION = '1.8.1'; # 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 = (); 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 = getcwd() . "/$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; Rex-1.8.1/lib/Rex/0000755000175000017500000000000013616635656012566 5ustar ferkiferkiRex-1.8.1/lib/Rex/Pkg.pm0000644000175000017500000000315313616635656013647 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Pkg; use strict; use warnings; our $VERSION = '1.8.1'; # 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; Rex-1.8.1/lib/Rex/Box.pm0000644000175000017500000000160313616635656013654 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Box; use strict; use warnings; our $VERSION = '1.8.1'; # 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; Rex-1.8.1/lib/Rex/CLI.pm0000644000175000017500000005051013616635656013534 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::CLI; use strict; use warnings; our $VERSION = '1.8.1'; # 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, "-Ta", "List all tasks, including hidden"; 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; if ( $opts{'a'} ) { @tasks = sort Rex::TaskList->create()->get_all_tasks(qr/.*/); } else { @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; Rex-1.8.1/lib/Rex/Hook.pm0000644000175000017500000000156713616635656014035 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Hook; use strict; use warnings; our $VERSION = '1.8.1'; # 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; Rex-1.8.1/lib/Rex/Cron.pm0000644000175000017500000000146613616635656014034 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Cron; use strict; use warnings; our $VERSION = '1.8.1'; # 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; Rex-1.8.1/lib/Rex/Args.pm0000644000175000017500000000773313616635656014032 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Args; use strict; use warnings; our $VERSION = '1.8.1'; # VERSION use vars qw(%rex_opts); use Rex::Logger; use Data::Dumper; our $CLEANUP = 1; sub args_spec { return ( a => {}, 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; Rex-1.8.1/lib/Rex/CMDB.pm0000644000175000017500000001043713616635656013636 0ustar ferkiferki# # (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 = get cmdb("vhost"); my %all_information = get cmdb; }; =head1 EXPORTED FUNCTIONS =cut package Rex::CMDB; use strict; use warnings; our $VERSION = '1.8.1'; # 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]; } } } } my $klass = $option->{type}; if ( !$klass ) { # no cmdb set return; } if ( $klass !~ m/::/ ) { $klass = "Rex::CMDB::$klass"; } eval "use $klass"; if ($@) { die("CMDB provider ($klass) not found: $@"); } $CMDB_PROVIDER = $klass->new( %{$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 = get cmdb("vhost"); my %all_information = get cmdb; }; =cut sub cmdb { my ( $item, $server ) = @_; $server ||= connection->server; return if !cmdb_active(); my $value; my $cache = Rex::get_cache(); my $cache_key = "cmdb/$CMDB_PROVIDER/$server"; if ( $cache->valid($cache_key) ) { $value = $cache->get($cache_key); } else { $value = $CMDB_PROVIDER->get( undef, $server ) || undef; $cache->set( $cache_key, $value ); } if ($item) { $value = $value->{$item}; } if ( defined $value ) { return Rex::Value->new( value => $value ); } else { Rex::Logger::debug("CMDB - no item ($item) found"); return; } } sub cmdb_active { return ( $CMDB_PROVIDER ? 1 : 0 ); } 1; Rex-1.8.1/lib/Rex/Test.pm0000644000175000017500000000160213616635656014042 0ustar ferkiferki# # (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.8.1'; # 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; Rex-1.8.1/lib/Rex/User.pm0000644000175000017500000000131113616635656014036 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::User; use strict; use warnings; our $VERSION = '1.8.1'; # 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; Rex-1.8.1/lib/Rex/Task.pm0000644000175000017500000005135713616635656014041 0ustar ferkiferki# # (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.8.1'; # 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; Rex-1.8.1/lib/Rex/Cloud.pm0000644000175000017500000000177613616635656014205 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Cloud; use strict; use warnings; our $VERSION = '1.8.1'; # 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; Rex-1.8.1/lib/Rex/Group.pm0000644000175000017500000000535213616635656014225 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Group; use strict; use warnings; our $VERSION = '1.8.1'; # 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; Rex-1.8.1/lib/Rex/Value.pm0000644000175000017500000000065713616635656014210 0ustar ferkiferki# # (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.8.1'; # 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; Rex-1.8.1/lib/Rex/Batch.pm0000644000175000017500000000207313616635656014147 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Batch; use strict; use warnings; our $VERSION = '1.8.1'; # 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; Rex-1.8.1/lib/Rex/Notify.pm0000644000175000017500000000446513616635656014405 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Notify; use strict; use warnings; our $VERSION = '1.8.1'; # 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; Rex-1.8.1/lib/Rex/Report.pm0000644000175000017500000000104213616635656014374 0ustar ferkiferki# # (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.8.1'; # 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; Rex-1.8.1/lib/Rex/Logger.pm0000644000175000017500000001250613616635656014347 0ustar ferkiferki# # (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.8.1'; # 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; Rex-1.8.1/lib/Rex/Output.pm0000644000175000017500000000224513616635656014427 0ustar ferkiferki# # (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.8.1'; # 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; Rex-1.8.1/lib/Rex/Config.pm0000644000175000017500000005250713616635656014342 0ustar ferkiferki# # (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.8.1'; # 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, $waitpid_blocking_sleep_time, ); # 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 scalar 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 set_waitpid_blocking_sleep_time { my ( $self, $waitpid_blocking_sleep_time ) = @_; } sub get_waitpid_blocking_sleep_time { return $waitpid_blocking_sleep_time // 0.1; } sub import { read_ssh_config_file(); read_config_file(); } no strict 'refs'; ## no critic ProhibitNoStrict __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 waitpid_blocking_sleep_time/; 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; Rex-1.8.1/lib/Rex/Require.pm0000644000175000017500000000222413616635656014540 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Require; use strict; use warnings; our $VERSION = '1.8.1'; # 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; Rex-1.8.1/lib/Rex/PkgConf.pm0000644000175000017500000000300213616635656014446 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::PkgConf; use strict; use warnings; our $VERSION = '1.8.1'; # 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; Rex-1.8.1/lib/Rex/RunList.pm0000644000175000017500000000601513616635656014526 0ustar ferkiferki# # (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.8.1'; # 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; Rex-1.8.1/lib/Rex/Service.pm0000644000175000017500000000513213616635656014525 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Service; use strict; use warnings; our $VERSION = '1.8.1'; # 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; i_run "initctl version | grep upstart", fail_ok => 1; my $running_upstart = $? == 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) && $running_upstart ) { # this is mainly Ubuntu with upstart $class = "Rex::Service::Ubuntu"; } 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; Rex-1.8.1/lib/Rex/Box/0000755000175000017500000000000013616635656013316 5ustar ferkiferkiRex-1.8.1/lib/Rex/Box/KVM.pm0000644000175000017500000000750313616635656014316 0ustar ferkiferki# # (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.8.1'; # 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; Rex-1.8.1/lib/Rex/Box/VBox.pm0000644000175000017500000001705713616635656014544 0ustar ferkiferki# # (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.8.1'; # 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; Rex-1.8.1/lib/Rex/Box/Base.pm0000644000175000017500000002006113616635656014525 0ustar ferkiferki# # (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.8.1'; # 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; Rex-1.8.1/lib/Rex/Box/Amazon.pm0000644000175000017500000001273613616635656015112 0ustar ferkiferki# # (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.8.1'; # 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; Rex-1.8.1/lib/Rex/Box/Docker.pm0000644000175000017500000001045713616635656015072 0ustar ferkiferki# # (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.8.1'; # 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; Rex-1.8.1/lib/Rex/FS/0000755000175000017500000000000013616635656013076 5ustar ferkiferkiRex-1.8.1/lib/Rex/FS/File.pm0000644000175000017500000000663113616635656014321 0ustar ferkiferki# # (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.8.1'; # 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; Rex-1.8.1/lib/Rex/Pkg/0000755000175000017500000000000013616635656013307 5ustar ferkiferkiRex-1.8.1/lib/Rex/Pkg/ALT.pm0000644000175000017500000000331613616635656014270 0ustar ferkiferki# # Work with ALT Linux APT-RPM package management system # package Rex::Pkg::ALT; use strict; use warnings; our $VERSION = '1.8.1'; # 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; Rex-1.8.1/lib/Rex/Pkg/Arch.pm0000644000175000017500000000561213616635656014526 0ustar ferkiferki# # (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.8.1'; # 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; Rex-1.8.1/lib/Rex/Pkg/Base.pm0000644000175000017500000001276713616635656014534 0ustar ferkiferki# # (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.8.1'; # 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'} || ''; my $env = $option->{'env'} || (); 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, env => $env; 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; Rex-1.8.1/lib/Rex/Pkg/SuSE.pm0000644000175000017500000000466113616635656014473 0ustar ferkiferki# # (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.8.1'; # 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; Rex-1.8.1/lib/Rex/Pkg/SunOS.pm0000644000175000017500000000453113616635656014657 0ustar ferkiferki# # (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.8.1'; # 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; Rex-1.8.1/lib/Rex/Pkg/NetBSD.pm0000644000175000017500000000234013616635656014723 0ustar ferkiferki# # (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.8.1'; # 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; Rex-1.8.1/lib/Rex/Pkg/Ubuntu.pm0000644000175000017500000000061213616635656015126 0ustar ferkiferki# # (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.8.1'; # 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; Rex-1.8.1/lib/Rex/Pkg/Debian.pm0000644000175000017500000001020213616635656015022 0ustar ferkiferki# # (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.8.1'; # 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; Rex-1.8.1/lib/Rex/Pkg/Gentoo.pm0000644000175000017500000001315013616635656015100 0ustar ferkiferki# # (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.8.1'; # 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; Rex-1.8.1/lib/Rex/Pkg/Mageia.pm0000644000175000017500000000403413616635656015031 0ustar ferkiferki# # (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.8.1'; # 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; Rex-1.8.1/lib/Rex/Pkg/Redhat.pm0000644000175000017500000000601413616635656015055 0ustar ferkiferki# # (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.8.1'; # 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; Rex-1.8.1/lib/Rex/Pkg/OpenWrt.pm0000644000175000017500000000357013616635656015250 0ustar ferkiferki# 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.8.1'; # 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; Rex-1.8.1/lib/Rex/Pkg/FreeBSD.pm0000644000175000017500000000461713616635656015067 0ustar ferkiferki# # (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.8.1'; # 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; Rex-1.8.1/lib/Rex/Pkg/OpenBSD.pm0000644000175000017500000000230113616635656015073 0ustar ferkiferki# # (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.8.1'; # 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 => '/usr/sbin/pkg_add %s', install_version => '/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; Rex-1.8.1/lib/Rex/Pkg/VoidLinux.pm0000644000175000017500000000412113616635656015564 0ustar ferkiferki# # (c) 2019 Leah Neukirchen # based on Rex::Pkg::Arch # (c) Harm Müller # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Pkg::VoidLinux; use strict; use warnings; our $VERSION = '1.8.1'; # 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 => 'xbps-install -y %s', install_version => 'xbps-install -y %s', # makes no sense to specify the package version update_system => 'xbps-install -yu', dist_update_system => 'xbps-install -yu', remove => 'xbps-remove -Ry %s', update_package_db => 'xbps-install -S', }; } else { $self->{commands} = { install => 'xbps-install -y %s', install_version => 'xbps-install -y %s', # makes no sense to specify the package version update_system => 'xbps-install -Syu', dist_update_system => 'xbps-install -Syu', remove => 'xbps-remove -Ry %s', update_package_db => 'xbps-install -S', }; } 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 = 'xbps-query --search='; if ( defined($pkg) ) { $pkg_query .= $pkg; } my @installed_packages = i_run $pkg_query; for my $line (@installed_packages) { if ( $line =~ /^\[\*\] ([^ ]*)-([^- ]*)_(\d+)/ ) { $name = $1; $version = $2; $release = $3; push( @pkgs, { name => $name, version => $version, release => $release, } ); } } return @pkgs; } 1; Rex-1.8.1/lib/Rex/Pkg/SunOS/0000755000175000017500000000000013616635656014316 5ustar ferkiferkiRex-1.8.1/lib/Rex/Pkg/SunOS/pkg.pm0000644000175000017500000000220413616635656015433 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Pkg::SunOS::pkg; use strict; use warnings; our $VERSION = '1.8.1'; # VERSION use Rex::Helper::Run; use Rex::Commands::File; use Rex::Pkg::SunOS; use base qw(Rex::Pkg::SunOS); sub new { my $that = shift; my $proto = ref($that) || $that; my $self = $proto->SUPER::new(@_); bless( $self, $proto ); $self->{commands} = { install => 'pkg install -q --accept %s', install_version => 'pkg install -q --accept %s', remove => 'pkg uninstall -r -q %s', update_package_db => 'pkg refresh', }; return $self; } sub get_installed { my ($self) = @_; my @lines = i_run "pkg info -l"; my @pkg; my ( $version, $name ); for my $line (@lines) { if ( $line =~ m/^$/ ) { push( @pkg, { name => $name, version => $version, } ); next; } if ( $line =~ m/Name: .*\/(.*?)$/ ) { $name = $1; next; } if ( $line =~ m/Version: (.*)$/ ) { $version = $1; next; } } return @pkg; } 1; Rex-1.8.1/lib/Rex/Pkg/SunOS/OpenCSW.pm0000644000175000017500000000134513616635656016135 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Pkg::SunOS::OpenCSW; use strict; use warnings; our $VERSION = '1.8.1'; # VERSION use Rex::Commands::File; use Rex::Pkg::SunOS; use base qw(Rex::Pkg::SunOS); sub new { my $that = shift; my $proto = ref($that) || $that; my $self = $proto->SUPER::new(@_); bless( $self, $proto ); $self->{commands} = { install => $self->_pkgutil() . ' --yes -i %s', install_version => $self->_pkgutil() . ' --yes -i %s', remove => $self->_pkgutil() . ' --yes -r %s', update_package_db => $self->_pkgutil() . " -U", }; return $self; } sub _pkgutil { return "/opt/csw/bin/pkgutil"; } 1; Rex-1.8.1/lib/Rex/SCM/0000755000175000017500000000000013616635656013210 5ustar ferkiferkiRex-1.8.1/lib/Rex/SCM/Git.pm0000644000175000017500000001040413616635656014270 0ustar ferkiferkipackage Rex::SCM::Git; use strict; use warnings; our $VERSION = '1.8.1'; # 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 %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 ) = @_; my %run_opt; $run_opt{env} = $checkout_opt->{env} if ( $checkout_opt->{env} ); my $clone_args = join( " ", @{ $checkout_opt->{clone_args} || [''] } ); if ( !is_dir($checkout_to) ) { my $clone_cmd = sprintf( $CLONE_COMMAND, $clone_args, $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, %run_opt; 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, %run_opt; 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, %run_opt; 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, %run_opt; 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, %run_opt; 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, %run_opt; 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; Rex-1.8.1/lib/Rex/SCM/Subversion.pm0000644000175000017500000000337113616635656015711 0ustar ferkiferkipackage Rex::SCM::Subversion; use strict; use warnings; our $VERSION = '1.8.1'; # VERSION use Cwd qw(getcwd); use Rex::Commands::Fs; use Rex::Helper::Run; use vars qw($CHECKOUT_COMMAND); BEGIN { my $version = qx{svn --version --quiet 2>/dev/null}; if ($version) { my @parts = split( /\./, $version ); if ( $parts[1] <= 5 ) { $CHECKOUT_COMMAND = "svn --non-interactive %s checkout %s %s"; } else { $CHECKOUT_COMMAND = "svn --non-interactive --trust-server-cert %s checkout %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 ) = @_; my $special_opts = ""; if ( exists $repo_info->{"username"} ) { $special_opts = " --username '" . $repo_info->{"username"} . "'"; } if ( exists $repo_info->{"password"} ) { $special_opts .= " --password '" . $repo_info->{"password"} . "'"; } my $checkout_cmd; if ( !is_dir($checkout_to) ) { $checkout_cmd = sprintf( $CHECKOUT_COMMAND, $special_opts, $repo_info->{"url"}, $checkout_to ); } elsif ( is_dir("$checkout_to/.svn") ) { $checkout_cmd = "svn up $checkout_to"; } else { Rex::Logger::info( "Error checking out repository.", "warn" ); die("Error checking out repository."); } Rex::Logger::debug("checkout_cmd: $checkout_cmd"); Rex::Logger::info( "Cloning " . $repo_info->{"url"} . " to " . ( $checkout_to ? $checkout_to : "." ) ); my $out = i_run "$checkout_cmd", fail_ok => 1; unless ( $? == 0 ) { Rex::Logger::info( "Error checking out repository.", "warn" ); Rex::Logger::info($out); die("Error checking out repository."); } } 1; Rex-1.8.1/lib/Rex/Hardware.pm0000644000175000017500000000360013616635656014660 0ustar ferkiferki# # (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.8.1'; # 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; Rex-1.8.1/lib/Rex/Profiler.pm0000644000175000017500000000323013616635656014704 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Profiler; use strict; use warnings; our $VERSION = '1.8.1'; # 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; Rex-1.8.1/lib/Rex/TaskList.pm0000644000175000017500000000264313616635656014667 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::TaskList; use strict; use warnings; our $VERSION = '1.8.1'; # 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; } Rex-1.8.1/lib/Rex/Resource.pm0000644000175000017500000000611113616635656014712 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Resource; use strict; use warnings; our $VERSION = '1.8.1'; # 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; Rex-1.8.1/lib/Rex/Commands.pm0000644000175000017500000012023213616635656014665 0ustar ferkiferki # (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.8.1'; # 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 Carp; 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 critic ProhibitNoStrict 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'; ## no critic ProhibitNoStrict 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 ) = @_; my $task = Rex::TaskList->create()->get_task($task_name); if ( !$task ) { croak("No task named '$task_name' found."); } if ( exists $option{on} ) { if ( exists $option{params} ) { $task->run( $option{on}, params => $option{params} ); } else { $task->run( $option{on} ); } } else { 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"); $self =~ s/^Rex:://g; $self =~ s/::/:/g; 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'; ## no critic ProhibitNoStrict *{"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 (&) { ## no critic ProhibitSubroutinePrototypes 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(&) { ## no critic ProhibitSubroutinePrototypes return $_[0]; } 1; Rex-1.8.1/lib/Rex/Exporter.pm0000644000175000017500000000153413616635656014737 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Exporter; use strict; use warnings; our $VERSION = '1.8.1'; # VERSION use Data::Dumper; our @EXPORT; no strict 'refs'; ## no critic ProhibitNoStrict 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; Rex-1.8.1/lib/Rex/Template.pm0000644000175000017500000001115113616635656014676 0ustar ferkiferki# # (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.8.1'; # 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'; ## no critic ProhibitNoStrict *{ $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 critic ProhibitNoStrict no strict 'vars'; ## no critic ProhibitNoStrict 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; Rex-1.8.1/lib/Rex/Constants.pm0000644000175000017500000000101513616635656015075 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=3 sw=3 tw=0: # vim: set expandtab: package Rex::Constants; use strict; use warnings; our $VERSION = '1.8.1'; # 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; Rex-1.8.1/lib/Rex/Inventory.pm0000644000175000017500000001262013616635656015122 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Inventory; use strict; use warnings; our $VERSION = '1.8.1'; # 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; Rex-1.8.1/lib/Rex/CMDB/0000755000175000017500000000000013616635656013273 5ustar ferkiferkiRex-1.8.1/lib/Rex/CMDB/Base.pm0000644000175000017500000000064613616635656014511 0ustar ferkiferki# # (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.8.1'; # 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; Rex-1.8.1/lib/Rex/CMDB/YAML.pm0000644000175000017500000000562613616635656014404 0ustar ferkiferki# # (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.8.1'; # 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 ); } } return $all; } 1; Rex-1.8.1/lib/Rex/Cron/0000755000175000017500000000000013616635656013467 5ustar ferkiferkiRex-1.8.1/lib/Rex/Cron/Base.pm0000644000175000017500000001305113616635656014677 0ustar ferkiferki# # (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.8.1'; # 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; Rex-1.8.1/lib/Rex/Cron/Linux.pm0000644000175000017500000000060713616635656015127 0ustar ferkiferki# # (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.8.1'; # 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; Rex-1.8.1/lib/Rex/Cron/SunOS.pm0000644000175000017500000000122613616635656015035 0ustar ferkiferki# # (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.8.1'; # 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; Rex-1.8.1/lib/Rex/Cron/FreeBSD.pm0000644000175000017500000000141313616635656015236 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Cron::FreeBSD; use strict; use warnings; our $VERSION = '1.8.1'; # VERSION use Rex::Cron::Base; use base qw(Rex::Cron::Base); use Rex::Helper::Run; use Rex::Helper::Path; 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 ) = @_; $user = undef if $user eq $self->_whoami; my $tmp_file = get_tmp_file; my $command = '( crontab -l'; $command .= " -u $user" if defined $user; $command .= " >$tmp_file ) >& /dev/null ; cat $tmp_file ; rm $tmp_file"; my @lines = i_run $command; $self->parse_cron(@lines); } 1; Rex-1.8.1/lib/Rex/Fork/0000755000175000017500000000000013616635656013467 5ustar ferkiferkiRex-1.8.1/lib/Rex/Fork/Task.pm0000644000175000017500000000130613616635656014727 0ustar ferkiferki# # (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.8.1'; # 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; Rex-1.8.1/lib/Rex/Fork/Manager.pm0000644000175000017500000000310013616635656015371 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Fork::Manager; use strict; use warnings; our $VERSION = '1.8.1'; # VERSION use Rex::Fork::Task; use Time::HiRes qw(sleep); sub new { my $that = shift; my $proto = ref($that) || $that; my $self = {@_}; bless( $self, $proto ); $self->{'forks'} = []; $self->{'running'} = 0; return $self; } sub add { my ( $self, $coderef ) = @_; my $f = Rex::Fork::Task->new( coderef => $coderef ); push( @{ $self->{'forks'} }, $f ); $f->start; ++$self->{'running'}; if ( $self->{'running'} >= $self->{'max'} ) { $self->wait_for_one; } } sub start { my ($self) = @_; my @threads = @{ $self->{'forks'} }; for ( my $i = 0 ; $i < scalar(@threads) ; ++$i ) { $threads[$i]->start; ++$self->{'running'}; if ( $self->{'running'} >= $self->{'max'} ) { $self->wait_for_one; } } $self->wait_for_all; } sub wait_for_one { my ($self) = @_; $self->wait_for; } sub wait_for_all { my ($self) = @_; $self->wait_for(1); } sub wait_for { my ( $self, $all ) = @_; do { for ( my $i = 0 ; $i < scalar( @{ $self->{'forks'} } ) ; $i++ ) { my $thr = $self->{'forks'}->[$i]; unless ( $thr->{'running'} ) { next; } my $kid; $kid = $thr->wait; if ( $kid == -1 ) { $thr = undef; $thr->{running} = 0; --$self->{'running'}; return 1 unless $all; } sleep Rex::Config->get_waitpid_blocking_sleep_time; } } until $self->{'running'} == 0; } 1; Rex-1.8.1/lib/Rex/Helper/0000755000175000017500000000000013616635656014005 5ustar ferkiferkiRex-1.8.1/lib/Rex/Helper/IP.pm0000644000175000017500000000156213616635656014657 0ustar ferkiferki# # (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.8.1'; # 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; Rex-1.8.1/lib/Rex/Helper/DBI.pm0000644000175000017500000000136213616635656014743 0ustar ferkiferki# # (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.8.1'; # 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; Rex-1.8.1/lib/Rex/Helper/URI.pm0000644000175000017500000000056213616635656015005 0ustar ferkiferki# # (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.8.1'; # 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; Rex-1.8.1/lib/Rex/Helper/INI.pm0000644000175000017500000000370413616635656014766 0ustar ferkiferki# # (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.8.1'; # 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; Rex-1.8.1/lib/Rex/Helper/Run.pm0000644000175000017500000000675013616635656015117 0ustar ferkiferki# # (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.8.1'; # 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; Rex-1.8.1/lib/Rex/Helper/Hash.pm0000644000175000017500000000223413616635656015227 0ustar ferkiferki# # (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.8.1'; # 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; Rex-1.8.1/lib/Rex/Helper/Misc.pm0000644000175000017500000000055613616635656015244 0ustar ferkiferki# # (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.8.1'; # VERSION sub get_random { my $count = shift; my @chars = @_; my $ret = ""; for ( 1 .. $count ) { $ret .= $chars[ int( rand( scalar(@chars) - 1 ) ) ]; } return $ret; } 1; Rex-1.8.1/lib/Rex/Helper/Path.pm0000644000175000017500000001223613616635656015243 0ustar ferkiferki# # (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.8.1'; # 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; use Rex::Interface::Fs; @EXPORT = qw(get_file_path get_tmp_file resolv_path parse_path resolve_symlink); 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; } sub resolve_symlink { my $path = shift; my $fs = Rex::Interface::Fs::create(); my $resolution; if ( $fs->is_symlink($path) ) { while ( my $link = $fs->readlink($path) ) { if ( $link !~ m/^\// ) { $path = dirname($path) . "/" . $link; } else { $path = $link; } $link = $fs->readlink($link); } $resolution = $path; } return $resolution; } 1; Rex-1.8.1/lib/Rex/Helper/SSH2.pm0000644000175000017500000000653113616635656015067 0ustar ferkiferki# # (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.8.1'; # VERSION require Exporter; use Data::Dumper; require Rex::Commands; use Time::HiRes qw(sleep); 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."); sleep 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; Rex-1.8.1/lib/Rex/Helper/Array.pm0000644000175000017500000000115613616635656015424 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Helper::Array; use strict; use warnings; our $VERSION = '1.8.1'; # VERSION require Exporter; use base qw(Exporter); use vars qw(@EXPORT); @EXPORT = qw(array_uniq in_array); sub array_uniq { my (@array) = @_; my %all = (); @all{@array} = 1; return keys %all; } sub in_array { my ( $needle, @haystack ) = @_; my ($ret) = grep { if ( ref $needle eq "RegExp" && $_ =~ $needle ) { return $_; } elsif ( $_ eq $needle ) { return $_; } } @haystack; return $ret; } 1; Rex-1.8.1/lib/Rex/Helper/Encode.pm0000644000175000017500000000246613616635656015550 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Helper::Encode; use strict; use warnings; our $VERSION = '1.8.1'; # VERSION require Exporter; use base qw(Exporter); use vars qw(@EXPORT); @EXPORT = qw(func_to_json); my %escapes; for ( 0 .. 255 ) { $escapes{ chr($_) } = sprintf( "%%%02X", $_ ); } sub url_encode { my ($txt) = @_; $txt =~ s/([^A-Za-z0-9_])/$escapes{$1}/g; return $txt; } sub url_decode { my ($txt) = @_; $txt =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg; return $txt; } sub func_to_json { return q| sub to_json { my ($ref) = @_; my $s = ""; if(ref $ref eq "ARRAY") { $s .= "["; for my $itm (@{ $ref }) { if(substr($s, -1) ne "[") { $s .= ","; } $s .= to_json($itm); } return $s . "]"; } elsif(ref $ref eq "HASH") { $s .= "{"; for my $key (keys %{ $ref }) { if(substr($s, -1) ne "{") { $s .= ","; } $s .= "\"$key\": " . to_json($ref->{$key}); } return $s . "}"; } else { if($ref =~ /^0\d+/) { return "\"$ref\""; } elsif($ref =~ /^\d+$/) { return $ref; } else { $ref =~ s/'/\\\'/g; return "\"$ref\""; } } } |; } 1; Rex-1.8.1/lib/Rex/Helper/System.pm0000644000175000017500000000217213616635656015631 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Helper::System; use strict; use warnings; our $VERSION = '1.8.1'; # VERSION use Rex::Helper::Hash; sub info { return ( eval { my %merge1 = (); my %merge2 = Rex::Hardware->get(qw/ All /); my %template_vars = ( %merge1, %merge2 ); for my $info_key (qw(Network Host Kernel Memory Swap)) { my $flatten_info = {}; if ( $info_key eq "Memory" ) { hash_flatten( $merge2{$info_key}, $flatten_info, "_", "memory" ); } elsif ( $info_key eq "Swap" ) { hash_flatten( $merge2{$info_key}, $flatten_info, "_", "swap" ); } elsif ( $info_key eq "Network" ) { hash_flatten( $merge2{$info_key}->{"networkconfiguration"}, $flatten_info, "_" ); } else { hash_flatten( $merge2{$info_key}, $flatten_info, "_" ); } for my $key ( keys %{$flatten_info} ) { $template_vars{$key} = $flatten_info->{$key}; } } return %template_vars; } ); } 1; Rex-1.8.1/lib/Rex/Helper/UserAgent.pm0000644000175000017500000000143613616635656016244 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Helper::UserAgent; use strict; use warnings; our $VERSION = '1.8.1'; # VERSION use base 'LWP::UserAgent'; sub new { my $that = shift; my $proto = ref($that) || $that; my $self = $proto->SUPER::new(@_); bless( $self, $proto ); return $self; } sub get_basic_credentials { my ($self) = @_; if ( exists $self->{__user__} && $self->{__password__} ) { return $self->{__user__}, $self->{__password__}; } return; } sub get { my ( $self, $url, %option ) = @_; if ( exists $option{user} ) { $self->{__user__} = $option{user}; } if ( exists $option{password} ) { $self->{__password__} = $option{password}; } return $self->SUPER::get($url); } 1; Rex-1.8.1/lib/Rex/Helper/File/0000755000175000017500000000000013616635656014664 5ustar ferkiferkiRex-1.8.1/lib/Rex/Helper/File/Spec.pm0000644000175000017500000000137713616635656016124 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Helper::File::Spec; use strict; use warnings; our $VERSION = '1.8.1'; # VERSION require File::Spec::Unix; require File::Spec::Win32; sub catfile { shift @_; _spec()->catfile(@_); } sub catdir { shift @_; _spec()->catdir(@_); } sub join { shift @_; _spec()->join(@_); } sub splitdir { shift @_; _spec()->splitdir(@_); } sub tmpdir { shift @_; _spec()->tmpdir(@_); } sub rootdir { shift @_; _spec()->rootdir(@_); } sub _spec { if ( Rex::is_ssh() ) { return "File::Spec::Unix"; } else { if ( $^O =~ m/^MSWin/ ) { return "File::Spec::Win32"; } else { return "File::Spec::Unix"; } } } 1; Rex-1.8.1/lib/Rex/Helper/File/Stat.pm0000644000175000017500000000150513616635656016136 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Helper::File::Stat; use strict; use warnings; our $VERSION = '1.8.1'; # VERSION require Rex::Helper::File::Stat::Unix; require Rex::Helper::File::Stat::Win32; sub S_ISDIR { shift; _fcntl()->S_ISDIR(@_); } sub S_ISREG { shift; _fcntl()->S_ISREG(@_); } sub S_ISLNK { shift; _fcntl()->S_ISLNK(@_); } sub S_ISBLK { shift; _fcntl()->S_ISBLK(@_); } sub S_ISCHR { shift; _fcntl()->S_ISCHR(@_); } sub S_ISFIFO { shift; _fcntl()->S_ISFIFO(@_); } sub S_ISSOCK { shift; _fcntl()->S_ISSOCK(@_); } sub S_IMODE { shift; _fcntl()->S_IMODE(@_); } sub _fcntl { if ( $^O =~ m/^MSWin/ ) { return "Rex::Helper::File::Stat::Win32"; } else { return "Rex::Helper::File::Stat::Unix"; } } 1; Rex-1.8.1/lib/Rex/Helper/File/Stat/0000755000175000017500000000000013616635656015577 5ustar ferkiferkiRex-1.8.1/lib/Rex/Helper/File/Stat/Unix.pm0000644000175000017500000000116013616635656017056 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Helper::File::Stat::Unix; use strict; use warnings; our $VERSION = '1.8.1'; # VERSION use Fcntl; use Rex::Helper::Run; sub S_ISDIR { shift; Fcntl::S_ISDIR(@_); } sub S_ISREG { shift; Fcntl::S_ISREG(@_); } sub S_ISLNK { shift; Fcntl::S_ISLNK(@_); } sub S_ISBLK { shift; Fcntl::S_ISBLK(@_); } sub S_ISCHR { shift; Fcntl::S_ISCHR(@_); } sub S_ISFIFO { shift; Fcntl::S_ISFIFO(@_); } sub S_ISSOCK { shift; Fcntl::S_ISSOCK(@_); } sub S_IMODE { shift; Fcntl::S_IMODE(@_); } 1; Rex-1.8.1/lib/Rex/Helper/File/Stat/Win32.pm0000644000175000017500000000212513616635656017037 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Helper::File::Stat::Win32; use strict; use warnings; our $VERSION = '1.8.1'; # VERSION use Fcntl; use Rex::Interface::Exec; sub S_ISDIR { shift; Fcntl::S_ISDIR(@_); } sub S_ISREG { shift; Fcntl::S_ISREG(@_); } sub S_ISLNK { shift; if ( Rex::is_ssh() ) { my $exec = Rex::Interface::Exec->create; $exec->exec("perl -le 'use Fcntl; exit Fcntl::S_ISLNK($_[0])'"); return $?; } else { Rex::Logger::info( "S_ISLNK not supported on your platform.", "warn" ); return 0; } } sub S_ISBLK { shift; Fcntl::S_ISBLK(@_); } sub S_ISCHR { shift; Fcntl::S_ISCHR(@_); } sub S_ISFIFO { shift; Fcntl::S_ISFIFO(@_); } sub S_ISSOCK { shift; if ( Rex::is_ssh() ) { my $exec = Rex::Interface::Exec->create; $exec->exec("perl -le 'use Fcntl; exit Fcntl::S_ISSOCK($_[0])'"); return $?; } else { Rex::Logger::info( "S_ISSOCK not supported on your platform.", "warn" ); return 0; } } sub S_IMODE { shift; Fcntl::S_IMODE(@_); } 1; Rex-1.8.1/lib/Rex/Helper/SSH2/0000755000175000017500000000000013616635656014524 5ustar ferkiferkiRex-1.8.1/lib/Rex/Helper/SSH2/Expect.pm0000644000175000017500000001003413616635656016310 0ustar ferkiferki# # (c) 2011 Jan Gehring # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: # =head1 NAME Rex::Helper::SSH2::Expect - An Expect like module for Net::SSH2 =head1 DESCRIPTION This is a module to have expect like features for Net::SSH2. This is the first version of this module. Please report bugs at GitHub L =head1 DEPENDENCIES =over 4 =item * L =back =head1 SYNOPSIS use Rex::Helper::SSH2::Expect; my $exp = Rex::Helper::SSH2::Expect->new($ssh2); $exp->spawn("passwd"); $exp->expect($timeout, [ qr/Enter new UNIX password:/ => sub { my ($exp, $line) = @_; $exp->send($new_password); } ], [ qr/Retype new UNIX password:/ => sub { my ($exp, $line) = @_; $exp->send($new_password); } ], [ qr/passwd: password updated successfully/ => sub { my ($exp, $line) = @_; $exp->hard_close; } ]); =head1 CLASS METHODS =cut package Rex::Helper::SSH2::Expect; use strict; use warnings; our $VERSION = '1.8.1'; # VERSION =head2 new($ssh2) Constructor: You need to parse an connected Net::SSH2 Object. =cut our $Log_Stdout = 1; sub new { my $that = shift; my $proto = ref($that) || $that; my $self = {}; bless( $self, $proto ); $self->{"__shell"} = $_[0]->channel(); $self->{"__shell"}->pty("vt100"); $self->{"__shell"}->shell; $self->{"__log_stdout"} = $Rex::Helper::SSH2::Expect::Log_Stdout; $self->{"__log_to"} = sub { }; return $self; } =head2 log_stdout(0|1) Log on STDOUT. =cut sub log_stdout { my ( $self, $log ) = @_; $self->{"__log_stdout"} = $log; } =head2 log_file($file) Log everything to a file. $file can be a filename, a filehandle or a subRef. =cut sub log_file { my ( $self, $file ) = @_; $self->{"__log_to"} = $file; } sub shell { my ($self) = @_; return $self->{"__shell"}; } =head2 spawn($command, @parameters) Spawn $command with @parameters as parameters. =cut sub spawn { my ( $self, $command, @parameters ) = @_; my $cmd = "$command " . join( " ", @parameters ); $self->shell->write("$cmd\n"); } =head2 soft_close() Currently only an alias to hard_close(); =cut sub soft_close { my ($self) = @_; $self->hard_close; } =head2 hard_close(); Stops the execution of the process. =cut sub hard_close { my ($self) = @_; die; } =head2 expect($timeout, @match_patters) This method controls the execution of your process. =cut sub expect { my ( $self, $timeout, @match_patterns ) = @_; eval { local $SIG{'ALRM'} = sub { die; }; alarm $timeout; my $line = ""; while (1) { my $buf; $self->shell->read( $buf, 1 ); # log to stdout if wanted print $buf if $self->{"__log_stdout"}; $self->_log($buf); if ( $self->_check_patterns( $line, @match_patterns ) ) { $line = ""; alarm $timeout; next; } $line .= $buf; } }; } =head2 send($string) Send a string to the running command. =cut sub send { my ( $self, $str ) = @_; $self->shell->write($str); } sub _check_patterns { my ( $self, $line, @match_patterns ) = @_; for my $pattern (@match_patterns) { if ( $line =~ $pattern->[0] ) { my $code = $pattern->[1]; &$code( $self, $line ); return 1; } } } sub _log { my ( $self, $str ) = @_; my $log_to = $self->{"__log_to"}; if ( ref($log_to) eq "CODE" ) { &$log_to($str); } elsif ( ref($log_to) eq "GLOB" ) { print $log_to $str; } else { # log to a file open( my $fh, ">>", $log_to ) or die($!); print $fh $str; close($fh); } } 1; Rex-1.8.1/lib/Rex/Helper/Rexfile/0000755000175000017500000000000013616635656015403 5ustar ferkiferkiRex-1.8.1/lib/Rex/Helper/Rexfile/ParamLookup.pm0000644000175000017500000000506013616635656020174 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=3 sw=3 tw=0: # vim: set expandtab: package Rex::Helper::Rexfile::ParamLookup; use strict; use warnings; our $VERSION = '1.8.1'; # VERSION use Devel::Caller; use Data::Dumper; require Rex::Exporter; require Rex::Commands; use base qw(Rex::Exporter); use vars qw (@EXPORT); @EXPORT = qw(param_lookup); sub param_lookup { my ( $key, $default ) = @_; my $ret; my ($caller_pkg) = caller(0); my @args = Devel::Caller::caller_args(1); if ( ref $args[0] eq "HASH" ) { if ( exists $args[0]->{$key} ) { $ret = $args[0]->{$key}; } } if ( !$ret ) { # check if cmdb is loaded my ($use_cmdb) = grep { m/CMDB\.pm/ } keys %INC; if ($use_cmdb) { # look inside cmdb my $cmdb_key = "${caller_pkg}::$key"; $ret = Rex::Commands::get( Rex::CMDB::cmdb($cmdb_key) ); if ( !$ret ) { # check in resource if ( Rex::Resource->is_inside_resource ) { $cmdb_key = Rex::Resource->get_current_resource->display_name . "::$key"; $ret = Rex::Commands::get( Rex::CMDB::cmdb($cmdb_key) ); } if ( !$ret ) { # check in task my $task = Rex::Commands::task(); if ($task) { my $task_name = $task->{name}; $task_name =~ s/:/::/; $cmdb_key = $task_name . "::$key"; $ret = Rex::Commands::get( Rex::CMDB::cmdb($cmdb_key) ); } } } if ( !$ret ) { # check in global namespace $ret = Rex::Commands::get( Rex::CMDB::cmdb($key) ); } } } if ( !$ret ) { $ret = $default; } if ( Rex::Resource->is_inside_resource ) { Rex::Resource->get_current_resource()->set_parameter( $key => $ret ); } if ( !Rex::Resource->is_inside_resource ) { Rex::Commands::task()->set_opt( $key => $ret ); } return $ret; } 1; =pod =head1 NAME Rex::Helper::Rexfile::ParamLookup - A command to manage task parameters. A command to manage task parameters. Additionally it register the parameters as template values. This module also looks inside a CMDB (if present) for a valid key. =head1 SYNOPSIS task "setup", sub { my $var = param_lookup "param_name", "default_value"; }; =head1 LOOKUP First I checks the task parameters for a valid parameter. If none is found and if a CMDB is used, it will look inside the cmdb. If your module is named "Rex::NTP" than it will first look if the key "Rex::NTP::param_name" exists. If it doesn't exists it checks for the key "param_name". =cut Rex-1.8.1/lib/Rex/Sudo/0000755000175000017500000000000013616635656013500 5ustar ferkiferkiRex-1.8.1/lib/Rex/Sudo/File.pm0000644000175000017500000000421613616635656014720 0ustar ferkiferki# # (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.8.1'; # 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; Rex-1.8.1/lib/Rex/Test/0000755000175000017500000000000013616635656013505 5ustar ferkiferkiRex-1.8.1/lib/Rex/Test/Base.pm0000644000175000017500000001253413616635656014722 0ustar ferkiferki# # (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.8.1'; # 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(&) { ## no critic ProhibitSubroutinePrototypes 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; Rex-1.8.1/lib/Rex/Test/Base/0000755000175000017500000000000013616635656014357 5ustar ferkiferkiRex-1.8.1/lib/Rex/Test/Base/has_dir.pm0000644000175000017500000000101413616635656016322 0ustar ferkiferki# # (c) Ferenc Erki , adjust GmbH # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Test::Base::has_dir; use strict; use warnings; our $VERSION = '1.8.1'; # VERSION use Rex -base; use base qw(Rex::Test::Base); sub new { my $that = shift; my $proto = ref($that) || $that; my $self = {@_}; bless( $self, $proto ); my ( $pkg, $file ) = caller(0); return $self; } sub run_test { my ( $self, $path ) = @_; $self->ok( is_dir($path), "Found $path directory." ); } 1; Rex-1.8.1/lib/Rex/Test/Base/has_file.pm0000644000175000017500000000077513616635656016500 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Test::Base::has_file; use strict; use warnings; our $VERSION = '1.8.1'; # VERSION use Rex -base; use base qw(Rex::Test::Base); sub new { my $that = shift; my $proto = ref($that) || $that; my $self = {@_}; bless( $self, $proto ); my ( $pkg, $file ) = caller(0); return $self; } sub run_test { my ( $self, $file ) = @_; $self->ok( is_file($file), "Found $file file." ); } 1; Rex-1.8.1/lib/Rex/Test/Base/has_cron.pm0000644000175000017500000000230513616635656016511 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Test::Base::has_cron; use strict; use warnings; our $VERSION = '1.8.1'; # VERSION use Rex -minimal; use Rex::Commands::Cron; use base qw(Rex::Test::Base); sub new { my $that = shift; my $proto = ref($that) || $that; my $self = {@_}; bless( $self, $proto ); my ( $pkg, $file ) = caller(0); return $self; } sub run_test { my ( $self, $user, $key, $value, $count ) = @_; my @crons = cron list => $user; my @matched_crons = grep { $_->{$key} eq $value } @crons; if ($count) { $self->ok( scalar @matched_crons == $count, "Found $count cron(s) with $key = $value" ); } else { $self->ok( scalar @matched_crons > 0, "Found cron with $key = $value" ); } } sub run_not_test { my ( $self, $user, $key, $value, $count ) = @_; my @crons = cron list => $user; my @matched_crons = grep { $_->{$key} eq $value } @crons; if ($count) { $self->ok( scalar @matched_crons != $count, "Not found $count cron(s) with $key = $value" ); } else { $self->ok( scalar @matched_crons == 0, "Not found cron with $key = $value" ); } } 1; Rex-1.8.1/lib/Rex/Test/Base/has_stat.pm0000644000175000017500000000237713616635656016534 0ustar ferkiferki# # (c) Robert Abraham # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Test::Base::has_stat; use strict; use warnings; our $VERSION = '1.8.1'; # VERSION use Rex -base; use base qw(Rex::Test::Base); use Rex::Commands::Fs; use Rex::Commands::User; sub new { my $that = shift; my $proto = ref($that) || $that; my $self = {@_}; bless( $self, $proto ); my ( $pkg, $file ) = caller(0); return $self; } sub run_test { my ( $self, $path, $stats ) = @_; my %stat; eval { %stat = stat $path; }; if ($@) { $self->ok( 0, "has_stat: cannot stat $path." ); $self->diag($@); return; } if ( defined( $stats->{'owner'} ) ) { my $uid = get_uid( $stats->{'owner'} ); my $result = defined $uid ? $uid == $stat{'uid'} : 0; $self->ok( $result, "Owner of $path is $stats->{'owner'}" ); $self->diag("has_stat: get_uid failed for $stats->{'owner'}.") unless defined $uid; } if ( defined( $stats->{'group'} ) ) { my $gid = get_gid( $stats->{'group'} ); my $result = defined $gid ? $gid == $stat{'gid'} : 0; $self->ok( $result, "Group of $path is $stats->{'group'}" ); $self->diag("has_stat: get_gid failed for $stats->{'group'}.") unless defined $gid; } } 1; Rex-1.8.1/lib/Rex/Test/Base/has_output.pm0000644000175000017500000000113613616635656017111 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Test::Base::has_output; use strict; use warnings; our $VERSION = '1.8.1'; # VERSION use Rex -minimal; use Rex::Helper::Run; use base qw(Rex::Test::Base); sub new { my $that = shift; my $proto = ref($that) || $that; my $self = {@_}; bless( $self, $proto ); my ( $pkg, $file ) = caller(0); return $self; } sub run_test { my ( $self, $exec, $wanted_output ) = @_; my $output = i_exec $exec; $self->ok( $output eq $wanted_output, "Output of $exec is as expected." ); } 1; Rex-1.8.1/lib/Rex/Test/Base/has_content.pm0000644000175000017500000000121513616635656017221 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Test::Base::has_content; use strict; use warnings; our $VERSION = '1.8.1'; # VERSION use Rex -base; use base qw(Rex::Test::Base); sub new { my $that = shift; my $proto = ref($that) || $that; my $self = {@_}; bless( $self, $proto ); my ( $pkg, $file ) = caller(0); return $self; } sub run_test { my ( $self, $file, $test ) = @_; return $self->ok( 0, "has_content: $file not found" ) unless is_file($file); my $content = cat $file; $self->ok( ( $content =~ $test ) >= 1, "Content of $file contain $test." ); } 1; Rex-1.8.1/lib/Rex/Test/Base/has_package.pm0000644000175000017500000000147213616635656017147 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Test::Base::has_package; use strict; use warnings; our $VERSION = '1.8.1'; # VERSION use Rex -base; use base qw(Rex::Test::Base); use Data::Dumper; sub new { my $that = shift; my $proto = ref($that) || $that; my $self = {@_}; bless( $self, $proto ); my ( $pkg, $file ) = caller(0); return $self; } sub run_test { my ( $self, $package, $version ) = @_; my $pkg = Rex::Pkg->get; if ( $pkg->is_installed( $package, { version => $version } ) ) { $self->ok( 1, "Found package $package" . ( $version ? " at version $version" : "" ) ); return 1; } else { $self->ok( 0, "Found package $package" . ( $version ? " at version $version" : "" ) ); return 0; } } 1; Rex-1.8.1/lib/Rex/Test/Base/has_cron_env.pm0000644000175000017500000000246113616635656017364 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Test::Base::has_cron_env; use strict; use warnings; our $VERSION = '1.8.1'; # VERSION use Rex -minimal; use Rex::Commands::Cron; use base qw(Rex::Test::Base); sub new { my $that = shift; my $proto = ref($that) || $that; my $self = {@_}; bless( $self, $proto ); my ( $pkg, $file ) = caller(0); return $self; } sub run_test { my ( $self, $user, $key, $value, $count ) = @_; my @envs = cron env => $user => "list"; my @matched_envs = grep { $_->{name} eq $key && $_->{value} eq $value } @envs; if ($count) { $self->ok( scalar @matched_envs == $count, "Found $count cron(s) env entries with $key = $value" ); } else { $self->ok( scalar @matched_envs > 0, "Found cron env entry with $key = $value" ); } } sub run_not_test { my ( $self, $user, $key, $value, $count ) = @_; my @envs = cron env => $user => "list"; my @matched_envs = grep { $_->{name} eq $key && $_->{value} eq $value } @envs; if ($count) { $self->ok( scalar @matched_envs != $count, "Not found $count cron(s) env entries with $key = $value" ); } else { $self->ok( scalar @matched_envs == 0, "Not found cron env entry with $key = $value" ); } } 1; Rex-1.8.1/lib/Rex/Test/Base/has_file_content.pm0000644000175000017500000000111613616635656020220 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Test::Base::has_file_content; use strict; use warnings; our $VERSION = '1.8.1'; # VERSION use Rex -base; use base qw(Rex::Test::Base); sub new { my $that = shift; my $proto = ref($that) || $that; my $self = {@_}; bless( $self, $proto ); my ( $pkg, $file ) = caller(0); return $self; } sub run_test { my ( $self, $file, $wanted_content ) = @_; my $content = cat $file; $self->ok( $content eq $wanted_content, "File $file has content: $content" ); } 1; Rex-1.8.1/lib/Rex/Test/Base/has_output_matching.pm0000644000175000017500000000114713616635656020765 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Test::Base::has_output_matching; use strict; use warnings; our $VERSION = '1.8.1'; # VERSION use Rex -minimal; use Rex::Helper::Run; use base qw(Rex::Test::Base); sub new { my $that = shift; my $proto = ref($that) || $that; my $self = {@_}; bless( $self, $proto ); my ( $pkg, $file ) = caller(0); return $self; } sub run_test { my ( $self, $exec, $wanted_output ) = @_; my $output = i_exec $exec; $self->like( $output, $wanted_output, "Output of $exec is as expected." ); } 1; Rex-1.8.1/lib/Rex/Test/Base/has_service_running.pm0000644000175000017500000000107513616635656020753 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Test::Base::has_service_running; use strict; use warnings; our $VERSION = '1.8.1'; # VERSION use Rex -base; use base qw(Rex::Test::Base); sub new { my $that = shift; my $proto = ref($that) || $that; my $self = {@_}; bless( $self, $proto ); my ( $pkg, $file ) = caller(0); return $self; } sub run_test { my ( $self, $service ) = @_; local $::QUIET = 1; $self->ok( service( $service, "status" ) == 1, "Service $service running." ); } 1; Rex-1.8.1/lib/Rex/Test/Base/has_service_stopped.pm0000644000175000017500000000107513616635656020751 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Test::Base::has_service_stopped; use strict; use warnings; our $VERSION = '1.8.1'; # VERSION use Rex -base; use base qw(Rex::Test::Base); sub new { my $that = shift; my $proto = ref($that) || $that; my $self = {@_}; bless( $self, $proto ); my ( $pkg, $file ) = caller(0); return $self; } sub run_test { my ( $self, $service ) = @_; local $::QUIET = 1; $self->ok( service( $service, "status" ) == 0, "Service $service stopped." ); } 1; Rex-1.8.1/lib/Rex/User/0000755000175000017500000000000013616635656013504 5ustar ferkiferkiRex-1.8.1/lib/Rex/User/Base.pm0000644000175000017500000000113213616635656014711 0ustar ferkiferki# # (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.8.1'; # 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; Rex-1.8.1/lib/Rex/User/Linux.pm0000644000175000017500000002551613616635656015152 0ustar ferkiferki# # (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.8.1'; # 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; Rex-1.8.1/lib/Rex/User/SunOS.pm0000644000175000017500000000753613616635656015064 0ustar ferkiferki# # (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.8.1'; # 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; Rex-1.8.1/lib/Rex/User/NetBSD.pm0000644000175000017500000001075713616635656015133 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::User::NetBSD; use strict; use warnings; our $VERSION = '1.8.1'; # VERSION use Rex::Logger; use Rex::Commands::MD5; use Rex::Helper::Run; use Rex::Commands::Fs; use Rex::User::Linux; use Rex::Interface::File; use Rex::Interface::Fs; use Rex::Interface::Exec; use Rex::Helper::Path; use base qw(Rex::User::Linux); 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; my $old_pw_md5 = md5("/etc/passwd"); 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} ) { Rex::Logger::debug("Changing password of $user."); $rnd_file = get_tmp_file; $fh = Rex::Interface::File->create; $fh->open( ">", $rnd_file ); $fh->write( "usermod -p \$(pwhash '" . $data->{password} . "') $user\nexit \$?\n" ); $fh->close; i_run "/bin/sh $rnd_file", fail_ok => 1; if ( $? != 0 ) { die("Error setting password for $user"); } Rex::Interface::Fs->create()->unlink($rnd_file); } if ( exists $data->{crypt_password} ) { Rex::Logger::debug("Setting encrypted password of $user"); $rnd_file = get_tmp_file; $fh = Rex::Interface::File->create; $fh->open( ">", $rnd_file ); $fh->write( "usermod -p '" . $data->{crypt_password} . "' $user\nexit \$?\n" ); $fh->close; i_run "/bin/sh $rnd_file", fail_ok => 1; if ( $? != 0 ) { die("Error setting password for $user"); } Rex::Interface::Fs->create()->unlink($rnd_file); } my $new_pw_md5 = md5("/etc/passwd"); if ( $new_pw_md5 eq $old_pw_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 %user_info = $self->get_user($user); my $cmd = "userdel"; if ( exists $data->{delete_home} ) { $cmd .= " -r"; } my $output = i_run $cmd . " " . $user, fail_ok => 1; if ( $? == 67 ) { Rex::Logger::info( "Cannot delete user $user (no such user)", "warn" ); } elsif ( $? != 0 ) { die("Error deleting user $user ($output)"); } if ( exists $data->{delete_home} && is_dir( $user_info{home} ) ) { Rex::Logger::debug( "userdel doesn't delete home directory. removing it now by hand..."); rmdir $user_info{home}; } if ( $? != 0 ) { die( "Error removing " . $user_info{home} ); } } 1; Rex-1.8.1/lib/Rex/User/FreeBSD.pm0000644000175000017500000001521513616635656015260 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::User::FreeBSD; use strict; use warnings; our $VERSION = '1.8.1'; # VERSION use Rex::Logger; use Rex::Commands::MD5; use Rex::Helper::Run; use Rex::Helper::Encode; use Rex::Commands::Fs; use Rex::Commands::File; use Rex::Interface::File; use Rex::Interface::Fs; use Rex::Interface::Exec; use Rex::User::Linux; use Rex::Helper::Path; use JSON::MaybeXS; use base qw(Rex::User::Linux); 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; my $old_pw_md5 = md5("/etc/passwd"); 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 = "pw useradd "; } else { Rex::Logger::debug("User $user already exists. Updating..."); $cmd = "pw usermod "; } if ( $data->{"uid"} ) { $cmd .= " -u " . $data->{"uid"}; } if ( $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 ( $data->{"comment"} ) { $cmd .= " -c \"" . $data->{"comment"} . "\" "; } if ( $data->{"expire"} ) { $cmd .= " -e " . $data->{"expire"}; } if ( $data->{"groups"} ) { my @groups = @{ $data->{groups} }; $cmd .= " -g " . $groups[0]; $cmd .= " -G " . join( ",", @groups ); } my $rnd_file = get_tmp_file; my $fh = Rex::Interface::File->create; $fh->open( ">", $rnd_file ); $fh->write("$cmd -n $user\nexit \$?\n"); $fh->close; i_run "/bin/sh $rnd_file", fail_ok => 1; my $retval = $?; Rex::Interface::Fs->create()->unlink($rnd_file); if ( $retval == 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} ) { Rex::Logger::debug("Changing password of $user."); $rnd_file = get_tmp_file; $fh = Rex::Interface::File->create; $fh->open( ">", $rnd_file ); $fh->write( "echo '" . $data->{password} . "' | pw usermod $user -h 0\nexit \$?\n" ); $fh->close; i_run "/bin/sh $rnd_file", fail_ok => 1; my $pw_retval = $?; Rex::Interface::Fs->create()->unlink($rnd_file); if ( $pw_retval != 0 ) { die("Error setting password for $user"); } } my $new_pw_md5 = md5("/etc/passwd"); if ( $new_pw_md5 eq $old_pw_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 = "pw userdel"; if ( exists $data->{delete_home} ) { $cmd .= " -r "; } my $output = i_run $cmd . " -n " . $user, fail_ok => 1; if ( $? == 67 ) { 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 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"); } Rex::Interface::Fs->create()->unlink($rnd_file); 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 create_group { my ( $self, $group, $data ) = @_; my $cmd; if ( !defined $self->get_gid($group) ) { Rex::Logger::debug("Creating new group $group"); $cmd = "pw groupadd "; if ( exists $data->{gid} ) { $cmd .= " -g " . $data->{gid}; } i_run $cmd . " -n " . $group, fail_ok => 1; if ( $? != 0 ) { die("Error creating/modifying group $group"); } } else { Rex::Logger::debug("Group $group already exists. Updating..."); # updating with pw groupmod doesn't work good in freebsd 10 # so we directly edit the /etc/group file #$cmd = "pw groupmod "; if ( exists $data->{gid} ) { eval { my @content = split( /\n/, cat("/etc/group") ); my $gid = $data->{gid}; for (@content) { s/^$group:([^:]+):(\d+):/$group:$1:$gid:/; } my $fh = file_write("/etc/group"); $fh->write( join( "\n", @content ) ); $fh->close; 1; } or do { die("Error creating/modifying group $group"); }; } } 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 $rnd_file = get_tmp_file; my $fh = Rex::Interface::File->create; my $script = q| unlink $0; print to_json([ getgrnam($ARGV[0]) ]); |; $fh->open( ">", $rnd_file ); $fh->write($script); $fh->write( func_to_json() ); $fh->close; my $data_str = i_run "perl $rnd_file $group", fail_ok => 1; if ( $? != 0 ) { die("Error getting group information"); } Rex::Interface::Fs->create()->unlink($rnd_file); my $data = decode_json($data_str); return ( name => $data->[0], password => $data->[1], gid => $data->[2], members => $data->[3], ); } sub rm_group { my ( $self, $group ) = @_; i_run "pw groupdel $group", fail_ok => 1; if ( $? != 0 ) { die("Error deleting group $group"); } } 1; Rex-1.8.1/lib/Rex/User/OpenBSD.pm0000644000175000017500000001561713616635656015306 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::User::OpenBSD; use strict; use warnings; our $VERSION = '1.8.1'; # VERSION use Rex::Logger; 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::User::Linux; use Rex::Helper::Path; use JSON::MaybeXS; use base qw(Rex::User::Linux); 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 $old_pw_md5 = md5("/etc/passwd"); my $uid = $self->get_uid($user); my %user_info = $self->get_user($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 ( defined $user_info{uid} ) { if ( exists $data->{uid} ) { # On OpenBSD, "usermod -u n login" fails when the user login # has already n as userid. So skip it from the command arg # when the uid is already correct. $cmd .= " -u " . $data->{uid} unless $data->{uid} == $user_info{uid}; } } else { # the user does not exist yet. $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->{login_class} ) { $cmd .= " -L '" . $data->{login_class} . "'"; } 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} ) { Rex::Logger::debug("Changing password of $user."); $rnd_file = get_tmp_file; $fh = Rex::Interface::File->create; $fh->open( ">", $rnd_file ); $fh->write( "usermod -p \$(encrypt -b 6 '" . $data->{password} . "') $user\nexit \$?\n" ); $fh->close; i_run "/bin/sh $rnd_file", fail_ok => 1; if ( $? != 0 ) { die("Error setting password for $user"); } Rex::Interface::Fs->create()->unlink($rnd_file); } if ( exists $data->{crypt_password} ) { Rex::Logger::debug("Setting encrypted password of $user"); $rnd_file = get_tmp_file; $fh = Rex::Interface::File->create; $fh->open( ">", $rnd_file ); $fh->write( "usermod -p '" . $data->{crypt_password} . "' $user\nexit \$?\n" ); $fh->close; i_run "/bin/sh $rnd_file", fail_ok => 1; if ( $? != 0 ) { die("Error setting password for $user"); } Rex::Interface::Fs->create()->unlink($rnd_file); } my $new_pw_md5 = md5("/etc/passwd"); if ( $new_pw_md5 eq $old_pw_md5 ) { return { changed => 0, ret => $self->get_uid($user), }; } else { return { changed => 1, ret => $self->get_uid($user), }, ; } } 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], pwchange => $data->[4], class => $data->[5], comment => $data->[6], home => $data->[7], shell => $data->[8], expire => $data->[9], ); } sub lock_password { my ( $self, $user ) = @_; # Is the password already locked? my $result = i_run "getent passwd $user", fail_ok => 1; if ( $result !~ /^$user.*$/ ) { die "Unexpected result from getent: $result"; } elsif ( $result =~ /^$user.*-$/ ) { # Already locked return { changed => 0 }; } else { my $ret = i_run "usermod -Z $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 "getent passwd $user", fail_ok => 1; if ( $result !~ /^$user.*$/ ) { die "Unexpected result from getent: $result"; } elsif ( $result !~ /^$user.*-$/ ) { # Already unlocked return { changed => 0 }; } else { my $ret = i_run "usermod -U $user", sub { @_ }, fail_ok => 1; if ( $? != 0 ) { die("Error unlocking account $user: $ret"); } return { changed => 1, ret => $ret, }; } } sub rm_user { my ( $self, $user, $data ) = @_; Rex::Logger::debug("Removing user $user"); my %user_info = $self->get_user($user); my $cmd = "userdel"; if ( exists $data->{delete_home} ) { $cmd .= " -r"; } my $output = i_run $cmd . " " . $user, fail_ok => 1; if ( $? == 67 ) { Rex::Logger::info( "Cannot delete user $user (no such user)", "warn" ); } elsif ( $? != 0 ) { die("Error deleting user $user ($output)"); } if ( exists $data->{delete_home} && is_dir( $user_info{home} ) ) { Rex::Logger::debug( "userdel doesn't delete home directory. removing it now by hand..."); rmdir $user_info{home}; } if ( $? != 0 ) { die( "Error removing " . $user_info{home} ); } } 1; Rex-1.8.1/lib/Rex/User/OpenWrt.pm0000644000175000017500000000317113616635656015442 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::User::OpenWrt; use strict; use warnings; our $VERSION = '1.8.1'; # VERSION use Rex::Logger; require Rex::Commands; use Rex::Helper::Run; use Rex::Commands::Fs; use Rex::Interface::File; use Rex::Interface::Fs; use Rex::Interface::Exec; use Rex::Helper::Path; use Rex::User::Linux; use base qw(Rex::User::Linux); sub new { my $that = shift; my $proto = ref($that) || $that; my $self = $proto->SUPER::new(@_); bless( $self, $proto ); return $self; } sub get_user { my ( $self, $user ) = @_; Rex::Logger::debug("Getting information for $user"); my $o_data = i_run "perl -e 'print join(\";\", getpwnam(\"$user\"))'"; chomp $o_data; my @data = split( /;/, $o_data ); return ( name => $data[0], password => $data[1], uid => $data[2], gid => $data[3], comment => $data[6], home => $data[7], shell => $data[8], ); } sub user_groups { my ( $self, $user ) = @_; Rex::Logger::debug("Getting group membership of $user"); my $data_str = i_run "/usr/bin/id -Gn $user", fail_ok => 1; if ( $? != 0 ) { die("Error getting group list"); } my $wantarray = wantarray(); if ( defined $wantarray && !$wantarray ) { # arrayref return [ split( / /, $data_str ) ]; } return split( / /, $data_str ); } sub user_list { my $self = shift; Rex::Logger::debug("Getting user list"); my $data_str = i_run "cut -d':' -f1 /etc/passwd", fail_ok => 1; if ( $? != 0 ) { die("Error getting user list"); } return split( /\n/, $data_str ); } 1; Rex-1.8.1/lib/Rex/Cloud/0000755000175000017500000000000013616635656013634 5ustar ferkiferkiRex-1.8.1/lib/Rex/Cloud/Base.pm0000644000175000017500000000322013616635656015041 0ustar ferkiferki# # (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.8.1'; # 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; Rex-1.8.1/lib/Rex/Cloud/Amazon.pm0000644000175000017500000003135613616635656015427 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: # # Some of the code is based on Net::Amazon::EC2 # package Rex::Cloud::Amazon; use strict; use warnings; our $VERSION = '1.8.1'; # VERSION use Rex::Logger; use Rex::Cloud::Base; use AWS::Signature4; use HTTP::Request::Common; use Digest::HMAC_SHA1; use base qw(Rex::Cloud::Base); use LWP::UserAgent; use XML::Simple; use Carp; BEGIN { use Rex::Require; HTTP::Date->use(qw(time2isoz)); MIME::Base64->use(qw(encode_base64 decode_base64)); } use Data::Dumper; sub new { my $that = shift; my $proto = ref($that) || $that; my $self = {@_}; bless( $self, $proto ); #$self->{"__version"} = "2009-11-30"; $self->{"__version"} = "2011-05-15"; $self->{"__signature_version"} = 1; $self->{"__endpoint"} = "us-east-1.ec2.amazonaws.com"; Rex::Logger::debug( "Creating new Amazon Object, with endpoint: " . $self->{"__endpoint"} ); Rex::Logger::debug( "Using API Version: " . $self->{"__version"} ); return $self; } sub signer { my ($self) = @_; return AWS::Signature4->new( -access_key => $self->{__access_key}, -secret_key => $self->{__secret_access_key} ); } sub set_auth { my ( $self, $access_key, $secret_access_key ) = @_; $self->{"__access_key"} = $access_key; $self->{"__secret_access_key"} = $secret_access_key; } sub set_endpoint { my ( $self, $endpoint ) = @_; Rex::Logger::debug("Setting new endpoint to $endpoint"); $self->{'__endpoint'} = $endpoint; } sub timestamp { my $t = time2isoz(); chop($t); $t .= ".000Z"; $t =~ s/\s+/T/g; return $t; } sub run_instance { my ( $self, %data ) = @_; Rex::Logger::debug("Trying to start a new Amazon instance with data:"); Rex::Logger::debug( " $_ -> " . ( $data{$_} ? $data{$_} : "undef" ) ) for keys %data; my $security_groups; if ( ref( $data{security_group} ) eq "ARRAY" ) { $security_groups = $data{security_group}; } elsif ( exists $data{security_groups} ) { $security_groups = $data{security_groups}; } else { $security_groups = $data{security_group}; } my %security_group = (); if ( ref($security_groups) eq "ARRAY" ) { my $i = 0; for my $sg ( @{$security_groups} ) { $security_group{"SecurityGroup.$i"} = $sg; $i++; } } elsif ( !exists $data{options}->{SubnetId} ) { $security_group{SecurityGroup} = $security_groups || "default"; } my %more_options = %{ $data{options} || {} }; my $xml = $self->_request( "RunInstances", ImageId => $data{"image_id"}, MinCount => 1, MaxCount => 1, KeyName => $data{"key"}, InstanceType => $data{"type"} || "m1.small", "Placement.AvailabilityZone" => $data{"zone"} || "", %security_group, %more_options, ); my $ref = $self->_xml($xml); if ( exists $data{"name"} ) { $self->add_tag( id => $ref->{"instancesSet"}->{"item"}->{"instanceId"}, name => "Name", value => $data{"name"} ); } my ($info) = grep { $_->{"id"} eq $ref->{"instancesSet"}->{"item"}->{"instanceId"} } $self->list_instances(); while ( $info->{"state"} ne "running" ) { Rex::Logger::debug("Waiting for instance to be created..."); ($info) = grep { $_->{"id"} eq $ref->{"instancesSet"}->{"item"}->{"instanceId"} } $self->list_instances(); sleep 1; } if ( exists $data{"volume"} ) { $self->attach_volume( volume_id => $data{"volume"}, instance_id => $ref->{"instancesSet"}->{"item"}->{"instanceId"}, name => "/dev/sdh", # default for new instances ); } return $info; } sub attach_volume { my ( $self, %data ) = @_; Rex::Logger::debug("Trying to attach a new volume"); $self->_request( "AttachVolume", VolumeId => $data{"volume_id"}, InstanceId => $data{"instance_id"}, Device => $data{"name"} || "/dev/sdh" ); } sub detach_volume { my ( $self, %data ) = @_; Rex::Logger::debug("Trying to detach a volume"); $self->_request( "DetachVolume", VolumeId => $data{"volume_id"}, ); } sub delete_volume { my ( $self, %data ) = @_; Rex::Logger::debug("Trying to delete a volume"); $self->_request( "DeleteVolume", VolumeId => $data{"volume_id"}, ); } sub terminate_instance { my ( $self, %data ) = @_; Rex::Logger::debug("Trying to terminate an instance"); $self->_request( "TerminateInstances", "InstanceId.1" => $data{"instance_id"} ); } sub start_instance { my ( $self, %data ) = @_; Rex::Logger::debug("Trying to start an instance"); $self->_request( "StartInstances", "InstanceId.1" => $data{instance_id} ); my ($info) = grep { $_->{"id"} eq $data{"instance_id"} } $self->list_instances(); while ( $info->{"state"} ne "running" ) { Rex::Logger::debug("Waiting for instance to be started..."); ($info) = grep { $_->{"id"} eq $data{"instance_id"} } $self->list_instances(); sleep 5; } } sub stop_instance { my ( $self, %data ) = @_; Rex::Logger::debug("Trying to stop an instance"); $self->_request( "StopInstances", "InstanceId.1" => $data{instance_id} ); my ($info) = grep { $_->{"id"} eq $data{"instance_id"} } $self->list_instances(); while ( $info->{"state"} ne "stopped" ) { Rex::Logger::debug("Waiting for instance to be stopped..."); ($info) = grep { $_->{"id"} eq $data{"instance_id"} } $self->list_instances(); sleep 5; } } sub add_tag { my ( $self, %data ) = @_; Rex::Logger::debug( "Adding a new tag: " . $data{id} . " -> " . $data{name} . " -> " . $data{value} ); $self->_request( "CreateTags", "ResourceId.1" => $data{"id"}, "Tag.1.Key" => $data{"name"}, "Tag.1.Value" => $data{"value"} ); } sub create_volume { my ( $self, %data ) = @_; Rex::Logger::debug("Creating a new volume"); my $xml = $self->_request( "CreateVolume", "Size" => $data{"size"} || 1, "AvailabilityZone" => $data{"zone"}, ); my $ref = $self->_xml($xml); return $ref->{"volumeId"}; my ($info) = grep { $_->{"id"} eq $ref->{"volumeId"} } $self->list_volumes(); while ( $info->{"status"} ne "available" ) { Rex::Logger::debug("Waiting for volume to become ready..."); ($info) = grep { $_->{"id"} eq $ref->{"volumeId"} } $self->list_volumes(); sleep 1; } } sub list_volumes { my ($self) = @_; my $xml = $self->_request("DescribeVolumes"); my $ref = $self->_xml($xml); return unless ($ref); return unless ( exists $ref->{"volumeSet"}->{"item"} ); if ( ref( $ref->{"volumeSet"}->{"item"} ) eq "HASH" ) { $ref->{"volumeSet"}->{"item"} = [ $ref->{"volumeSet"}->{"item"} ]; } my @volumes; for my $vol ( @{ $ref->{"volumeSet"}->{"item"} } ) { push( @volumes, { id => $vol->{"volumeId"}, status => $vol->{"status"}, zone => $vol->{"availabilityZone"}, size => $vol->{"size"}, attached_to => $vol->{"attachmentSet"}->{"item"}->{"instanceId"}, } ); } return @volumes; } sub _make_instance_map { my ( $self, $instance_set ) = @_; return ( ip => $_[1]->{"ipAddress"}, id => $_[1]->{"instanceId"}, image_id => $_[1]->{"imageId"}, architecture => $_[1]->{"architecture"}, type => $_[1]->{"instanceType"}, dns_name => $_[1]->{"dnsName"}, state => $_[1]->{"instanceState"}->{"name"}, launch_time => $_[1]->{"launchTime"}, ( name => exists( $instance_set->{"tagSet"}->{"item"}->{"value"} ) ? $instance_set->{"tagSet"}->{"item"}->{"value"} : $instance_set->{"tagSet"}->{"item"}->{"Name"}->{"value"} ), private_ip => $_[1]->{"privateIpAddress"}, ( security_group => ref $_[1]->{"groupSet"}->{"item"} eq 'ARRAY' ? join ',', map { $_->{groupName} } @{ $_[1]->{"groupSet"}->{"item"} } : $_[1]->{"groupSet"}->{"item"}->{"groupName"} ), ( security_groups => ref $_[1]->{"groupSet"}->{"item"} eq 'ARRAY' ? [ map { $_->{groupName} } @{ $_[1]->{"groupSet"}->{"item"} } ] : [ $_[1]->{"groupSet"}->{"item"}->{"groupName"} ] ), ( tags => { map { if ( ref $instance_set->{"tagSet"}->{"item"}->{$_} eq 'HASH' ) { $_ => $instance_set->{"tagSet"}->{"item"}->{$_}->{value}; } else { $instance_set->{"tagSet"}->{"item"}->{key} => $instance_set->{"tagSet"}->{"item"}->{value}; } } keys %{ $instance_set->{"tagSet"}->{"item"} } } ), ); } sub list_instances { my ($self) = @_; my @ret; my $xml = $self->_request("DescribeInstances"); my $ref = $self->_xml($xml); return unless ($ref); return unless ( exists $ref->{"reservationSet"} ); return unless ( exists $ref->{"reservationSet"}->{"item"} ); if ( ref $ref->{"reservationSet"}->{"item"} eq "HASH" ) { # if only one instance is returned, turn it to an array $ref->{"reservationSet"}->{"item"} = [ $ref->{"reservationSet"}->{"item"} ]; } for my $instance_set ( @{ $ref->{"reservationSet"}->{"item"} } ) { # push(@ret, $instance_set); my $isi = $instance_set->{"instancesSet"}->{"item"}; if ( ref $isi eq 'HASH' ) { push( @ret, { $self->_make_instance_map($isi) } ); } elsif ( ref $isi eq 'ARRAY' ) { for my $iset (@$isi) { push( @ret, { $self->_make_instance_map($iset) } ); } } } return @ret; } sub list_running_instances { my ($self) = @_; return grep { $_->{"state"} eq "running" } $self->list_instances(); } sub get_regions { my ($self) = @_; my $content = $self->_request("DescribeRegions"); my %items = ( $content =~ m/([^<]+)<\/regionName>\s+([^<]+)<\/regionEndpoint>/gsim ); return %items; } sub get_availability_zones { my ($self) = @_; my $xml = $self->_request("DescribeAvailabilityZones"); my $ref = $self->_xml($xml); my @zones; for my $item ( @{ $ref->{"availabilityZoneInfo"}->{"item"} } ) { push( @zones, { zone_name => $item->{"zoneName"}, region_name => $item->{"regionName"}, zone_state => $item->{"zoneState"}, } ); } return @zones; } sub _request { my ( $self, $action, %args ) = @_; my $ua = LWP::UserAgent->new; $ua->timeout(300); $ua->env_proxy; my %param = $self->_sign( $action, %args ); Rex::Logger::debug( "Sending request to: https://" . $self->{'__endpoint'} ); Rex::Logger::debug( " $_ -> " . $param{$_} ) for keys %param; my $req = POST( "https://" . $self->{__endpoint}, [%param] ); $self->signer->sign($req); #my $res = $ua->post( "https://" . $self->{'__endpoint'}, \%param ); my $res = $ua->request($req); if ( $res->code >= 500 ) { Rex::Logger::info( "Error on request", "warn" ); Rex::Logger::debug( $res->content ); return; } else { my $ret; eval { no warnings; $ret = $res->content; Rex::Logger::debug($ret); use warnings; }; return $ret; } } sub _sign { my ( $self, $action, %o_args ) = @_; my %args; for my $key ( keys %o_args ) { next unless $key; next unless $o_args{$key}; $args{$key} = $o_args{$key}; } $args{Action} = $action; $args{Version} = $self->{__version}; return %args; my %sign_hash = ( AWSAccessKeyId => $self->{"__access_key"}, Action => $action, Timestamp => $self->timestamp(), Version => $self->{"__version"}, SignatureVersion => $self->{"__signature_version"}, %args ); my $sign_this; foreach my $key ( sort { lc($a) cmp lc($b) } keys %sign_hash ) { $sign_this .= $key . $sign_hash{$key}; } Rex::Logger::debug("Signed: $sign_this"); my $encoded = $self->_hash($sign_this); my %params = ( Action => $action, SignatureVersion => $self->{"__signature_version"}, AWSAccessKeyId => $self->{"__access_key"}, Timestamp => $self->timestamp(), Version => $self->{"__version"}, Signature => $encoded, %args ); return %params; } sub _hash { my ( $self, $query_string ) = @_; my $hashed = Digest::HMAC_SHA1->new( $self->{"__secret_access_key"} ); $hashed->add($query_string); return encode_base64( $hashed->digest, "" ); } sub _xml { my ( $self, $xml ) = @_; my $x = XML::Simple->new; my $res = $x->XMLin($xml); if ( defined $res->{"Errors"} ) { if ( ref( $res->{"Errors"} ) ne "ARRAY" ) { $res->{"Errors"} = [ $res->{"Errors"} ]; } my @error_msg = (); for my $error ( @{ $res->{"Errors"} } ) { push( @error_msg, $error->{"Error"}->{"Message"} . " (Code: " . $error->{"Error"}->{"Code"} . ")" ); } confess( join( "\n", @error_msg ) ); } return $res; } 1; Rex-1.8.1/lib/Rex/Cloud/Jiffybox.pm0000644000175000017500000001510713616635656015756 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Cloud::Jiffybox; use strict; use warnings; our $VERSION = '1.8.1'; # VERSION use Rex::Logger; BEGIN { use Rex::Require; LWP::UserAgent->use; HTTP::Request::Common->use; JSON::MaybeXS->use; } use Data::Dumper; use Rex::Cloud::Base; use base qw(Rex::Cloud::Base); sub new { my $that = shift; my $proto = ref($that) || $that; my $self = {@_}; bless( $self, $proto ); $self->{"__endpoint"} = "https://api.jiffybox.de/%s/v1.0/%s"; Rex::Logger::debug( "Creating new Jiffybox Object, with endpoint: " . $self->{"__endpoint"} ); return $self; } sub _auth_key { my ($self) = @_; return $self->{"__auth_key"}; } sub _do_request { my ( $self, $type, $action, @params ) = @_; my $url = sprintf( $self->{"__endpoint"}, $self->_auth_key, $action ); Rex::Logger::debug("Requesting $url"); my $ua = LWP::UserAgent->new; $ua->env_proxy; my ($res); if ( $type eq "GET" ) { $res = $ua->request( GET $url); } elsif ( $type eq "POST" ) { $res = $ua->request( POST $url, \@params ); } elsif ( $type eq "PUT" ) { my $req = POST $url, \@params; $req->method("PUT"); $res = $ua->request($req); } elsif ( $type eq "DELETE" ) { my $req = GET $url; $req->method("DELETE"); $res = $ua->request($req); } if ( $res->code >= 500 ) { Rex::Logger::info( $res->content ); die("Error on request."); } my $json = JSON::MaybeXS->new; my $data = $json->decode( $res->decoded_content ); Rex::Logger::debug( Dumper($data) ); unless ( $data->{"result"} ) { die( "Error talking to jiffybox: " . $data->{"messages"}->[0]->{"message"} ); } return $data; } sub _result_to_array { my ( $self, $data, $with_key ) = @_; my @ret = (); for my $key ( keys %{ $data->{"result"} } ) { if ($with_key) { $data->{"result"}->{$key}->{$with_key} = $key; } push( @ret, $data->{"result"}->{$key} ); } return @ret; } sub set_auth { my ( $self, $key ) = @_; $self->{"__auth_key"} = $key; } sub list_plans { my ($self) = @_; Rex::Logger::debug("Listing plans"); my $data = $self->_do_request( "GET", "plans" ); return $self->_result_to_array($data); } sub list_operating_systems { my ($self) = @_; Rex::Logger::debug("Listing operating systems"); my $data = $self->_do_request( "GET", "distributions" ); return $self->_result_to_array( $data, "os_id" ); } sub run_instance { my ( $self, %data ) = @_; my @jiffy_data; Rex::Logger::debug("Trying to start a new instance with data:"); Rex::Logger::debug( " $_ -> " . ( $data{$_} ? $data{$_} : "undef" ) ) for keys %data; push( @jiffy_data, "name" => $data{"name"}, "planid" => $data{"plan_id"}, "distribution" => $data{"image_id"} ); if ( exists $data{"password"} ) { push( @jiffy_data, "password" => $data{"password"} ); } if ( exists $data{"key"} ) { push( @jiffy_data, "use_sshkey" => $data{"key"} ); } if ( exists $data{"metadata"} ) { push( @jiffy_data, "metadata" => $data{"metadata"} ); } my $data; if ( exists $data{"clone_id"} ) { $data = $self->_do_request( "POST", "jiffyBoxes/" . $data{"clone_id"}, @jiffy_data ); } else { $data = $self->_do_request( "POST", "jiffyBoxes", @jiffy_data ); } my $instance_id = $data->{"result"}->{"id"}; my $sleep_countdown = 10; sleep $sleep_countdown; # wait 10 seconds ($data) = grep { $_->{"id"} eq $instance_id } $self->list_instances(); while ( $data->{"state"} ne "STOPPED" && $data->{"state"} ne "RUNNING" ) { Rex::Logger::debug("Waiting for instance to be created..."); ($data) = grep { $_->{"id"} eq $instance_id } $self->list_instances(); sleep $sleep_countdown; --$sleep_countdown; if ( $sleep_countdown <= 3 ) { $sleep_countdown = 7; } } $self->start_instance( instance_id => $instance_id ); ($data) = grep { $_->{"id"} eq $instance_id } $self->list_instances(); while ( $data->{"state"} ne "RUNNING" ) { Rex::Logger::debug("Waiting for instance to be started..."); ($data) = grep { $_->{"id"} eq $instance_id } $self->list_instances(); sleep $sleep_countdown; --$sleep_countdown; if ( $sleep_countdown <= 3 ) { $sleep_countdown = 7; } } return $data; } sub start_instance { my ( $self, %data ) = @_; my $instance_id = $data{"instance_id"}; Rex::Logger::debug("Starting instance $instance_id"); $self->_do_request( "PUT", "jiffyBoxes/$instance_id", status => "START" ); } sub terminate_instance { my ( $self, %data ) = @_; my $instance_id = $data{"instance_id"}; Rex::Logger::debug("Terminating instance $instance_id"); $self->stop_instance(%data); $self->_do_request( "DELETE", "jiffyBoxes/$instance_id" ); } sub stop_instance { my ( $self, %data ) = @_; my $instance_id = $data{"instance_id"}; Rex::Logger::debug("Stopping instance $instance_id"); $self->_do_request( "PUT", "jiffyBoxes/$instance_id", status => "SHUTDOWN" ); my $sleep_countdown = 10; sleep $sleep_countdown; # wait 10 seconds my ($data) = grep { $_->{"id"} eq $instance_id } $self->list_instances(); while ( $data->{"state"} ne "STOPPED" ) { Rex::Logger::debug("Waiting for instance to be stopped..."); ($data) = grep { $_->{"id"} eq $instance_id } $self->list_instances(); sleep $sleep_countdown; --$sleep_countdown; if ( $sleep_countdown <= 3 ) { $sleep_countdown = 7; } } return 1; } sub list_instances { my ($self) = @_; my @ret; my $data = $self->_do_request( "GET", "jiffyBoxes" ); for my $instance_id ( keys %{ $data->{"result"} } ) { my $state = $data->{"result"}->{$instance_id}->{"status"}; if ( $state eq "READY" ) { if ( $data->{"result"}->{$instance_id}->{"running"} ) { $state = "RUNNING"; } else { $state = "STOPPED"; } } push( @ret, { ip => $data->{"result"}->{$instance_id}->{"ips"}->{"public"}->[0], id => $instance_id, architecture => undef, type => $data->{"result"}->{$instance_id}->{"plan"}->{"name"}, dns_name => "j$instance_id.servers.jiffybox.net", state => $state, __state => $data->{"result"}->{$instance_id}->{"status"}, launch_time => undef, name => $data->{"result"}->{$instance_id}->{"name"}, } ); } return @ret; } sub list_running_instances { my ($self) = @_; return grep { $_->{"__state"} eq "READY" || $_->{"__state"} eq "UPDATING" } $self->list_instances(); } 1; Rex-1.8.1/lib/Rex/Cloud/OpenStack.pm0000644000175000017500000003050213616635656016061 0ustar ferkiferki# # (c) Ferenc Erki # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Cloud::OpenStack; use strict; use warnings; our $VERSION = '1.8.1'; # VERSION use Rex::Logger; use base 'Rex::Cloud::Base'; BEGIN { use Rex::Require; JSON::MaybeXS->use; HTTP::Request::Common->use(qw(:DEFAULT DELETE)); LWP::UserAgent->use; } use Data::Dumper; use Carp; use MIME::Base64 qw(decode_base64); use Digest::MD5 qw(md5_hex); use File::Basename; sub new { my $that = shift; my $proto = ref($that) || $that; my $self = {@_}; bless( $self, $proto ); $self->{_agent} = LWP::UserAgent->new; $self->{_agent}->env_proxy; return $self; } sub set_auth { my ( $self, %auth ) = @_; $self->{auth} = \%auth; } sub _request { my ( $self, $method, $url, %params ) = @_; my $response; Rex::Logger::debug("Sending request to $url"); Rex::Logger::debug(" $_ => $params{$_}") for keys %params; { no strict 'refs'; ## no critic ProhibitNoStrict $response = $self->{_agent}->request( $method->( $url, %params ) ); } Rex::Logger::debug( Dumper($response) ); if ( $response->is_error ) { Rex::Logger::info( 'Response indicates an error', 'warn' ); Rex::Logger::debug( $response->content ); } return decode_json( $response->content ) if $response->content; } sub _authenticate { my $self = shift; my $auth_data = { auth => { tenantName => $self->{auth}{tenant_name} || '', passwordCredentials => { username => $self->{auth}{username}, password => $self->{auth}{password}, } } }; my $content = $self->_request( POST => $self->{__endpoint} . '/tokens', content_type => 'application/json', content => encode_json($auth_data), ); $self->{auth}{tokenId} = $content->{access}{token}{id}; $self->{_agent}->default_header( 'X-Auth-Token' => $self->{auth}{tokenId} ); $self->{_catalog} = $content->{access}{serviceCatalog}; } sub get_nova_url { my $self = shift; $self->_authenticate unless $self->{auth}{tokenId}; my @nova_services = grep { $_->{type} eq 'compute' } @{ $self->{_catalog} }; return $nova_services[0]{endpoints}[0]{publicURL}; } sub get_cinder_url { my $self = shift; $self->_authenticate unless $self->{auth}{tokenId}; my @cinder_services = grep { $_->{type} eq 'volume' } @{ $self->{_catalog} }; return $cinder_services[0]{endpoints}[0]{publicURL}; } sub run_instance { my ( $self, %data ) = @_; my $nova_url = $self->get_nova_url; Rex::Logger::debug('Trying to start a new instance with data:'); Rex::Logger::debug(" $_ => $data{$_}") for keys %data; my $request_data = { server => { flavorRef => $data{plan_id}, imageRef => $data{image_id}, name => $data{name}, key_name => $data{key}, } }; my $content = $self->_request( POST => $nova_url . '/servers', content_type => 'application/json', content => encode_json($request_data), ); my $id = $content->{server}{id}; my $info; until ( ($info) = grep { $_->{id} eq $id } $self->list_running_instances ) { Rex::Logger::debug('Waiting for instance to be created...'); sleep 1; } if ( exists $data{volume} ) { $self->attach_volume( instance_id => $id, volume_id => $data{volume}, ); } if ( exists $data{floating_ip} ) { $self->associate_floating_ip( instance_id => $id, floating_ip => $data{floating_ip}, ); ($info) = grep { $_->{id} eq $id } $self->list_running_instances; } return $info; } sub terminate_instance { my ( $self, %data ) = @_; my $nova_url = $self->get_nova_url; Rex::Logger::debug("Terminating instance $data{instance_id}"); $self->_request( DELETE => $nova_url . '/servers/' . $data{instance_id} ); until ( !grep { $_->{id} eq $data{instance_id} } $self->list_running_instances ) { Rex::Logger::debug('Waiting for instance to be deleted...'); sleep 1; } } sub list_instances { my $self = shift; my %options = @_; $options{private_network} ||= "private"; $options{public_network} ||= "public"; $options{public_ip_type} ||= "floating"; $options{private_ip_type} ||= "fixed"; my $nova_url = $self->get_nova_url; my @instances; my $content = $self->_request( GET => $nova_url . '/servers/detail' ); for my $instance ( @{ $content->{servers} } ) { my %networks; for my $net ( keys %{ $instance->{addresses} } ) { for my $ip_conf ( @{ $instance->{addresses}->{$net} } ) { push @{ $networks{$net} }, { mac => $ip_conf->{'OS-EXT-IPS-MAC:mac_addr'}, ip => $ip_conf->{addr}, type => $ip_conf->{'OS-EXT-IPS:type'}, }; } } push @instances, { ip => ( [ map { $_->{"OS-EXT-IPS:type"} eq $options{public_ip_type} ? $_->{'addr'} : () } @{ $instance->{addresses}{ $options{public_network} } } ]->[0] || undef ), id => $instance->{id}, architecture => undef, type => $instance->{flavor}{id}, dns_name => undef, state => ( $instance->{status} eq 'ACTIVE' ? 'running' : 'stopped' ), __state => $instance->{status}, launch_time => $instance->{'OS-SRV-USG:launched_at'}, name => $instance->{name}, private_ip => ( [ map { $_->{"OS-EXT-IPS:type"} eq $options{private_ip_type} ? $_->{'addr'} : () } @{ $instance->{addresses}{ $options{private_network} } } ]->[0] || undef ), security_groups => ( join ',', map { $_->{name} } @{ $instance->{security_groups} } ), networks => \%networks, }; } return @instances; } sub list_running_instances { my $self = shift; return grep { $_->{state} eq 'running' } $self->list_instances; } sub stop_instance { my ( $self, %data ) = @_; my $nova_url = $self->get_nova_url; Rex::Logger::debug("Suspending instance $data{instance_id}"); $self->_request( POST => $nova_url . '/servers/' . $data{instance_id} . '/action', content_type => 'application/json', content => encode_json( { suspend => 'null' } ), ); while ( grep { $_->{id} eq $data{instance_id} } $self->list_running_instances ) { Rex::Logger::debug('Waiting for instance to be stopped...'); sleep 5; } } sub start_instance { my ( $self, %data ) = @_; my $nova_url = $self->get_nova_url; Rex::Logger::debug("Resuming instance $data{instance_id}"); $self->_request( POST => $nova_url . '/servers/' . $data{instance_id} . '/action', content_type => 'application/json', content => encode_json( { resume => 'null' } ), ); until ( grep { $_->{id} eq $data{instance_id} } $self->list_running_instances ) { Rex::Logger::debug('Waiting for instance to be started...'); sleep 5; } } sub list_flavors { my $self = shift; my $nova_url = $self->get_nova_url; Rex::Logger::debug('Listing flavors'); my $flavors = $self->_request( GET => $nova_url . '/flavors' ); confess "Error getting cloud flavors." if ( !exists $flavors->{flavors} ); return @{ $flavors->{flavors} }; } sub list_plans { return shift->list_flavors; } sub list_images { my $self = shift; my $nova_url = $self->get_nova_url; Rex::Logger::debug('Listing images'); my $images = $self->_request( GET => $nova_url . '/images' ); confess "Error getting cloud images." if ( !exists $images->{images} ); return @{ $images->{images} }; } sub create_volume { my ( $self, %data ) = @_; my $cinder_url = $self->get_cinder_url; Rex::Logger::debug('Creating a new volume'); my $request_data = { volume => { size => $data{size} || 1, availability_zone => $data{zone}, } }; my $content = $self->_request( POST => $cinder_url . '/volumes', content_type => 'application/json', content => encode_json($request_data), ); my $id = $content->{volume}{id}; until ( grep { $_->{id} eq $id and $_->{status} eq 'available' } $self->list_volumes ) { Rex::Logger::debug('Waiting for volume to become available...'); sleep 1; } return $id; } sub delete_volume { my ( $self, %data ) = @_; my $cinder_url = $self->get_cinder_url; Rex::Logger::debug('Trying to delete a volume'); $self->_request( DELETE => $cinder_url . '/volumes/' . $data{volume_id} ); until ( !grep { $_->{id} eq $data{volume_id} } $self->list_volumes ) { Rex::Logger::debug('Waiting for volume to be deleted...'); sleep 1; } } sub list_volumes { my $self = shift; my $cinder_url = $self->get_cinder_url; my @volumes; my $content = $self->_request( GET => $cinder_url . '/volumes' ); for my $volume ( @{ $content->{volumes} } ) { push @volumes, { id => $volume->{id}, status => $volume->{status}, zone => $volume->{availability_zone}, size => $volume->{size}, attached_to => join ',', map { $_->{server_id} } @{ $volume->{attachments} }, }; } return @volumes; } sub attach_volume { my ( $self, %data ) = @_; my $nova_url = $self->get_nova_url; Rex::Logger::debug('Trying to attach a new volume'); my $request_data = { volumeAttachment => { volumeId => $data{volume_id}, name => $data{name}, } }; $self->_request( POST => $nova_url . '/servers/' . $data{instance_id} . '/os-volume_attachments', content_type => 'application/json', content => encode_json($request_data), ); } sub detach_volume { my ( $self, %data ) = @_; my $nova_url = $self->get_nova_url; Rex::Logger::debug('Trying to detach a volume'); $self->_request( DELETE => $nova_url . '/servers/' . $data{instance_id} . '/os-volume_attachments/' . $data{volume_id} ); } sub get_floating_ip { my $self = shift; my $nova_url = $self->get_nova_url; # look for available floating IP my $floating_ips = $self->_request( GET => $nova_url . '/os-floating-ips' ); for my $floating_ip ( @{ $floating_ips->{floating_ips} } ) { return $floating_ip->{ip} if ( !$floating_ip->{instance_id} ); } confess "No floating IP available."; } sub associate_floating_ip { my ( $self, %data ) = @_; my $nova_url = $self->get_nova_url; # associate available floating IP to instance id my $request_data = { addFloatingIp => { address => $data{floating_ip} } }; Rex::Logger::debug('Associating floating IP to instance'); my $content = $self->_request( POST => $nova_url . '/servers/' . $data{instance_id} . '/action', content_type => 'application/json', content => encode_json($request_data), ); } sub list_keys { my $self = shift; my $nova_url = $self->get_nova_url; my $content = $self->_request( GET => $nova_url . '/os-keypairs' ); # remove ':' from fingerprint string foreach ( @{ $content->{keypairs} } ) { $_->{keypair}->{fingerprint} =~ s/://g; } return @{ $content->{keypairs} }; } sub upload_key { my ($self) = shift; my $nova_url = $self->get_nova_url; my $public_key = glob( Rex::Config->get_public_key ); my ( $public_key_name, undef, undef ) = fileparse( $public_key, qr/\.[^.]*/ ); my ( $type, $key, $comment ); # read public key my $fh; unless ( open( $fh, "<", glob($public_key) ) ) { Rex::Logger::debug("Cannot read $public_key"); return; } { local $/ = undef; ( $type, $key, $comment ) = split( /\s+/, <$fh> ); } close $fh; # calculate key fingerprint so we can compare them my $fingerprint = md5_hex( decode_base64($key) ); Rex::Logger::debug("Public key fingerprint is $fingerprint"); # upoad only new key my $online_key = pop @{ [ map { $_->{keypair}->{fingerprint} eq $fingerprint ? $_ : () } $self->list_keys() ] }; if ($online_key) { Rex::Logger::debug("Public key already uploaded"); return $online_key->{keypair}->{name}; } my $request_data = { keypair => { public_key => "$type $key", name => $public_key_name, } }; Rex::Logger::info('Uploading public key'); $self->_request( POST => $nova_url . '/os-keypairs', content_type => 'application/json', content => encode_json($request_data), ); return $public_key_name; } 1; Rex-1.8.1/lib/Rex/Shared/0000755000175000017500000000000013616635656013774 5ustar ferkiferkiRex-1.8.1/lib/Rex/Shared/Var.pm0000644000175000017500000000337213616635656015067 0ustar ferkiferki# # (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.8.1'; # 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; Rex-1.8.1/lib/Rex/Shared/Var/0000755000175000017500000000000013616635656014524 5ustar ferkiferkiRex-1.8.1/lib/Rex/Shared/Var/Hash.pm0000644000175000017500000000276713616635656015761 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Shared::Var::Hash; use strict; use warnings; use Rex::Shared::Var::Common qw/__lock __store __retrieve/; our $VERSION = '1.8.1'; # VERSION sub TIEHASH { my $self = { varname => $_[1], }; bless $self, $_[0]; } sub STORE { my $self = shift; my $key = shift; my $value = shift; return __lock sub { my $ref = __retrieve; my $ret = $ref->{ $self->{varname} }->{$key} = $value; __store $ref; return $ret; }; } sub FETCH { my $self = shift; my $key = shift; return __lock sub { my $ref = __retrieve; return $ref->{ $self->{varname} }->{$key}; }; } sub DELETE { my $self = shift; my $key = shift; __lock sub { my $ref = __retrieve; delete $ref->{ $self->{varname} }->{$key}; __store $ref; }; } sub CLEAR { my $self = shift; __lock sub { my $ref = __retrieve; $ref->{ $self->{varname} } = {}; __store $ref; }; } sub EXISTS { my $self = shift; my $key = shift; return __lock sub { my $ref = __retrieve; return exists $ref->{ $self->{varname} }->{$key}; }; } sub FIRSTKEY { my $self = shift; return __lock sub { my $ref = __retrieve; $self->{__iter__} = $ref->{ $self->{varname} }; my $temp = keys %{ $self->{__iter__} }; return scalar each %{ $self->{__iter__} }; }; } sub NEXTKEY { my $self = shift; my $prevkey = shift; return scalar each %{ $self->{__iter__} }; } 1; Rex-1.8.1/lib/Rex/Shared/Var/Array.pm0000644000175000017500000000512313616635656016141 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Shared::Var::Array; use strict; use warnings; use Rex::Shared::Var::Common qw/__lock __store __retrieve/; our $VERSION = '1.8.1'; # VERSION sub TIEARRAY { my $self = { varname => $_[1], }; bless $self, $_[0]; } sub STORE { my $self = shift; my $index = shift; my $value = shift; return __lock sub { my $ref = __retrieve; my $ret = $ref->{ $self->{varname} }->{data}->[$index] = $value; __store $ref; return $ret; }; } sub FETCH { my $self = shift; my $index = shift; return __lock sub { my $ref = __retrieve; my $ret = $ref->{ $self->{varname} }->{data}->[$index]; return $ret; }; } sub CLEAR { my $self = shift; __lock sub { my $ref = __retrieve; $ref->{ $self->{varname} } = { data => [] }; __store $ref; }; } sub DELETE { my $self = shift; my $index = shift; __lock sub { my $ref = __retrieve; delete $ref->{ $self->{varname} }->{data}->[$index]; __store $ref; }; } sub EXISTS { my $self = shift; my $index = shift; return __lock sub { my $ref = __retrieve; return exists $ref->{ $self->{varname} }->{data}->[$index]; }; } sub PUSH { my $self = shift; my @data = @_; __lock sub { my $ref = __retrieve; if ( !ref( $ref->{ $self->{varname} }->{data} ) eq "ARRAY" ) { $ref->{ $self->{varname} }->{data} = []; } push( @{ $ref->{ $self->{varname} }->{data} }, @data ); __store $ref; }; } sub UNSHIFT { my $self = shift; my @data = @_; __lock sub { my $ref = __retrieve; if ( !ref( $ref->{ $self->{varname} }->{data} ) eq "ARRAY" ) { $ref->{ $self->{varname} }->{data} = []; } unshift( @{ $ref->{ $self->{varname} }->{data} }, @data ); __store $ref; }; } sub SHIFT { my $self = shift; my @data = @_; my $result; __lock sub { my $ref = __retrieve; $result = shift( @{ $ref->{ $self->{varname} }->{data} } ); __store $ref; }; return $result; } sub POP { my $self = shift; my @data = @_; my $result; __lock sub { my $ref = __retrieve; $result = pop( @{ $ref->{ $self->{varname} }->{data} } ); __store $ref; }; return $result; } sub EXTEND { my $self = shift; my $count = shift; } sub STORESIZE { my $self = shift; my $newsize = shift; } sub FETCHSIZE { my $self = shift; return __lock sub { my $ref = __retrieve; if ( !exists $ref->{ $self->{varname} } ) { return 0; } return scalar( @{ $ref->{ $self->{varname} }->{data} } ); }; } 1; Rex-1.8.1/lib/Rex/Shared/Var/Scalar.pm0000644000175000017500000000122413616635656016266 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Shared::Var::Scalar; use strict; use warnings; use Rex::Shared::Var::Common qw/__lock __store __retrieve/; our $VERSION = '1.8.1'; # VERSION sub TIESCALAR { my $self = { varname => $_[1], }; bless $self, $_[0]; } sub STORE { my $self = shift; my $value = shift; return __lock sub { my $ref = __retrieve; my $ret = $ref->{ $self->{varname} } = $value; __store $ref; return $ret; }; } sub FETCH { my $self = shift; return __lock sub { my $ref = __retrieve; return $ref->{ $self->{varname} }; }; } 1; Rex-1.8.1/lib/Rex/Shared/Var/Common.pm0000644000175000017500000000242313616635656016313 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Shared::Var::Common; use strict; use warnings; require Exporter; use base qw/Exporter/; our @EXPORT_OK = qw/__lock __store __retrieve/; our $VERSION = '1.8.1'; # VERSION use Fcntl qw(:DEFAULT :flock); use Storable; use File::Spec; # $PARENT_PID gets set when Rex starts. This value remains the same after the # process forks. So $PARENT_PID is always the pid of the parent process. $$ # however is always the pid of the current process. our $PARENT_PID = $$; our $FILE = File::Spec->catfile( File::Spec->tmpdir(), "vars.db.$PARENT_PID" ); our $LOCK_FILE = File::Spec->catfile( File::Spec->tmpdir(), "vars.db.lock.$PARENT_PID" ); sub __lock { sysopen( my $dblock, $LOCK_FILE, O_RDONLY | O_CREAT ) or die($!); flock( $dblock, LOCK_EX ) or die($!); my $ret = $_[0]->(); close($dblock); return $ret; } sub __store { my $ref = shift; store( $ref, $FILE ); } sub __retrieve { return {} unless -f $FILE; return retrieve($FILE); } sub END { # return if we exiting a child process return unless $$ eq $PARENT_PID; # we are exiting the master process unlink $FILE if -f $FILE; unlink $LOCK_FILE if -f $LOCK_FILE; } 1; Rex-1.8.1/lib/Rex/Transaction.pm0000644000175000017500000000464413616635656015421 0ustar ferkiferki# # (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.8.1'; # 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(&) { ## no critic ProhibitSubroutinePrototypes 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(&) { ## no critic ProhibitSubroutinePrototypes my ($code) = @_; push( @ROLLBACKS, { code => $code, connection => Rex::get_current_connection() } ); } 1; Rex-1.8.1/lib/Rex/Args/0000755000175000017500000000000013616635656013462 5ustar ferkiferkiRex-1.8.1/lib/Rex/Args/Single.pm0000644000175000017500000000032413616635656015240 0ustar ferkiferki# # (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.8.1'; # VERSION sub get { return 1; } 1; Rex-1.8.1/lib/Rex/Args/String.pm0000644000175000017500000000042013616635656015262 0ustar ferkiferki# # (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.8.1'; # VERSION sub get { my ( $class, $name ) = @_; my $arg = shift @ARGV; return $arg; } 1; Rex-1.8.1/lib/Rex/Args/Integer.pm0000644000175000017500000000060613616635656015417 0ustar ferkiferki# # (c) Jan Gehring # # vim: set ts=2 sw=2 tw=0: # vim: set expandtab: package Rex::Args::Integer; use strict; use warnings; our $VERSION = '1.8.1'; # VERSION use Rex::Logger; sub get { my ( $class, $name ) = @_; my $arg = shift @ARGV; if ( $arg =~ m/^\d+$/ ) { return $arg; } Rex::Logger::debug("Invalid argument for $name"); return; } 1; Rex-1.8.1/lib/Rex/Commands/0000755000175000017500000000000013616635656014327 5ustar ferkiferkiRex-1.8.1/lib/Rex/Commands/DB.pm0000644000175000017500000001075213616635656015157 0ustar ferkiferki# # (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