#
# (c) Jan Gehring <jan.gehring@gmail.com>
#
# vim: set ts=2 sw=2 tw=0:
# vim: set expandtab:
=head1 NAME
Rex::Config - Handles the configuration.
=head1 DESCRIPTION
This module holds all configuration parameters for Rex.
With this module you can specify own configuration parameters for your modules.
=head1 EXPORTED METHODS
=cut
package Rex::Config;
use strict;
use warnings;
our $VERSION = '1.6.0'; # VERSION
use Rex::Helper::File::Spec;
use Rex::Logger;
use YAML;
use Data::Dumper;
use Rex::Require;
our (
$user, $password,
$port, $timeout,
$max_connect_fails, $password_auth,
$key_auth, $krb5_auth,
$public_key, $private_key,
$parallelism, $log_filename,
$log_facility, $sudo_password,
$ca_file, $ca_cert,
$ca_key, $path,
$no_path_cleanup, $set_param,
$environment, $connection_type,
$distributor, $template_function,
$SET_HANDLER, $HOME_CONFIG,
$HOME_CONFIG_YAML, %SSH_CONFIG_FOR,
$sudo_without_locales, $sudo_without_sh,
$no_tty, $source_global_profile,
$source_profile, %executor_for,
$allow_empty_groups, $use_server_auth,
$tmp_dir, %openssh_opt,
$use_cache, $cache_type,
$use_sleep_hack, $report_type,
$do_reporting, $say_format,
$exec_autodie, $verbose_run,
$disable_taskname_warning, $proxy_command,
$task_call_by_method, $fallback_auth,
$register_cmdb_template, $check_service_exists,
$set_no_append, $use_net_openssh_if_present,
$use_template_ng, $use_rex_kvm_agent,
$autodie, $task_chaining_cmdline_args,
);
# some defaults
%executor_for = (
perl => "perl",
python => "python",
ruby => "ruby",
bash => "bash",
);
sub set_autodie {
my $class = shift;
$autodie = shift;
}
sub get_autodie {
return $autodie;
}
sub set_use_net_openssh_if_present {
my $class = shift;
$use_net_openssh_if_present = shift;
}
sub get_use_net_openssh_if_present {
return $use_net_openssh_if_present;
}
sub set_use_rex_kvm_agent {
my $class = shift;
$use_rex_kvm_agent = shift;
}
sub get_use_rex_kvm_agent {
return $use_rex_kvm_agent;
}
sub set_use_template_ng {
my $class = shift;
$use_template_ng = shift;
}
sub get_use_template_ng {
return $use_template_ng;
}
sub set_set_no_append {
my $class = shift;
$set_no_append = shift;
}
sub get_set_no_append {
return $set_no_append;
}
sub set_check_service_exists {
my $class = shift;
$check_service_exists = shift;
}
sub get_check_service_exists {
return $check_service_exists;
}
sub set_register_cmdb_template {
my $class = shift;
$register_cmdb_template = shift;
}
sub get_register_cmdb_template {
return $register_cmdb_template;
}
sub set_fallback_auth {
my $class = shift;
$fallback_auth = [@_];
}
sub get_fallback_auth {
return $fallback_auth;
}
sub set_task_call_by_method {
my $class = shift;
$task_call_by_method = shift;
}
sub get_task_call_by_method {
return $task_call_by_method;
}
sub set_disable_taskname_warning {
my $class = shift;
$disable_taskname_warning = shift;
}
sub get_disable_taskname_warning {
return $disable_taskname_warning;
}
sub set_task_chaining_cmdline_args {
my $class = shift;
$task_chaining_cmdline_args = shift;
}
sub get_task_chaining_cmdline_args {
return $task_chaining_cmdline_args;
}
sub set_verbose_run {
my $class = shift;
$verbose_run = shift;
}
sub get_verbose_run {
return $verbose_run;
}
sub set_exec_autodie {
my $class = shift;
$exec_autodie = shift;
}
sub get_exec_autodie {
return $exec_autodie;
}
sub set_no_path_cleanup {
my $class = shift;
$no_path_cleanup = shift;
}
sub get_no_path_cleanup {
return $no_path_cleanup;
}
sub set_source_profile {
my $class = shift;
$source_profile = shift;
}
sub get_source_profile {
return $source_profile;
}
sub set_say_format {
my $class = shift;
$say_format = shift;
}
sub get_say_format {
return $say_format;
}
sub set_do_reporting {
my $class = shift;
$do_reporting = shift;
}
sub get_do_reporting {
return $do_reporting;
}
sub set_report_type {
my $class = shift;
$report_type = shift;
}
sub get_report_type {
if ( exists $ENV{REX_REPORT_TYPE} ) {
return $ENV{REX_REPORT_TYPE};
}
return $report_type;
}
sub set_sleep_hack {
my $class = shift;
$use_sleep_hack = shift;
}
sub get_sleep_hack {
return $use_sleep_hack;
}
sub set_cache_type {
my $class = shift;
$cache_type = shift;
}
sub get_cache_type {
if ( exists $ENV{REX_CACHE_TYPE} ) {
return $ENV{REX_CACHE_TYPE};
}
return $cache_type || "Base";
}
sub set_use_cache {
my $class = shift;
$use_cache = shift;
}
sub get_use_cache {
return $use_cache;
}
sub get_sudo_without_locales {
return $sudo_without_locales;
}
sub get_sudo_without_sh {
return $sudo_without_sh;
}
sub set_openssh_opt {
my ( $class, %opt ) = @_;
for my $key ( keys %opt ) {
if ( !defined $opt{$key} ) {
$openssh_opt{$key} = undef;
delete $openssh_opt{$key};
next;
}
$openssh_opt{$key} = $opt{$key};
}
}
sub get_openssh_opt {
return %openssh_opt;
}
sub set_sudo_without_locales {
my $class = shift;
$sudo_without_locales = shift;
}
sub set_sudo_without_sh {
my $class = shift;
$sudo_without_sh = shift;
}
sub set_executor_for {
my $class = shift;
my $for = shift;
my $e = shift;
$executor_for{$for} = $e;
}
sub get_executor_for {
my $class = shift;
my $e = shift;
return $executor_for{$e};
}
sub set_tmp_dir {
my ( $class, $dir ) = @_;
if ( $class eq "Rex::Config" ) {
$tmp_dir = $dir;
}
else {
$tmp_dir = $class;
}
}
sub get_tmp_dir {
my $cache = Rex::get_cache();
if ( my $cached_tmp = $cache->get("tmpdir") ) {
return $cached_tmp;
}
if ( !$tmp_dir ) {
if ( my $ssh = Rex::is_ssh() ) {
my $exec;
if ( Rex::is_sudo() ) {
if ( ref $ssh eq "Net::OpenSSH" ) {
$exec = Rex::Interface::Exec->create("OpenSSH");
}
else {
$exec = Rex::Interface::Exec->create("SSH");
}
}
else {
$exec = Rex::Interface::Exec->create;
}
my ($out) =
$exec->exec("perl -MFile::Spec -le 'print File::Spec->tmpdir'");
if ( $? == 0 && $out ) {
$out =~ s/[\r\n]//gms;
$cache->set( "tmpdir", $out );
return $out;
}
$cache->set( "tmpdir", "/tmp" );
return "/tmp";
}
else {
$cache->set( "tmpdir", Rex::Helper::File::Spec->tmpdir );
return Rex::Helper::File::Spec->tmpdir;
}
}
return $tmp_dir;
}
sub set_path {
my $class = shift;
$path = shift;
}
sub get_path {
if ( !$path ) {
return (
"/bin", "/sbin", "/usr/bin",
"/usr/sbin", "/usr/local/bin", "/usr/local/sbin",
"/usr/pkg/bin", "/usr/pkg/sbin"
);
}
return @{$path};
}
sub set_user {
my $class = shift;
$user = shift;
}
sub set_password {
my $class = shift;
$password = shift;
}
sub set_port {
my $class = shift;
$port = shift;
}
sub set_sudo_password {
my $class = shift;
$sudo_password = shift;
}
sub set_source_global_profile {
my $class = shift;
$source_global_profile = shift;
}
sub get_source_global_profile {
return $source_global_profile;
}
sub set_max_connect_fails {
my $class = shift;
$max_connect_fails = shift;
}
sub get_max_connect_fails {
my $class = shift;
my $param = {@_};
if ( exists $param->{server}
&& exists $SSH_CONFIG_FOR{ $param->{server} }
&& exists $SSH_CONFIG_FOR{ $param->{server} }->{connectionattempts} )
{
return $SSH_CONFIG_FOR{ $param->{server} }->{connectionattempts};
}
return $max_connect_fails || 3;
}
sub has_user {
my $class = shift;
return $user;
}
sub get_user {
my $class = shift;
if ( exists $ENV{REX_USER} ) {
return $ENV{REX_USER};
}
if ($user) {
return $user;
}
if ( $^O =~ m/^MSWin/ ) {
return getlogin;
}
else {
return getpwuid($<);
}
}
sub get_password {
my $class = shift;
if ( exists $ENV{REX_PASSWORD} ) {
return $ENV{REX_PASSWORD};
}
return $password;
}
sub get_port {
my $class = shift;
my $param = {@_};
if ( exists $param->{server}
&& exists $SSH_CONFIG_FOR{ $param->{server} }
&& exists $SSH_CONFIG_FOR{ $param->{server} }->{port} )
{
return $SSH_CONFIG_FOR{ $param->{server} }->{port};
}
return $port;
}
sub set_proxy_command {
my $class = shift;
$proxy_command = shift;
}
sub get_proxy_command {
my $class = shift;
my $param = {@_};
if ( exists $param->{server}
&& exists $SSH_CONFIG_FOR{ $param->{server} }
&& exists $SSH_CONFIG_FOR{ $param->{server} }->{proxycommand} )
{
return $SSH_CONFIG_FOR{ $param->{server} }->{proxycommand};
}
return $proxy_command;
}
sub get_sudo_password {
my $class = shift;
if ( exists $ENV{REX_SUDO_PASSWORD} ) {
return $ENV{REX_SUDO_PASSWORD};
}
if ($sudo_password) {
return $sudo_password;
}
elsif ( !defined $sudo_password ) {
return "";
}
else {
return $password;
}
return "";
}
sub set_timeout {
my $class = shift;
$timeout = shift;
}
sub get_timeout {
my $class = shift;
my $param = {@_};
if ( exists $param->{server}
&& exists $SSH_CONFIG_FOR{ $param->{server} }
&& exists $SSH_CONFIG_FOR{ $param->{server} }->{connecttimeout} )
{
return $SSH_CONFIG_FOR{ $param->{server} }->{connecttimeout};
}
return $timeout || 2;
}
sub set_password_auth {
my $class = shift;
$key_auth = 0;
$krb5_auth = 0;
$password_auth = shift || 1;
}
sub set_key_auth {
my $class = shift;
$password_auth = 0;
$krb5_auth = 0;
$key_auth = shift || 1;
}
sub set_krb5_auth {
my $class = shift;
$password_auth = 0;
$key_auth = 0;
$krb5_auth = shift || 1;
}
sub get_password_auth {
if ( exists $ENV{REX_AUTH_TYPE} && $ENV{REX_AUTH_TYPE} eq "pass" ) {
return 1;
}
return $password_auth;
}
sub get_key_auth {
if ( exists $ENV{REX_AUTH_TYPE} && $ENV{REX_AUTH_TYPE} eq "key" ) {
return 1;
}
return $key_auth;
}
sub get_krb5_auth {
if ( exists $ENV{REX_AUTH_TYPE} && $ENV{REX_AUTH_TYPE} eq "krb5" ) {
return 1;
}
return $krb5_auth;
}
sub set_public_key {
my $class = shift;
$public_key = shift;
}
sub has_public_key {
return get_public_key();
}
sub get_public_key {
if ( exists $ENV{REX_PUBLIC_KEY} ) { return $ENV{REX_PUBLIC_KEY}; }
if ($public_key) {
return $public_key;
}
return;
}
sub set_private_key {
my $class = shift;
$private_key = shift;
}
sub has_private_key {
return get_private_key();
}
sub get_private_key {
if ( exists $ENV{REX_PRIVATE_KEY} ) { return $ENV{REX_PRIVATE_KEY}; }
if ($private_key) {
return $private_key;
}
return;
}
sub set_parallelism {
my $class = shift;
$parallelism = $_[0];
}
sub get_parallelism {
my $class = shift;
return $parallelism || 1;
}
sub set_log_filename {
my $class = shift;
$log_filename = shift;
}
sub get_log_filename {
my $class = shift;
return $log_filename;
}
sub set_log_facility {
my $class = shift;
$log_facility = shift;
}
sub get_log_facility {
my $class = shift;
return $log_facility || "local0";
}
sub set_environment {
my ( $class, $env ) = @_;
$environment = $env;
}
sub get_environment {
return $environment || "";
}
sub get_ssh_config_username {
my $class = shift;
my $param = {@_};
if ( exists $param->{server}
&& exists $SSH_CONFIG_FOR{ $param->{server} }
&& exists $SSH_CONFIG_FOR{ $param->{server} }->{user} )
{
return $SSH_CONFIG_FOR{ $param->{server} }->{user};
}
return 0;
}
sub get_ssh_config_hostname {
my $class = shift;
my $param = {@_};
if ( exists $param->{server}
&& exists $SSH_CONFIG_FOR{ $param->{server} }
&& exists $SSH_CONFIG_FOR{ $param->{server} }->{hostname} )
{
if ( $SSH_CONFIG_FOR{ $param->{server} }->{hostname} =~ m/^\%h\.(.*)/ ) {
return $param->{server} . "." . $1;
}
else {
return $SSH_CONFIG_FOR{ $param->{server} }->{hostname};
}
}
return 0;
}
sub get_ssh_config_private_key {
my $class = shift;
my $param = {@_};
if ( exists $param->{server}
&& exists $SSH_CONFIG_FOR{ $param->{server} }
&& exists $SSH_CONFIG_FOR{ $param->{server} }->{identityfile} )
{
my $file = $SSH_CONFIG_FOR{ $param->{server} }->{identityfile};
my $home_dir = _home_dir();
$file =~ s/^~/$home_dir/;
return $file;
}
return 0;
}
sub get_ssh_config_public_key {
my $class = shift;
my $param = {@_};
if ( exists $param->{server}
&& exists $SSH_CONFIG_FOR{ $param->{server} }
&& exists $SSH_CONFIG_FOR{ $param->{server} }->{identityfile} )
{
my $file = $SSH_CONFIG_FOR{ $param->{server} }->{identityfile} . ".pub";
my $home_dir = _home_dir();
$file =~ s/^~/$home_dir/;
return $file;
}
return 0;
}
sub get_connection_type {
my $class = shift;
if ( $^O !~ m/^MSWin/ && !$connection_type && $use_net_openssh_if_present ) {
my $has_net_openssh = 0;
eval {
Net::OpenSSH->require;
Net::SFTP::Foreign->require;
$has_net_openssh = 1;
1;
};
if ($has_net_openssh) {
Rex::Logger::debug(
"Found Net::OpenSSH and Net::SFTP::Foreign - using it as default");
$connection_type = "OpenSSH";
return "OpenSSH";
}
}
if ( !$connection_type ) {
my $has_net_ssh2 = 0;
eval {
Net::SSH2->require;
$has_net_ssh2 = 1;
1;
};
if ($has_net_ssh2) {
$connection_type = "SSH";
return "SSH";
}
}
return $connection_type || "SSH";
}
sub get_ca {
my $class = shift;
return $ca_file || "";
}
sub get_ca_cert {
my $class = shift;
return $ca_cert || "";
}
sub get_ca_key {
my $class = shift;
return $ca_key || "";
}
sub set_distributor {
my $class = shift;
$distributor = shift;
}
sub get_distributor {
my $class = shift;
return $distributor || "Base";
}
sub set_template_function {
my $class = shift;
($template_function) = @_;
}
sub get_template_function {
if ( ref($template_function) eq "CODE" ) {
return sub {
my ( $content, $template_vars ) = @_;
$template_vars = {
Rex::Commands::task()->get_opts,
(
Rex::Resource->is_inside_resource
? %{ Rex::Resource->get_current_resource()->get_all_parameters }
: ()
),
%{ $template_vars || {} }
}
if ( Rex::Commands::task() );
return $template_function->( $content, $template_vars );
};
}
if ( Rex::Template::NG->is_loadable && get_use_template_ng() ) {
# new template engine
return sub {
my ( $content, $template_vars ) = @_;
$template_vars = {
Rex::Commands::task()->get_opts,
(
Rex::Resource->is_inside_resource
? %{ Rex::Resource->get_current_resource()->get_all_parameters }
: ()
),
%{ $template_vars || {} }
}
if ( Rex::Commands::task() );
Rex::Template::NG->require;
my $t = Rex::Template::NG->new;
return $t->parse( $content, %{$template_vars} );
};
}
return sub {
my ( $content, $template_vars ) = @_;
$template_vars = {
Rex::Commands::task()->get_opts,
(
Rex::Resource->is_inside_resource
? %{ Rex::Resource->get_current_resource()->get_all_parameters }
: ()
),
%{ $template_vars || {} }
}
if ( Rex::Commands::task() );
use Rex::Template;
my $template = Rex::Template->new;
return $template->parse( $content, $template_vars );
};
}
sub set_no_tty {
shift;
$no_tty = shift;
}
sub get_no_tty {
return $no_tty;
}
=head2 register_set_handler($handler_name, $code)
Register a handler that gets called by I<set>.
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<Rexfile> like this:
set foo => "bar";
=cut
sub register_set_handler {
my ( $class, $handler_name, $code ) = @_;
$SET_HANDLER->{$handler_name} = $code;
}
sub set {
my ( $class, $var, $data ) = @_;
if ( exists( $SET_HANDLER->{$var} ) ) {
shift;
shift;
return &{ $SET_HANDLER->{$var} }(@_);
}
if ($set_no_append) {
$set_param->{$var} = $data;
}
else {
if ( ref($data) eq "HASH" ) {
if ( !ref( $set_param->{$var} ) ) {
$set_param->{$var} = {};
}
for my $key ( keys %{$data} ) {
$set_param->{$var}->{$key} = $data->{$key};
}
}
elsif ( ref($data) eq "ARRAY" ) {
push( @{ $set_param->{$var} }, @{$data} );
}
else {
$set_param->{$var} = $data;
}
}
}
sub unset {
my ( $class, $var ) = @_;
$set_param->{$var} = undef;
delete $set_param->{$var};
}
sub get {
my ( $class, $var ) = @_;
$var or return;
if ( exists $set_param->{$var} ) {
return $set_param->{$var};
}
}
sub get_all {
my ($class) = @_;
return $set_param;
}
=head2 register_config_handler($topic, $code)
With this function it is possible to register own sections in the users config file ($HOME/.rex/config.yml).
Example:
Rex::Config->register_config_handler("foo", sub {
my ($param) = @_;
print "bar is: " . $param->{bar} . "\n";
});
And now the user can set this in his configuration file:
base:
user: theuser
password: thepassw0rd
foo:
bar: baz
=cut
sub register_config_handler {
my ( $class, $topic, $code ) = @_;
if ( !ref($HOME_CONFIG) ) { $HOME_CONFIG = {}; }
$HOME_CONFIG->{$topic} = $code;
if ( ref($HOME_CONFIG_YAML) && exists $HOME_CONFIG_YAML->{$topic} ) {
&$code( $HOME_CONFIG_YAML->{$topic} );
}
}
sub read_config_file {
my ($config_file) = @_;
$config_file ||= _home_dir() . "/.rex/config.yml";
if ( -f $config_file ) {
my $yaml = eval { local ( @ARGV, $/ ) = ($config_file); <>; };
eval { $HOME_CONFIG_YAML = Load($yaml); };
if ($@) {
print STDERR "Error loading $config_file\n";
print STDERR "$@\n";
exit 2;
}
for my $key ( keys %{$HOME_CONFIG} ) {
if ( exists $HOME_CONFIG_YAML->{$key} ) {
my $code = $HOME_CONFIG->{$key};
&$code( $HOME_CONFIG_YAML->{$key} );
}
}
}
}
sub read_ssh_config_file {
my ($config_file) = @_;
$config_file ||= _home_dir() . '/.ssh/config';
if ( -f $config_file ) {
my @lines = eval { local (@ARGV) = ($config_file); <>; };
%SSH_CONFIG_FOR = _parse_ssh_config(@lines);
}
}
sub _parse_ssh_config {
my (@lines) = @_;
my %ret = ();
my ( @host, $in_host );
for my $line (@lines) {
chomp $line;
next if ( $line =~ m/^\s*#/ );
next if ( $line =~ m/^\s*$/ );
if ( $line =~ m/^Host(?:\s*=\s*|\s+)(.*)$/i ) {
my $host_tmp = $1;
@host = split( /\s+/, $host_tmp );
$in_host = 1;
for my $h (@host) {
$ret{$h} = {};
}
next;
}
elsif ($in_host) {
#my ($key, $val) = ($line =~ m/^\s*([^\s]+)\s+=?\s*(.*)$/);
$line =~ s/^\s*//g;
my ( $key, $val_tmp ) = split( /[\s=]/, $line, 2 );
$val_tmp =~ s/^[\s=]+//g;
my $val = $val_tmp;
$val =~ s/^\s+//;
$val =~ s/\s+$//;
for my $h (@host) {
$ret{$h}->{ lc($key) } = $val;
}
}
}
return %ret;
}
sub set_allow_empty_groups {
my ( $class, $set ) = @_;
if ($set) {
$allow_empty_groups = 1;
}
else {
$allow_empty_groups = 0;
}
}
sub get_allow_empty_groups {
if ($allow_empty_groups) {
return 1;
}
return 0;
}
sub set_use_server_auth {
my ( $class, $set ) = @_;
if ($set) {
$use_server_auth = 1;
}
else {
$use_server_auth = 0;
}
}
sub get_use_server_auth {
if ($use_server_auth) {
return 1;
}
return 0;
}
sub import {
read_ssh_config_file();
read_config_file();
}
no strict 'refs';
__PACKAGE__->register_config_handler(
base => sub {
my ($param) = @_;
for my $key ( keys %{$param} ) {
if ( $key eq "keyauth" ) {
$key_auth = $param->{keyauth};
next;
}
if ( $key eq "passwordauth" ) {
$password_auth = $param->{passwordauth};
next;
}
if ( $key eq "passauth" ) {
$password_auth = $param->{passauth};
next;
}
$$key = $param->{$key};
}
}
);
my @set_handler =
qw/user password private_key public_key -keyauth -passwordauth -passauth
parallelism sudo_password connection ca cert key distributor
template_function port/;
for my $hndl (@set_handler) {
__PACKAGE__->register_set_handler(
$hndl => sub {
my ($val) = @_;
if ( $hndl =~ m/^\-/ ) {
$hndl = substr( $hndl, 1 );
}
if ( $hndl eq "keyauth" ) { $hndl = "key_auth"; $val = 1; }
if ( $hndl eq "passwordauth" || $hndl eq "passauth" ) {
$hndl = "password_auth";
$val = 1;
}
if ( $hndl eq "connection" ) { $hndl = "connection_type"; }
if ( $hndl eq "ca" ) { $hndl = "ca_file"; }
if ( $hndl eq "cert" ) { $hndl = "ca_cert"; }
if ( $hndl eq "key" ) { $hndl = "ca_key"; }
$$hndl = $val;
}
);
}
use strict;
sub _home_dir {
if ( $^O =~ m/^MSWin/ ) {
return $ENV{'USERPROFILE'};
}
return $ENV{'HOME'} || "";
}
1;