%PDF- %PDF-
Direktori : /opt/webdir/lib/ |
Current File : //opt/webdir/lib/Pool.pm |
# main class for manage in the ansible pool # package Pool; use strict; use warnings; use Moose; use File::Basename qw( dirname basename ); use File::Spec::Functions; use File::Path qw(remove_tree); use Data::Dumper; use Sys::Hostname; use bxNetwork; use bxNetworkNode; use bxDaemon; use Output; use bxInventory qw( get_from_yaml save_to_yaml); # main ansible config dir, all hosts and groups file definitions saved here has 'ansible_dir', is => 'ro', default => '/etc/ansible'; has 'bitrix_dir', is => 'ro', default => '/opt/webdir'; has 'ansible_conf', is => 'ro', lazy => 1, builder => 'set_ansible_conf'; has 'bitrix_conf', is => 'ro', lazy => 1, builder => 'set_bitrix_conf'; has 'debug', is => 'ro', lazy => 1, default => 0; has 'logfile', is => 'ro', default => '/opt/webdir/logs/pool_manage.debug'; has 'bitrix_type', is => 'ro', default => 'general'; sub esc_chars { my $str = shift; $str =~ s/([;<>\*\|`&\$!#\(\)\[\]\{\}:'"\\ ])/\\$1/g; return $str; } # set default config directories sub set_ansible_conf { my $self = shift; my $ansible_base = $self->ansible_dir; my $ansibleConfigOpt = { base => $ansible_base, main => catfile( $ansible_base, "ansible.cfg" ), hosts => catfile( $ansible_base, "hosts" ), sshkeys => catfile( $ansible_base, ".ssh" ), group_vars => catfile( $ansible_base, "group_vars" ), host_vars => catfile( $ansible_base, "host_vars" ), library => catfile( $ansible_base, "library" ), playbook => "/usr/bin/ansible-playbook", ansible => "/usr/bin/ansible", client_conf => catfile( $ansible_base, "ansible-roles" ), }; return $ansibleConfigOpt; } sub set_bitrix_conf { my $self = shift; my $bitrix_base = $self->bitrix_dir; my $bitrixConfigOpt = { base => $bitrix_base, logs => catfile( $bitrix_base, 'logs' ), aHostsTemplate => catfile( $bitrix_base, 'templates', 'ansible' ), aHostsGroups => [ 'hosts', 'mgmt', 'web', 'sphinx', 'memcached', 'mysql', 'push' ], aHostsDefault => 'hosts', aHostsPrefix => 'bitrix', }; return $bitrixConfigOpt; } sub generate_random { my $self = shift; my $len = shift; if ( not defined $len ) { $len = 10 } my @alphanum = ( 'A' .. 'Z', 'a' .. 'z', 0 .. 9 ); my $random = join( '', map( $alphanum[ rand($#alphanum) ], ( 1 .. $len ) ) ); return $random; } sub generate_host_id { my $self = shift; my $tm = time; my $random = $self->generate_random; return $tm . "_" . $random; } sub generate_host_password { my $self = shift; my $host = shift; return $host . "_" . $self->generate_random; } sub get_ansible_inventory { my ($self) = @_; my %ansible_inventory; my $ansible_conf = $self->ansible_conf; my $bitrix_conf = $self->bitrix_conf; my $message_p = ( caller(0) )[3]; my $message_t = __PACKAGE__; my $debug = $self->debug; my $logOutput = Output->new( error => 0, logfile => $self->logfile ); # get info from config file open( my $ch, '<', $ansible_conf->{'hosts'} ) or return Output->new( 'error' => '1', 'message' => "$message_p: Config file " . $ansible_conf->{'hosts'} . " does not exist" ); my ( $section_name, $is_pool_group ); my $server_cnt = 0; while (<$ch>) { chomp; next if (/^$/); next if (/^#/); s/^\s+//; s/\s+$//; # section found if (/^\[([^\]]+)\]/) { $section_name = $1; if ( $section_name =~ /^$bitrix_conf->{'aHostsPrefix'}\-(\S+)$/ ) { $section_name = $1; $is_pool_group = 1; } else { $is_pool_group = 0; } } next if ( $is_pool_group == 0 ); # option found if (/^([^\]\[\s]+)\s+(.+)$/) { my $server = $1; # vm1 my $server_opt = $2; # ansible_ssh_host=192.168.1.231 my $server_type = "ssh"; # default connection type my $server_ip = "127.0.0.1"; # default IP address # [bitrix-hosts] # vm03.ksh.bx ansible_ssh_host=172.17.10.103 # vm04.ksh.bx ansible_connection=local ansible_ssh_host=172.17.10.104 if ( $server_opt =~ /ansible_connection\s*=\s*(\S+)/ ) { $server_type = $1; } if ( $server_opt =~ /ansible_ssh_host\s*=\s*(\S+)/ ) { $server_ip = $1; } # get host connection settings from hosts group # and personal settings from host_vars if ( $section_name =~ /^$bitrix_conf->{'aHostsDefault'}$/ ) { $ansible_inventory{$server} = { 'ip' => $server_ip, 'connection' => $server_type, 'roles' => {} }; my $server_file = catfile( $ansible_conf->{'host_vars'}, $server ); my $get_host_vars = get_from_yaml($server_file); if ( $get_host_vars->is_error ) { #return $get_host_vars; $ansible_inventory{$server}->{host_vars} = {}; $ansible_inventory{$server}->{hostname} = $server; $ansible_inventory{$server}->{host_vars_file} = ""; next; } $server_cnt++; my $host_vars = $get_host_vars->data->[1]; $ansible_inventory{$server}->{host_vars} = $host_vars; $ansible_inventory{$server}->{host_vars_file} = $server_file; $ansible_inventory{$server}->{hostname} = ( $host_vars->{bx_host} ) ? $host_vars->{bx_host} : $server; $ansible_inventory{$server}->{host_id} = $host_vars->{host_id}; if ( ( defined $host_vars->{bx_host} ) && ( $host_vars->{bx_host} ne $server ) ) { $ansible_inventory{aliases}->{ $host_vars->{bx_host} } = $server; } } elsif ( $section_name =~ /^(mysql|memcached|sphinx|push|web|mgmt)$/ ) { my $group = $1; $ansible_inventory{$server}->{groups}->{$group} = 1; } } } close $ch; if ( $server_cnt == 0 ) { return Output->new( error => 2, message => "$message_p: Not found records in ansible config " . $ansible_conf->{'hosts'}, ); } foreach my $server ( keys %ansible_inventory ) { next if ( $server eq "aliases" ); #if (grep (/^mysql$/, @{$ansible_inventory->{$server}->{groups}} )){ if ( exists $ansible_inventory{$server}->{groups}->{mysql} ) { $ansible_inventory{$server}->{roles}->{mysql} = { type => ( $ansible_inventory{$server}->{host_vars} ->{mysql_replication_role} ) ? $ansible_inventory{$server}->{host_vars} ->{mysql_replication_role} : "slave", id => ( $ansible_inventory{$server}->{host_vars}->{mysql_serverid} ) ? $ansible_inventory{$server}->{host_vars}->{mysql_serverid} : 1, }; } #if (grep (/^memcached$/, @{$ansible_inventory->{$server}->{groups}} )){ if ( exists $ansible_inventory{$server}->{groups}->{memcached} ) { if ( $ansible_inventory{$server}->{host_vars}->{memcached_socket} ) { $ansible_inventory{$server}->{roles}->{memcached} ->{memcached_socket} = $ansible_inventory{$server}->{host_vars}->{memcached_socket}; } else { $ansible_inventory{$server}->{roles}->{memcached} ->{memcached_port} = ( $ansible_inventory{$server}->{host_vars}->{memcached_port} ) ? $ansible_inventory{$server}->{host_vars}->{memcached_port} : 11211; } $ansible_inventory{$server}->{roles}->{memcached}->{memcached_size} = ( $ansible_inventory{$server}->{host_vars}->{memcached_size} ) ? $ansible_inventory{$server}->{host_vars}->{memcached_size} : 64; } if ( exists $ansible_inventory{$server}->{groups}->{sphinx} ) { $ansible_inventory{$server}->{roles}->{sphinx} = { sphinx_general_listen => ( $ansible_inventory{$server}->{host_vars} ->{sphinx_general_listen} ) ? $ansible_inventory{$server}->{host_vars} ->{sphinx_general_listen} : 9312, sphinx_mysqlproto_listen => ( $ansible_inventory{$server}->{host_vars} ->{sphinx_mysqlproto_listen} ) ? $ansible_inventory{$server}->{host_vars} ->{sphinx_mysqlproto_listen} : 9306, }; } if ( exists $ansible_inventory{$server}->{groups}->{web} ) { $ansible_inventory{$server}->{roles}->{web} = {}; } if ( exists $ansible_inventory{$server}->{groups}->{push} ) { $ansible_inventory{$server}->{roles}->{push} = {}; } if ( exists $ansible_inventory{$server}->{groups}->{mgmt} ) { $ansible_inventory{$server}->{roles}->{mgmt} = {}; } } return Output->new( error => 0, data => [ "inventory", \%ansible_inventory ], ); } # get information about current configuration of ansible pool sub get_ansible_data { my ( $self, $host ) = @_; # initilize data my $ansible_conf = $self->ansible_conf; my $bitrix_conf = $self->bitrix_conf; my $message_p = ( caller(0) )[3]; my $message_t = __PACKAGE__; my $debug = $self->debug; my $logOutput = Output->new( error => 0, logfile => $self->logfile ); my $get_ansible_inventory = $self->get_ansible_inventory(); return $get_ansible_inventory if ( $get_ansible_inventory->is_error ); my $ansible_pool_data = $get_ansible_inventory->data->[1]; delete $ansible_pool_data->{aliases} if ( exists $ansible_pool_data->{aliases} ); if ( defined $host ) { foreach my $s ( keys %$ansible_pool_data ) { next if ( $host eq $s ); next if ( $host eq $ansible_pool_data->{$s}->{ip} ); next if ( $host eq $ansible_pool_data->{$s}->{hostname} ); delete $ansible_pool_data->{$s}; } } return Output->new( 'error' => 0, data => [ 'hosts', $ansible_pool_data ] ); } sub get_inventory_hostname { my ( $self, $host ) = @_; if ( not defined $host ) { return Output->new( error => 1, message => "Option host is mandatory option", ); } # initilize data my $ansible_conf = $self->ansible_conf; my $bitrix_conf = $self->bitrix_conf; my $message_p = ( caller(0) )[3]; my $message_t = __PACKAGE__; my $debug = $self->debug; my $logOutput = Output->new( error => 0, logfile => $self->logfile ); my $get_ansible_inventory = $self->get_ansible_inventory(); return $get_ansible_inventory if ( $get_ansible_inventory->is_error ); my $ansible_pool_data = $get_ansible_inventory->data->[1]; delete $ansible_pool_data->{aliases} if ( exists $ansible_pool_data->{aliases} ); if ( defined $host ) { foreach my $s ( keys %$ansible_pool_data ) { if ( ( $host eq $s ) || ( $host eq $ansible_pool_data->{$s}->{ip} ) || ( $host eq $ansible_pool_data->{$s}->{hostname} ) ) { return Output->new( error => 0, data => [ 'ident', $s ], ); } } } return Output->new( 'error' => 1, 'message' => "Cannot find host=$host in the pool." ); } sub get_inventory_hostname_at_group { my ( $self, $group ) = @_; if ( not defined $group ) { return Output->new( error => 1, message => "Option group is mandatory option", ); } # initilize data my $ansible_conf = $self->ansible_conf; my $bitrix_conf = $self->bitrix_conf; my $message_p = ( caller(0) )[3]; my $message_t = __PACKAGE__; my $debug = $self->debug; my $logOutput = Output->new( error => 0, logfile => $self->logfile ); my $get_ansible_inventory = $self->get_ansible_inventory(); return $get_ansible_inventory if ( $get_ansible_inventory->is_error ); my $ansible_pool_data = $get_ansible_inventory->data->[1]; delete $ansible_pool_data->{aliases} if ( exists $ansible_pool_data->{aliases} ); foreach my $s ( keys %$ansible_pool_data ) { next if ( exists $ansible_pool_data->{$s}->{roles}->{$group}); delete $ansible_pool_data->{$s}; } return Output->new( 'error' => 0, data => [ 'hosts', $ansible_pool_data ] ); } # create config file from template # usage on initial setup # replace only IP address and HostName by local values sub create_conf_from_template { my $template = shift; # template file my $dest = shift; # destination file my $master_info = shift; # hash with master info{ netaddr, host, interface, netname } my $bitrix_type = shift; if ( not defined $bitrix_type ) { $bitrix_type = "general"; } my $message_p = ( caller(0) )[3]; my $message_t = __PACKAGE__; my $po = Pool->new(); my $debug = $po->debug; my $logOutput = Output->new( error => 0, logfile => $po->logfile ); if ( $bitrix_type ne "general" ) { if ( -f $template . "_" . $bitrix_type ) { $template .= "_" . $bitrix_type; } } my $netaddr_type = 'ipv4'; if ( $master_info->{'netaddr'} !~ /^[\d\.]+$/ ) { $netaddr_type = 'fqdn'; } # if template not exists - nothing to do ( -f $template ) or return Output->new( error => 0, message => "$message_p: not found template. nothing to do!" ); # replace open( my $th, '<', "$template" ) or return Output->new( error => 1, message => "$message_p: cannot open $template: $!" ); open( my $dh, '>', "$dest" ) or return Output->new( error => 1, message => "$message_p: cannot open $dest: $!" ); while (<$th>) { s/\{\{\s*hostname\s*\}\}/$master_info->{'host'}/g; s/\{\{\s*host_ip_address\s*\}\}/$master_info->{'netaddr'}/g; s/\{\{\s*host_id\s*\}\}/$master_info->{'host_id'}/g; s/\{\{\s*host_pass\s*\}\}/$master_info->{'host_pass'}/g; s/\{\{\s*local_interface\s*\}\}/$master_info->{'interface'}/g; s/\{\{\s*netaddr_type\s*\}\}/$netaddr_type/g; s/\{\{\s*host_netname\s*\}\}/$master_info->{'netname'}/g; print $dh $_; } close $th; close $dh; # set permission chmod 0640, $dest; return Output->new( error => 0, message => "$message_p: replace in $dest complete" ); } # test ansible clien file sub test_ansible_client_file { my $self = shift; my $message_p = ( caller(0) )[3]; my $message_t = __PACKAGE__; my $debug = $self->debug; my $logOutput = Output->new( error => 0, logfile => $self->logfile ); # get ansible config options my $ansible_conf = $self->ansible_conf; # test if host already in the pool if ( -f $ansible_conf->{'client_conf'} ) { if ($debug) { $logOutput->log_data( "$message_p: Found client config " . $ansible_conf->{'client_conf'} ); } open( my $ch, '<', $ansible_conf->{'client_conf'} ) or return Output->new( error => 2, message => "$message_p: Found client " . $ansible_conf->{'client_conf'} . ", cannot open it: $!", ); my $local_name = undef; my @local_groups = (); while (<$ch>) { s/^\s+//; s/\s\+$//; next if (/^#/); next if (/^$/); if (/^hostname\s*=\s*(\S+)$/) { $local_name = $1; } if (/^groups\s*=(.+)$/) { my $groups = $1; $groups =~ s/^\s+//; $groups =~ s/\s+$//; @local_groups = split( /\s+/, $groups ); } } close $ch; if ( defined $local_name ) { if ( grep ( /^bitrix-mgmt$/, @local_groups ) ) { return Output->new( error => 1, message => "$message_p: Bitrix pool already exists in hosts file" ); } else { return Output->new( error => 1, message => "$message_p: Host $local_name is configured as a client" ); } } else { return Output->new( error => 0, message => "$message_p: file " . $ansible_conf->{'client_conf'} . "doesn't contain pool info", ); } } else { if ($debug) { $logOutput->log_data( "$message_p: not found config client " . $ansible_conf->{'client_conf'} ); } return Output->new( error => 0, message => "$message_p: not found config client " . $ansible_conf->{'client_conf'}, ); } } # create pool ssh keys and save it for local usage in root directory sub create_pool_ssh_keys { my $self = shift; my $hostname = shift; my $message_p = ( caller(0) )[3]; my $message_t = __PACKAGE__; my $confData = $self->get_ansible_data(); my $debug = $self->debug; my $logOutput = Output->new( error => 0, logfile => $self->logfile ); $hostname = Pool::esc_chars($hostname); # create directory for ssh keys: /etc/ansible/.ssh my $ansible_conf = $self->ansible_conf; if ( !-d $ansible_conf->{'sshkeys'} ) { mkdir $ansible_conf->{'sshkeys'} or return Output->new( error => 2, message => "$message_p: Cannot create directory " . $ansible_conf->{'sshkeys'} ); chmod 0700, $ansible_conf->{'sshkeys'}; if ($debug) { $logOutput->log_data("$message_p: create ssh key dir"); } } # create ssh key file my $random = $self->generate_random; my $sshkey_sec = catfile( $ansible_conf->{'sshkeys'}, "$random.bxkey" ); my $sshkey_pub = catfile( $ansible_conf->{'sshkeys'}, "$random.bxkey.pub" ); if ( -f $sshkey_sec ) { unlink $sshkey_sec; } if ( -f $sshkey_pub ) { unlink $sshkey_pub; } if ($debug) { $logOutput->log_data("$message_p: defined ssh key path"); } # ssh generate via ssh-keygen (system): my $ssh_keygen_cmd = qq|ssh-keygen -t rsa -N "" -f $sshkey_sec -C "ANSIBLE_KEY_$hostname" >/dev/null 2>&1|; system($ssh_keygen_cmd ) == 0 or return Output->new( error => 3, message => "$message_p: Cannot generate sshkey for bitrix pool." ); if ($debug) { $logOutput->log_data("$message_p: generate key $sshkey_sec"); } # save public key info to variable open( my $sp, '<', $sshkey_pub ) or return Output->new( error => 1, message => "$message_p: cannot open $sshkey_pub: $!", ); my $key_info = ""; while (<$sp>) { $key_info .= $_; } close $sp; # install key to local server, on current server my $ssh_dir = "/root/.ssh"; if ( !-d $ssh_dir ) { mkdir $ssh_dir; chmod 0700, $ssh_dir; } my $ssh_auth = catfile( $ssh_dir, "authorized_keys" ); open( my $sa, '>>', $ssh_auth ) or return Output->new( error => 1, message => "$message_p: cannot open $ssh_auth: $!", ); print $sa $key_info; close $sa; if ($debug) { $logOutput->log_data( "$message_p: update $ssh_auth by new key $sshkey_pub on localhost"); } return Output->new( error => 0, data => [ 'sshkey', $sshkey_sec, $sshkey_pub ], ); } # save ssh security key to ansible.cfg and set display_skipped_hosts to False sub update_ansible_main_config { my $self = shift; my $sshkey_sec = shift; my $message_p = ( caller(0) )[3]; my $message_t = __PACKAGE__; my $confData = $self->get_ansible_data(); my $debug = $self->debug; my $logOutput = Output->new( error => 0, logfile => $self->logfile ); my $display_skipped_hosts = 'False'; # replace current ansible private key by new one # /etc/ansible/ansible.cfg my $ansible_conf = $self->ansible_conf; my $work_conf = $ansible_conf->{'main'}; my $temp_conf = $ansible_conf->{'main'} . ".tmp"; open( my $tmph, '>', $temp_conf ) or return Output->new( error => 4, message => "$message_p: Cannot open temporary $temp_conf: $!" ); open( my $workh, '<', $work_conf ) or return Output->new( error => 4, message => "$message_p: Cannot open config $work_conf: $!" ); my %updates = ( private_key_file => [ $sshkey_sec, 0 ], display_skipped_hosts => [ $display_skipped_hosts, 0 ], ); my $new_key_is_set = 0; my $display_skipped_hosts_is_set = 0; while (<$workh>) { chomp; my $line = $_; if ( $line =~ /^#?\s*(\S+)\s*=\s+(\S+)/ ) { my $config_key = $1; my $config_value = $2; if ( grep /^$config_key$/, keys %updates ) { $updates{$config_key}->[1] = 1; $line = "$config_key = " . $updates{$config_key}->[0] . "\n"; if ($debug) { $logOutput->log_data( "$message_p: replace $config_key, was $config_value set to " . $updates{$config_key}->[0] ); } } } print $tmph $line, "\n"; } close $tmph; close $workh; # test if all updates complete foreach my $key ( keys %updates ) { if ( $updates{$key}->[1] == 0 ) { return Output->new( error => 5, message => "Cannot replace $key value in $work_conf", ); } } # delete old config, recreate new one unlink $work_conf; rename $temp_conf, $work_conf or return Output->new( error => 6, message => "$message_p: Cannot replace work config $work_conf" ); if ($debug) { $logOutput->log_data( "$message_p: update $work_conf by new private_key_file"); } return Output->new( error => 0, message => "update $work_conf", data => [ 'updated', $work_conf ], ); } sub forget_host { my ( $self, $host ) = @_; my $message_p = ( caller(0) )[3]; my $message_t = __PACKAGE__; my $logOutput = Output->new( error => 0, logfile => $self->logfile, debug => $self->debug ); my $get_host_ident = $self->get_inventory_hostname($host); return $get_host_ident if ( $get_host_ident->is_error ); my $host_ident = $get_host_ident->data->[1]; $logOutput->log_data( "$message_p: delete server=$host_ident from the config files"); my $host_file = catfile( $self->ansible_conf->{'host_vars'}, $host_ident ); my $hosts_file = $self->{ansible_conf}->{'hosts'}; my $parse_yaml = get_from_yaml($host_file); return $parse_yaml if ( $parse_yaml->is_error ); my $f_opts = { common_manage => 'forget', forget_bx_hostname => $parse_yaml->data->[1]->{bx_hostname}, forget_bx_netaddr => $parse_yaml->data->[1]->{bx_netaddr}, forget_bx_host => ($parse_yaml->data->[1]->{bx_host}) ? $parse_yaml->data->[1]->{bx_host} : $parse_yaml->data->[1]->{bx_hostname}, }; my %w_objs; # delete records from inventory hosts file my $tmp_hosts_file = $hosts_file . ".tmp"; open( my $hf, '<', $hosts_file ) or return Output->new( error => 1, message => "Cannot open $hosts_file:$!", ); open( my $thf, '>', $tmp_hosts_file ) or return Output->new( error => 1, message => "Cannot open $tmp_hosts_file:$!", ); my $deleted_str = 0; while ( my $line = <$hf> ) { chomp($line); $line =~ s/^\s+//; $line =~ s/\s+$//; if ( $line =~ /^$host_ident\s+/ ) { $deleted_str++; next; } print $thf "$line\n"; } close $thf; close $hf; if ($deleted_str) { unlink $hosts_file; rename $tmp_hosts_file, $hosts_file; } else { $w_objs{$hosts_file} = "Not found record $host_ident in file"; } # delete host_vars file unlink $host_file or $w_objs{$host_file} = "$!"; # run ansible common script (update iptables and othe configs) my $cmd_play = $self->ansible_conf->{'playbook'}; my $cmd_conf = catfile( $self->ansible_conf->{'base'}, "common.yml" ); # run as daemon in background my $dh = bxDaemon->new( task_cmd => qq($cmd_play $cmd_conf) ); my $created_process = $dh->startAnsibleProcess('common', $f_opts); return Output->new( error => 0, message => "Deleting $host_ident configuration files is completed", data => [ $message_p, { 'warnings' => \%w_objs } ] ); } sub change_hostname { my ( $self, $host, $hostname ) = @_; my $message_p = ( caller(0) )[3]; my $message_t = __PACKAGE__; my $logOutput = Output->new( error => 0, logfile => $self->logfile, debug => $self->debug ); my $get_host_ident = $self->get_inventory_hostname($host); return $get_host_ident if ( $get_host_ident->is_error ); my $host_ident = $get_host_ident->data->[1]; $logOutput->log_data( "$message_p: change hostname for server=$host_ident from the config files" ); my $host_file = catfile( $self->ansible_conf->{'host_vars'}, $host_ident ); my $get_host_info = get_from_yaml($host_file); if ( $get_host_info->is_error ) { return $get_host_info; } my $host_info = $get_host_info->data->[1]; $host_info->{bx_host} = $hostname; my $save_host_info = save_to_yaml( $host_info, $host_file ); if ( $save_host_info->is_error ) { return $save_host_info; } # run ansible common script (update iptables and othe configs) my $cmd_play = $self->ansible_conf->{'playbook'}; my $cmd_conf = catfile( $self->ansible_conf->{'base'}, "common.yml" ); # run as daemon in background my $dh = bxDaemon->new( task_cmd => qq($cmd_play $cmd_conf) ); my $created_process = $dh->startProcess('common'); return $created_process; } # delete pool sub delete_pool { my $self = shift; my $message_p = ( caller(0) )[3]; my $message_t = __PACKAGE__; my $logOutput = Output->new( error => 0, logfile => $self->logfile, debug => $self->debug ); $logOutput->log_data("$message_p: delete config files for pool"); # run ansible common script (update iptables and othe configs) my $cmd_play = $self->ansible_conf->{'playbook'}; my $cmd_conf = catfile( $self->ansible_conf->{'base'}, "delete_pool.yml" ); # run as daemon in background my $dh = bxDaemon->new( task_cmd => qq($cmd_play $cmd_conf) ); my $created_process = $dh->startProcess('delete_pool'); return $created_process; } # create new pool with default ansible configuration: # create ssh-key and groups definition in config file sub create_new_pool { my ( $self, $req_hostname, $req_interface, $req_ip ) = @_; my $message_p = ( caller(0) )[3]; my $message_t = __PACKAGE__; my $confData = $self->get_ansible_data(); my $debug = $self->debug; my $bitrix_type = $self->bitrix_type; my $logOutput = Output->new( error => 0, logfile => $self->logfile ); # create pool options for first node #print "Input options: req_hostname=$req_hostname req_interface=$req_interface\n"; if ( not defined $req_hostname ) { $req_hostname = hostname; } if ( not defined $req_interface ) { $req_interface = 'any'; } if ( not defined $req_ip ) { $req_ip = 'any'; } my $net = bxNetworkNode->new( manager_interface => $req_interface, manager_hostname => $req_hostname, manager_ipaddress => $req_ip, debug => $debug, ); my $net_option = $net->create_network_options(); if ( $net_option->is_error ) { return $net_option; } my $master_data = $net_option->get_data->[1]; my $host_id = $self->generate_host_id; my $host_pass = $self->generate_host_password( $master_data->{'ident'} ); my %master_host = ( host => $master_data->{'ident'}, netaddr => $master_data->{'netaddr'}, interface => $master_data->{'interface'}, netname => $master_data->{'fqdn'}, host_id => $host_id, host_pass => $host_pass, ); if ($debug) { $logOutput->log_data( "$message_p: start create pool; host=" . $master_host{'host'} . " netaddr=" . $master_host{'netaddr'} . " int=" . $master_host{'interface'}, ); } # pool already exists if ( $confData->is_error == 0 ) { return Output->new( error => 1, message => "$message_p: Bitrix pool already exists in hosts file" ); } # test client config exist my $test_client = $self->test_ansible_client_file; if ( $test_client->is_error ) { return $test_client; } # create ssh keys my $get_sshkeys = $self->create_pool_ssh_keys( $master_host{'host'} ); if ( $get_sshkeys->is_error ) { return $get_sshkeys; } my $sshkey_sec = $get_sshkeys->get_data->[1]; # update ansible config my $update_main_conf = $self->update_ansible_main_config($sshkey_sec); if ( $update_main_conf->is_error ) { return $update_main_conf; } my $work_conf = $update_main_conf->get_data->[1]; ### fill out hosts and group information with default data my $ansible_conf = $self->ansible_conf; # update host interface if subinterface found $master_host{'interface'} =~ s/^([^:]+):.+$/$1/; # hosts file my $hosts_template = catfile( $self->bitrix_conf->{'aHostsTemplate'}, "hosts" ); my $hosts_dest = $ansible_conf->{'hosts'}; my $replace_hosts = create_conf_from_template( $hosts_template, $hosts_dest, \%master_host, $bitrix_type ); if ( $replace_hosts->is_error ) { return $replace_hosts; } if ($debug) { $logOutput->log_data("$message_p: create new config $hosts_dest"); } # group_vars my $roles = $self->bitrix_conf->{'aHostsGroups'}; my $template_dir = $self->bitrix_conf->{'aHostsTemplate'}; my $prefix = $self->bitrix_conf->{'aHostsPrefix'}; my $group_vars_dir = $self->ansible_conf->{'group_vars'}; if ( !-d $group_vars_dir ) { mkdir $group_vars_dir, 0750 } foreach my $group_name (@$roles) { my $group_template = catfile( $template_dir, $prefix . "-" . $group_name ); my $group_dest = catfile( $group_vars_dir, $prefix . "-" . $group_name . ".yml" ); my $replace_groups = create_conf_from_template( $group_template, $group_dest, \%master_host, $bitrix_type ); if ( $replace_groups->is_error ) { return $replace_groups; } if ($debug) { $logOutput->log_data( "$message_p: create new config $group_dest; with group default options" ); } } # host_vars my $host_vars_dir = $self->ansible_conf->{'host_vars'}; my $host_template = catfile( $template_dir, 'localhost' ); my $host_dest = catfile( $host_vars_dir, $master_host{'host'} ); if ( !-d $host_vars_dir ) { mkdir $host_vars_dir, 0750 } my $replace_host = create_conf_from_template( $host_template, $host_dest, \%master_host, $bitrix_type ); if ($debug) { $logOutput->log_data( "$message_p: create config $host_dest; deafult master host settings" ); } if ( $replace_host->is_error ) { return $replace_host; } # run common playbook # set network, time # run monitor playbook with new option setings my $cmd_play = $ansible_conf->{'playbook'}; my $cmd_conf = catfile( $ansible_conf->{'base'}, "common.yml" ); # run as daemon in background my $dh = bxDaemon->new( task_cmd => qq($cmd_play $cmd_conf) ); my $created_process = $dh->startProcess('common'); my ($task_id) = grep { !/^task_name$/ } keys %{ $created_process->{data}->[1] }; my $task_pid = $created_process->{$task_id}->{pid}; my $task_status = $created_process->{$task_id}->{status}; if ($debug) { $logOutput->log_data( "$message_p: configure common options on master server " . $master_host{'host'} ); } # output info my $output_message = "Created manager configuration for"; $output_message .= " identifier=" . $master_host{'host'}; $output_message .= " interface=" . $master_host{'interface'}; $output_message .= " netaddress=" . $master_host{'netaddr'} . "\\n"; $output_message .= "Created sshkey - $sshkey_sec\\n"; $output_message .= "Update config file $work_conf\\n"; $output_message .= "Created pool configuration in $hosts_dest\\n"; $output_message .= "Run configuration pool job task_id=$task_id\\n"; $output_message .= "All operations complete\\n"; return Output->new( error => 0, message => $output_message, data => [ "sshkey", "$sshkey_sec" ] ); } # get path to ssh key sub get_ssh_key { my $self = shift; my $message_p = "BX_KEY_VIEW"; my $ansData = $self->ansible_conf; my $ansMainConf = $ansData->{'main'}; # search key in the config file open( my $ch, '<', $ansMainConf ) or return Output->new( error => 1, message => "$message_p: Cannot open main config $ansMainConf: $!" ); my $ansSshKey = ""; while (<$ch>) { if (/private_key_file\s*=\s*(\S+)/) { $ansSshKey = $1; } } close $ch; if ( !$ansSshKey ) { return Output->new( error => 2, message => "$message_p: Not found private_key_file derictive in config $ansMainConf" ); } my $ansSshKeyPub = $ansSshKey . ".pub"; if ( !-f $ansSshKey ) { return Output->new( error => 3, message => "$message_p: Record private_key_file found in the config, but private key does not exist in FS" ); } if ( !-f $ansSshKeyPub ) { return Output->new( error => 3, message => "$message_p: Record private_key_file found in the config, but public key does not exist in FS" ); } return Output->new( data => [ 'sshkey', $ansSshKey ] ); } ## update group information # updated files in /etc/ansible/group_vars # options = { group => groupname, opt1 => val1, opt2 => undef } # opt2 - will be deleted, opt1 - updated|added sub update_group_vars { my ( $self, $options ) = @_; my $message_p = ( caller(0) )[3]; my $message_t = __PACKAGE__; my $debug = $self->debug; my $logOutput = Output->new( error => 0, logfile => $self->logfile ); my $bxData = $self->bitrix_conf; my $ansData = $self->ansible_conf; #print "bxPool: ".print Dumper($options); if ( not defined $options->{"group"} ) { return Output->new( error => 1, message => "$message_p: group name is mandatory option" ); } my $group = $options->{'group'}; $group =~ s/^["']//; $group =~ s/['"]$//; my $group_vars_path = catfile( $ansData->{'group_vars'}, $bxData->{'aHostsPrefix'} . '-' . $group . '.yml' ); my $group_vars_temp = $group_vars_path . ".tmp"; if ( !-f $group_vars_path ) { return Output->new( error => 1, message => "$message_p: not found group_vars in $group_vars_path" ); } $logOutput->log_data( "$message_p: start update inventory group=$group inventory file=$group_vars_path" ); # get current data from yaml my $get_inventory = get_from_yaml($group_vars_path); if ( $get_inventory->is_error ) { return $get_inventory; } my $inventory_data = $get_inventory->data->[1]; #print Dumper($inventory_data); # create conf data for update my $updates = 0; my $deletes = 0; my $made_updates = 0; my $made_deletes = 0; foreach my $key ( keys %$options ) { # skip handler options next if ( $key =~ /^(group|state)$/ ); # convert password_file to value which will be saved in the inventory if ( $key =~ /^(\S+_password)_file$/ ) { my $inventory_key = $1; open( my $h, '<', $options->{$key} ) or return Output->new( error => 1, message => "$message_p: cannot open file=" . $options->{$key}, ); my $value = <$h>; chomp($value); close $h; if ( ( defined $inventory_data->{$inventory_key} ) && ( $inventory_data->{$inventory_key} eq $value ) ) { next; } $inventory_data->{$inventory_key} = $value; $updates++; } else { # key=value if ( defined $options->{$key} ) { if ( defined $inventory_data->{$key} ) { if ( $inventory_data->{$key} ne $options->{$key} ) { $inventory_data->{$key} = $options->{$key}; $updates++; } } else { $inventory_data->{$key} = $options->{$key}; $updates++; } } # key else { if ( defined $inventory_data->{$key} ) { delete $inventory_data->{$key}; $deletes++; } } } } $logOutput->log_data("$message_p: found updates=$updates deletes=$deletes"); # update values or create new my $save_inventory = save_to_yaml( $inventory_data, $group_vars_temp ); if ( $save_inventory->is_error ) { return $save_inventory; } # rewrite origin file unlink $group_vars_path; rename $group_vars_temp, $group_vars_path; chmod 0640, $group_vars_path; return Output->new( error => 0, message => "$message_p: " . "File=$group_vars_path is modified; updates=$updates deletes=$deletes", data => [ "$message_p", { updates => $updates, deletes => $deletes, file => $group_vars_path } ] ); } # get current status of monitoring; enable or disable sub monitorStatus { my $self = shift; my $message_p = "BX_MONITOR"; my $ansData = $self->ansible_conf; my $bxData = $self->bitrix_conf; #print "ansData => ",Dumper($ansData); #print "bxData => ",Dumper($bxData); # my $monitoring_opt = { 'monitoring_status' => '', 'monitoring_server' => '', 'monitoring_file' => 0, 'monitoring_server_netaddr' => '', }; # config file that contains monitoring information: /etc/ansible/group_vars/bitrix-hosts my $cfg_file = catfile( $ansData->{'group_vars'}, $bxData->{'aHostsPrefix'} . '-' . $bxData->{'aHostsDefault'} . '.yml' ); if ( !-f $cfg_file ) { return Output->new( error => 0, data => [ 'monitor', $monitoring_opt ] ); } open( my $ch, '<', $cfg_file ) or return Output->new( error => 1, message => "$message_p: Cannot open $cfg_file" ); $monitoring_opt->{'monitoring_file'} = $cfg_file; while (<$ch>) { chomp; s/^\s+//; s/\s+$//; next if (/^#/); next if (/^$/); if ( /^(monitoring_status|monitoring_server|monitoring_server_netaddr)\s*:\s*(\S+)$/ ) { $monitoring_opt->{$1} = $2; } } close $ch; # if status wanted return Output->new( error => 0, data => [ 'monitor', $monitoring_opt ] ); } # change config file for monitoring # run playbook monitor.yml sub monitorEnable { my ( $self, $opts ) = @_; my $ansData = $self->ansible_conf; # run monitor playbook with new option setings my $cmd_play = $ansData->{'playbook'}; my $cmd_conf = catfile( $ansData->{'base'}, "monitor.yml" ); my $cmd_opts = { 'monitoring_status' => 'enable' }; # update ansible playbook options by optionals foreach my $o ( keys %$opts ) { if ( defined $opts->{$o} ) { $cmd_opts->{$o} = $opts->{$o}; } } # run as daemon in background my $dh = bxDaemon->new( debug => $self->debug, task_cmd => qq($cmd_play $cmd_conf) ); my $created_process = $dh->startAnsibleProcess( 'monitor', $cmd_opts ); return $created_process; } # update monitoring configuration for new host sub monitorUpdate { my $self = shift; # get current status of monitoring my $monitor = $self->monitorStatus; if ( $monitor->is_error ) { return Output->new( error => 1, message => "Cannot read config file" ); } my $monitor_status = $monitor->get_data; # current monitoring options my $mon_file = $monitor_status->[1]->{'monitoring_file'}; my $mon_flag = $monitor_status->[1]->{'monitoring_status'}; my $mon_mgmt = $monitor_status->[1]->{'monitoring_server'}; my $ansData = $self->ansible_conf; my $bxData = $self->bitrix_conf; # existen or not file with default value for group bitrix-hosts if ( !$mon_file ) { return Output->new( error => 1, message => "Monitoring does not enable" ); } # run monitor playbook with new option setings my $cmd_play = $ansData->{'playbook'}; my $cmd_conf = catfile( $ansData->{'base'}, "monitor.yml" ); my $cmd_opts = { 'monitoring_status' => 'update' }; # run as daemon in background my $dh = bxDaemon->new( debug => $self->debug, task_cmd => qq($cmd_play $cmd_conf) ); my $created_process = $dh->startAnsibleProcess( 'monitor', $cmd_opts ); return $created_process; } # change config file for monitoring # run playbook monitor.yml sub monitorDisable { my $self = shift; # get current status of monitoring my $monitor = $self->monitorStatus; if ( $monitor->is_error ) { return Output->new( error => 1, message => "Cannot read config file" ); } my $monitor_status = $monitor->get_data; # current monitoring options my $mon_file = $monitor_status->[1]->{'monitoring_file'}; my $mon_flag = $monitor_status->[1]->{'monitoring_status'}; my $mon_mgmt = $monitor_status->[1]->{'monitoring_server'}; my $ansData = $self->ansible_conf; my $bxData = $self->bitrix_conf; # existen or not file with default value for group bitrix-hosts if ( !$mon_file ) { return Output->new( error => 1, message => "Monitoring does not enable" ); } # if if ( $mon_flag =~ /^enable$/ ) { # run monitor playbook with new option setings my $cmd_play = $ansData->{'playbook'}; my $cmd_conf = catfile( $ansData->{'base'}, "monitor.yml" ); my $cmd_opts = { 'monitoring_status' => 'disable' }; # run as daemon in background my $dh = bxDaemon->new( debug => $self->debug, task_cmd => qq($cmd_play $cmd_conf) ); my $created_process = $dh->startAnsibleProcess( 'monitor', $cmd_opts ); return $created_process; } else { return Output->new( error => 0, message => "Monitoring already disable for pool", data => $monitor_status ); } } sub update_pool { my ( $self, $host, $type ) = @_; if ( not defined $type ) { $type = "bx_update"; } my ($host_ident); if ( defined $host ) { my $get_host_ident = $self->get_inventory_hostname($host); return $get_host_ident if ( $get_host_ident->is_error ); $host_ident = $get_host_ident->data->[1]; } # initilize data my $ansData = $self->ansible_conf; my $message_p = ( caller(0) )[3]; my $message_t = __PACKAGE__; my $cmd_play = $ansData->{'playbook'}; my $cmd_conf = catfile( $ansData->{'base'}, "common.yml" ); my $cmd_opts = { 'common_manage' => 'version' }; if ( $type eq "bx_upgrade" ) { $cmd_opts->{common_manage} = "update_packages"; } if ( defined $host_ident ) { $cmd_opts->{'common_server'} = $host_ident; } my $dh = bxDaemon->new( debug => $self->debug, task_cmd => qq($cmd_play $cmd_conf) ); my $created_process = $dh->startAnsibleProcess( 'update', $cmd_opts ); return $created_process; } sub reboot_server { my ( $self, $host ) = @_; my $message_p = ( caller(0) )[3]; my $message_t = __PACKAGE__; my $get_host_ident = $self->get_inventory_hostname($host); return $get_host_ident if ( $get_host_ident->is_error ); my $host_ident = $get_host_ident->data->[1]; # initilize data my $ansData = $self->ansible_conf; my $cmd_play = $ansData->{'playbook'}; my $cmd_conf = catfile( $ansData->{'base'}, "common.yml" ); my $cmd_opts = { 'common_manage' => 'reboot', 'common_server' => $host_ident }; my $dh = bxDaemon->new( debug => $self->debug, task_cmd => qq($cmd_play $cmd_conf) ); my $created_process = $dh->startAnsibleProcess( 'reboot', $cmd_opts ); return $created_process; } sub timezone_in_the_pool { my $self = shift; my $tz = shift; # timezone, default: Europe/Moscow my $tz_php = shift; # update php settings or not my $message_p = ( caller(0) )[3]; my $message_t = __PACKAGE__; if ( not defined $tz_php ) { $tz_php = 1; } my $update_str = 'update'; if ( $tz_php == 0 ) { $update_str = 'not_update'; } if ( not defined $tz ) { $tz = 'Europe/Moscow'; } # initilize data my $ansData = $self->ansible_conf; my $cmd_play = $ansData->{'playbook'}; my $cmd_conf = catfile( $ansData->{'base'}, "configure_timezone.yml" ); my $cmd_opts = { 'timezone_string' => $tz, 'timezone_php_update' => $update_str }; my $dh = bxDaemon->new( debug => $self->debug, task_cmd => qq($cmd_play $cmd_conf) ); my $created_process = $dh->startAnsibleProcess( 'configure_tz', $cmd_opts ); return $created_process; } sub password_on_server { my ( $self, $host, $user, $password ) = @_; my $message_p = ( caller(0) )[3]; my $message_t = __PACKAGE__; if ( ( not defined $host ) || ( not defined $user ) || ( not defined $password ) ) { return Output->new( error => 1, message => "$message_p: host, user, password is mandatory", ); } my $get_host_ident = $self->get_inventory_hostname($host); return $get_host_ident if ( $get_host_ident->is_error ); my $host_ident = $get_host_ident->data->[1]; # create file for chpasswd my $tmp_dir = "/opt/webdir/keys"; if ( !-d $tmp_dir ) { mkdir $tmp_dir; chmod 0700, $tmp_dir; } my $tmp_file = catfile( $tmp_dir, "source_passwd" ); open( my $th, '>', $tmp_file ) or return Output->new( error => 1, message => "$message_p: Cannot open $tmp_file: $!", ); print $th "$user:$password"; close $th; # initilize data my $ansData = $self->ansible_conf; my $cmd_play = $ansData->{'playbook'}; my $cmd_conf = catfile( $ansData->{'base'}, "common.yml" ); my $cmd_opts = { 'common_manage' => 'password', 'common_server' => $host_ident, 'common_file' => $tmp_file, 'common_user' => $user }; my $dh = bxDaemon->new( debug => $self->debug, task_cmd => qq($cmd_play $cmd_conf) ); my $created_process = $dh->startAnsibleProcess( 'password', $cmd_opts ); return $created_process; } sub deleteSSHFinger { my $self = shift; my $old_ip = shift; # old my $new_ip = shift; $old_ip = Pool::esc_chars($old_ip); $new_ip = Pool::esc_chars($new_ip); my $cmd = qq(ssh-keygen -R $old_ip >/dev/null 2>&1); system($cmd) == 0 or return Output->new( error => 1, message => "cmd \`ssh-keygen -R $old_ip\` return error: $!", ); # get new rsa key my $cmd_gen = qq(ssh-keyscan -t rsa $new_ip >> /root/.ssh/known_hosts 2>/dev/null); system($cmd_gen) == 0 or return Output->new( error => 1, message => "cmd \`ssh-keyscan -t rsa $new_ip\` return error: $!", ); return Output->new( error => 0, message => "known_hosts updated", ); } sub replaceIPAddress { my $self = shift; my $old_value = shift; my $new_value = shift; my $config = shift; my $message_p = ( caller(0) )[3]; my $message_t = __PACKAGE__; $old_value =~ s/\./\\\./g; my $tmp_config = $config . ".tmp"; open( my $tmp, '>', "$tmp_config" ) or return Output->new( error => 1, message => "$message_p: Cannot open $tmp_config: $!", ); open( my $cfg, '<', "$config" ) or return Output->new( error => 1, message => "$message_p: Cannot open $config: $!", ); while (<$cfg>) { s/\b$old_value\b/$new_value/; print $tmp $_; } close $cfg; close $tmp; unlink $config; rename $tmp_config, $config; chmod 0640, $config; return Output->new( error => 0, message => "$message_p: $config updated\n" ); } sub update_network { my ( $self, $host, $new_ip ) = @_; my $message_p = ( caller(0) )[3]; my $message_t = __PACKAGE__; my $debug = $self->debug; my $logOutput = Output->new( error => 0, logfile => $self->logfile, debug => $self->debug, ); my $get_host_ident = $self->get_inventory_hostname($host); return $get_host_ident if ( $get_host_ident->is_error ); my $host_ident = $get_host_ident->data->[1]; if ( not defined $new_ip ) { return Output->new( error => 1, message => "New IP is mandatory option", ); } $logOutput->log_data( "$message_p: change ipaddress for server=$host_ident from the config files" ); # get current ip address my $current_ip = undef; my $get_all_hosts = $self->get_ansible_data(); if ( $get_all_hosts->is_error ) { return $get_all_hosts } my $data_all_hosts = $get_all_hosts->get_data->[1]; # found host by host_id foreach my $ident ( keys %$data_all_hosts ) { if ( $ident eq $host_ident ) { $current_ip = $data_all_hosts->{$ident}->{'ip'}; } } if ( !$current_ip ) { return Output->new( error => 1, message => "$message_p: not found host host_id=$host_ident" ); } $logOutput->log_data( "$message_p: ident=$host_ident old_ip=$current_ip new_ip=$new_ip"); # have to update all group settings and personal host settings and hosts file # after that start common task + monitor task my $ansible_conf = $self->ansible_conf; my $bitrix_conf = $self->bitrix_conf; my @updated_files = ( $ansible_conf->{'hosts'}, catfile( $ansible_conf->{'host_vars'}, $host_ident ), ); foreach my $role ( @{ $bitrix_conf->{'aHostsGroups'} } ) { my $fp = catfile( $ansible_conf->{'group_vars'}, $bitrix_conf->{'aHostsPrefix'} . '-' . $role . '.yml' ); if ( -f $fp ) { push @updated_files, $fp; } } # update files #print "$current_ip, $new_ip\n"; foreach my $cfg (@updated_files) { my $update_file = $self->replaceIPAddress( $current_ip, $new_ip, $cfg ); if ( $update_file->is_error ) { return $update_file; } } # update ssh keygen my $update_known_hosts = $self->deleteSSHFinger( $current_ip, $new_ip ); # start ansible common play my $cmd_play = $ansible_conf->{'playbook'}; my $cmd_conf = catfile( $ansible_conf->{'base'}, "monitor.yml" ); my $dh = bxDaemon->new( task_cmd => qq($cmd_play $cmd_conf) ); my $created_process = $dh->startProcess( "network_" . $host_ident ); return $created_process; #return Output->new(error=>0, message => "123"); } # by unique host_id sub UpdateHostNetwork { my ( $self, $host_id, $new_ip ) = @_; my $message_p = ( caller(0) )[3]; my $message_t = __PACKAGE__; my $debug = $self->debug; my $logOutput = Output->new( error => 0, logfile => $self->logfile, debug => $self->debug, ); if ( not defined $host_id ) { return Output->new( error => 1, message => "Host identifier is mandatory option", ); } if ( not defined $new_ip ) { return Output->new( error => 1, message => "New IP is mandatory option", ); } $logOutput->log_data( "$message_p: change ipaddress for host_id=$host_id from the config files" ); # get current ip address my $current_ip = undef; my $host_ident = undef; my $get_all_hosts = $self->get_ansible_data(); if ( $get_all_hosts->is_error ) { return $get_all_hosts } my $data_all_hosts = $get_all_hosts->get_data->[1]; # found host by host_id foreach my $ident ( keys %$data_all_hosts ) { if ( $data_all_hosts->{$ident}->{host_id} eq $host_id ) { $current_ip = $data_all_hosts->{$ident}->{'ip'}; $host_ident = $ident; } } if ( !$current_ip ) { return Output->new( error => 1, message => "$message_p: not found host host_id=$host_id" ); } $logOutput->log_data( "$message_p: ident=$host_ident old_ip=$current_ip new_ip=$new_ip"); return $self->update_network( $host_ident, $new_ip ); } sub TestHostNetwork { my $self = shift; my $log = shift; my $message_p = ( caller(0) )[3]; my $message_t = __PACKAGE__; my $debug = $self->debug; my $logOutput = Output->new( error => 0, logfile => $self->logfile ); if ( !$log ) { return Output->new( error => 1, message => "$message_p: path to log file is mandatory", ); } if ($debug) { $logOutput->log_data("$message_p: parse log=$log"); } my %updates; my $updates_count = 0; open( my $lh, '<', $log ) or return Output->new( error => 1, message => "$message_p: Cannot open $log: $!", ); while (<$lh>) { # 172.17.0.120 - 1402045968_VzleKSTLPM [06/Jun/2014:13:24:56 +0400 - -] 200 "GET /change?client_ip=172.17.0.120 HTTP/1.1" 0 "-" "Updater/www2" "-" chomp; next if (/^$/); if (/^([\d\.]+)\s+\S+\s+(\S+)\s+\[[^\]]+\]\s+(\d+)\s+"([^"]+)"/) { my $remote_address = $1; my $host_id = $2; my $code = $3; my $request = $4; my ( $type, $uri, $proto ) = split( /\s+/, $request ); my $client_ip = ""; if ( $uri =~ m:^/change\?client_ip=([\d\.]+)$: ) { $client_ip = $1; } #print "$remote_address:$host_id:$code:$client_ip\n"; if ( $code == 200 && $client_ip !~ /^$/ ) { $updates{$host_id} = $remote_address; $updates_count++; } } } close $lh; if ( $updates_count > 0 ) { foreach my $host_id ( keys %updates ) { my $update_host = $self->UpdateHostNetwork( $host_id, $updates{$host_id} ); if ( $update_host->is_error ) { return $update_host; } } } else { return Output->new( error => 0, message => "$message_p: Not found request for network update", ); } unlink $log; my $nginx_cmd = "/sbin/service nginx reload 1>/dev/null 2>/dev/null"; system($nginx_cmd) == 0 or return Output->new( error => 1, message => "$message_p: Cannot reload nginx service", ); my $update_servers = join( ', ', keys %updates ); return Output->new( error => 0, message => "$message_p: Updated $update_servers", ); } sub beta_version { my $self = shift; my $type = shift; $type = "disable" if ( not defined $type ); my $message_p = ( caller(0) )[3]; my $message_t = __PACKAGE__; # initilize data my $ansData = $self->ansible_conf; my $cmd_play = $ansData->{'playbook'}; my $cmd_conf = catfile( $ansData->{'base'}, "beta_version.yml" ); my $cmd_opts = { 'beta_version' => $type }; my $dh = bxDaemon->new( debug => $self->debug, task_cmd => qq($cmd_play $cmd_conf) ); my $created_process = $dh->startAnsibleProcess( 'beta_version', $cmd_opts ); return $created_process; } 1;