# # (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 =over 4 =cut package Rex::Config; { $Rex::Config::VERSION = '0.55.3'; } use strict; use warnings; use File::Spec; use Rex::Logger; use YAML; use Data::Dumper; 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, ); # some defaults %executor_for = ( perl => "perl", python => "python", ruby => "ruby", bash => "bash", ); 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_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_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, $key, $val ) = @_; if ( !defined $val ) { $openssh_opt{$key} = undef; delete $openssh_opt{$key}; return; } $openssh_opt{$key} = $val; } 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'"); chomp $out; $out =~ s/[\r\n]//gms; if ( $? == 0 && $out ) { $cache->set( "tmpdir", $out ); return $out; } $cache->set( "tmpdir", "/tmp" ); return "/tmp"; } else { $cache->set( "tmpdir", File::Spec->tmpdir ); return 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; } return getlogin || getpwuid($<) || "Kilroy"; } 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 undef; } 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 undef; } 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} ) { 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 $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 $template_function; } return sub { my ( $content, $template_vars ) = @_; 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; } =item 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 ) = @_; if ( exists $set_param->{$var} ) { return $set_param->{$var}; } } sub get_all { my ($class) = @_; return $set_param; } =item 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'} || ""; } =back =cut 1;