Your IP : 18.191.168.65
Current Path : /opt/webdir/lib/ |
|
Current File : //opt/webdir/lib/bxSites.pm |
# list information about all sites on the server
package bxSites;
use strict;
use warnings;
use Moose;
use File::Basename qw( dirname basename );
use File::Spec::Functions;
use Data::Dumper;
use Output;
use bxSite;
use bxSiteFiles;
use Host;
use bxInventory;
use bxMysql;
use File::Temp;
use Pool;
# basic path for site
has 'apache', is => 'ro', default => '/etc/httpd/bx/conf';
has 'filters', is => 'rw', lazy => 1, builder => 'set_filter';
has 'conf', is => 'ro', default => 'conf';
has 'pref', is => 'ro', default => 'bx_ext_';
has 'debug', is => 'ro', default => 0;
has 'logfile', is => 'ro', default => '/opt/webdir/logs/bxSiteNew.debug';
our $TMPDIR = "/opt/webdir/tmp";
if ( !-d $TMPDIR ) {
mkdir $TMPDIR, 0700;
}
# set filter
sub set_filter {
my $self = shift;
return {};
}
sub generate_simple_password {
my $password = '';
my @chars = ( "A" .. "Z", "a" .. "z", "1" .. "9" );
$password .= $chars[ rand @chars ] for 1 .. 15;
return $password;
}
#
# listSite - list all sites: kernel, links and ext_kernel
sub listAllSite {
my $self = shift;
my $message_p = ( caller(0) )[3];
my $message_t = 'bxSite';
my $logOutput = Output->new(
error => 0,
logfile => $self->logfile,
debug => $self->debug,
);
$logOutput->log_data("$message_p: get all sites info");
# get all configs name from apache directory
my $apache_config_dir = $self->apache;
opendir( my $ach, $apache_config_dir )
or return Output->new(
error => 1,
message => "Can't open $apache_config_dir: $!",
);
my $pref = $self->pref;
my $ext = $self->conf;
my @found_sites = grep { /^$pref.+\.$ext$/ } readdir $ach;
closedir $ach;
if ( -f catfile( $self->apache, "default." . $self->conf ) ) {
push @found_sites, "default";
}
#print join('|',@found_sites),"\n";
$logOutput->log_data(
"$message_p: found configs " . join( ',', @found_sites ) );
# process site
my %list_sites;
my %name_to_db;
foreach my $site_name (@found_sites) {
$site_name =~ s/^$pref//;
$site_name =~ s/\.$ext$//;
$logOutput->log_data(
"$message_p: process $site_name; get site_options");
my $bxSites = bxSite->new( site_name => $site_name );
my $bxSiteOptions = $bxSites->site_options;
$list_sites{$site_name} = $bxSiteOptions;
$logOutput->log_data("$message_p: add $site_name for sites list");
}
my $sites_count = keys %list_sites;
if ( $sites_count == 0 ) {
return Output->new(
error => 1,
message => "$message_p: Not found sites on teh server"
);
}
else {
# found ext kernels
my @ext_kernels;
foreach my $site_name ( keys %list_sites ) {
# if site type is kernel, than
# SiteKernelDir => DBDir
if ( $list_sites{$site_name}->{'SiteInstall'} =~ /^kernel$/ ) {
next;
# not found kernel,ext_kernel or link
}
elsif ( $list_sites{$site_name}->{'SiteInstall'} =~ /^$/ ) {
next;
# try found kernel info in site list
}
else {
#print "site: $site_name\n";
my $site_kernel_root =
$list_sites{$site_name}->{'SiteKernelDir'};
my $site_kernel_root_regexp = $site_kernel_root;
$site_kernel_root_regexp =~ s/\//\\\//g;
# test kernel
foreach my $kernel_name ( keys %list_sites ) {
# skip links
next
if ( $list_sites{$kernel_name}->{'SiteInstall'} =~
/^link$/ );
# found kernel with the same DBName
if ( $list_sites{$kernel_name}->{'DocumentRoot'} =~
m/^$site_kernel_root_regexp$/ )
{
$list_sites{$site_name}->{'SiteKernelDB'} =
$list_sites{$kernel_name}->{'DBName'};
}
}
# kernel not found, assume the presence of the external core, without web configs
if ( $list_sites{$site_name}->{'SiteKernelDB'} =~ /^$/ ) {
$logOutput->log_data(
"Not found kernel for $site_name - $site_kernel_root_regexp"
);
next
if ( grep( /^$site_kernel_root_regexp$/, @ext_kernels ) );
$logOutput->log_data(
"Add $site_kernel_root to external kernel list");
push @ext_kernels, $site_kernel_root;
}
}
}
# if external kernel found
my $ext_kernels = @ext_kernels;
if ($ext_kernels) {
foreach my $kernel_root (@ext_kernels) {
my $site_name = "ext_" . basename($kernel_root);
my $bxSite = bxSite->new(
site_dir => $kernel_root,
site_name => $site_name,
);
my $bxSiteOptions = $bxSite->get_site_options();
$list_sites{$site_name} = $bxSiteOptions;
}
}
return Output->new(
error => 0,
data => [ $message_t, \%list_sites ],
);
}
}
# test limits for cluster configurations: web or mysql
# 1. exists sites without Scale module installed
# 2. exists sites without Cluster module installed
# 3. several sites ; type is kernel or ext kernel
# return:
# error => 0|1
# message => User message for output
sub testClusterConfig {
my $self = shift;
my $message_p = ( caller(0) )[3];
my $message_t = "testClusterConfig";
my $logOutput = Output->new(
error => 0,
logfile => $self->logfile,
debug => $self->debug,
);
my $test_result = {
without_scale => [],
without_cluster => [],
kernels => [],
test_without_scale => 0,
test_without_cluster => 0,
test_kernels => 0,
};
my $listAllSite = $self->listAllSite();
if ( $listAllSite->is_error ) {
return Output->new(
error => 0,
data => [ $message_t, $test_result ],
);
}
foreach my $site_name ( keys %{ $listAllSite->data->[1] } ) {
my $bxSiteOptions = $listAllSite->data->[1]->{$site_name};
my $site_module_scale = $bxSiteOptions->{'module_scale'};
my $site_module_cluster = $bxSiteOptions->{'module_cluster'};
my $site_state = $bxSiteOptions->{'SiteStatus'};
my $site_dir = $bxSiteOptions->{'DocumentRoot'};
my $site_type = $bxSiteOptions->{'SiteInstall'};
# cluster configuration doesn't use
if ( $site_state =~ /^finished$/ ) {
# scale module
if ( $site_module_scale =~ /^not_installed$/ ) {
push @{ $test_result->{'without_scale'} },
{
SiteName => $site_name,
DocumentRoot => $site_dir,
SiteInstall => $site_type,
};
}
# cluster module
if ( $site_module_cluster =~ /^not_installed$/ ) {
push @{ $test_result->{'without_cluster'} },
{
SiteName => $site_name,
DocumentRoot => $site_dir,
SiteInstall => $site_type,
};
}
# count kernels
if ( $site_type =~ /kernel$/ ) {
push @{ $test_result->{'kernels'} },
{
SiteName => $site_name,
DocumentRoot => $site_dir,
SiteInstall => $site_type,
};
}
}
}
# testing result and create approve/disapprove for cluster configuration
$test_result->{'test_kernels'} = @{ $test_result->{'kernels'} };
$test_result->{'test_without_scale'} = @{ $test_result->{'without_scale'} };
$test_result->{'test_without_cluster'} =
@{ $test_result->{'without_cluster'} };
return Output->new(
error => 0,
data => [ $message_t, $test_result ],
);
}
# listSite - list all sites with defined filters
sub listSite {
my $self = shift;
my $message_p = ( caller(0) )[3];
my $message_t = 'bxSite';
my $logOutput = Output->new(
error => 0,
logfile => $self->logfile,
debug => $self->debug,
);
$logOutput->log_data("$message_p: get all sites info with filters");
my $listAllSite = $self->listAllSite();
if ( $listAllSite->is_error ) { return $listAllSite; }
my %list_sites;
my $filters = $self->filters;
my $filters_count = keys %$filters;
$logOutput->log_data(
"$message_p: user define $filters_count filters: " . Dumper($filters) );
foreach my $site_name ( keys %{ $listAllSite->data->[1] } ) {
my $skip_site = 0;
my $bxSiteOptions = $listAllSite->data->[1]->{$site_name};
if ($filters_count) {
foreach my $fk ( keys %$filters ) {
my $filter_val = $filters->{$fk};
$filter_val =~ s/^['"]//;
$filter_val =~ s/['"]$//;
if ( $bxSiteOptions->{$fk} !~ /^($filter_val)$/ ) {
$skip_site = 1;
$logOutput->log_data(
"$message_p: skip site $site_name because key=$fk "
. $bxSiteOptions->{$fk} . "!="
. $filter_val );
}
}
}
next if ( $skip_site == 1 );
$list_sites{$site_name} = $bxSiteOptions;
$logOutput->log_data("$message_p: add $site_name for sites list");
}
my $sites_count = keys %list_sites;
if ( $sites_count == 0 ) {
my $user_message = "Not found sites with defined options:";
foreach my $k ( sort keys %$filters ) {
$user_message .= " $k=" . $filters->{$k};
}
return Output->new(
error => 1,
message => "$message_p: $user_message"
);
}
else {
return Output->new(
error => 0,
data => [ $message_t, \%list_sites ],
);
}
}
# enable backup task for sites with the same DBName
sub enableBackupForDB {
my $self = shift;
my $kernel_name = shift;
my $cron_min = shift;
my $cron_hour = shift;
my $cron_day = shift;
my $cron_month = shift;
my $cron_wday = shift;
my $message_p = ( caller(0) )[3];
my $message_t = 'bxSite';
my $logOutput = Output->new(
error => 0,
logfile => $self->logfile,
debug => $self->debug
);
$logOutput->log_data(
"$message_p: enable backup for kernel_name=$kernel_name");
# get cron info
my $task_backup_v5 = '/opt/webdir/bin/bx_backup.sh';
my $task_backup_v4 = '/home/bitrix/backup/scripts/bxbackup.sh';
my $task_crontab = '/etc/crontab';
my $task_backup_dir = '/home/bitrix/backup';
my $task_archive_dir = catfile( $task_backup_dir, 'archive' );
my $task_user = 'bitrix';
my $task_crontab_bak = '/etc/crontab.bak';
# this task can do replace for backup and we need temporary file
open( my $hb, ">$task_crontab_bak" )
or return Output->new(
error => 1,
message => "Cannot open $task_crontab_bak: $!",
);
open( my $hc, "$task_crontab" )
or return Output->new(
error => 1,
message => "Cannot open $task_crontab: $!",
);
my $is_found = 0;
while (<$hc>) {
s/^\s+//;
s/\s+$//;
my $str = $_;
if ( $str =~ /^$/ || $str =~ /^#/ ) {
print $hb $str, "\n";
next;
}
# min hour day month weekday user script site_name site_backup_folder
# new version has onw script with site_name and folder defined
if (
m:^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+\S+\s+$task_backup_v5\s+($kernel_name)\s+(\S+):
)
{
$str =
"$cron_min $cron_hour $cron_day $cron_month $cron_wday $task_user $task_backup_v5 $kernel_name $task_archive_dir";
$is_found = 1;
$logOutput->log_data(
"$message_p: found backup v5 for $kernel_name; replace it");
}
# old backup definitions
# min hour day month weekday user test -f script_name
if (
m|^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+\S+\s+test\s+\-f\s+$task_backup_v4|
)
{
$str =
"$cron_min $cron_hour $cron_day $cron_month $cron_wday $task_user $task_backup_v5 $kernel_name $task_archive_dir";
$is_found = 1;
$logOutput->log_data(
"$message_p: found backup v4 for $kernel_name; replace it");
}
print $hb $str, "\n";
}
if ( $is_found == 0 ) {
print $hb
"$cron_min $cron_hour $cron_day $cron_month $cron_wday $task_user $task_backup_v5 $kernel_name $task_archive_dir\n";
$is_found = 1;
$logOutput->log_data(
"$message_p: not found backup for $kernel_name; create new one");
}
close $hc;
close $hb;
# change access rights
if ( !-d $task_backup_dir ) { mkdir $task_backup_dir; }
if ( !-d $task_archive_dir ) { mkdir $task_archive_dir; }
my ( $task_login, $task_pass, $task_uid, $task_guid ) = getpwnam($task_user)
or return Output->new(
error => 1,
message => "User $task_user not found in password file",
);
chown $task_uid, $task_guid, $task_backup_dir, $task_archive_dir;
unlink $task_crontab;
rename $task_crontab_bak, $task_crontab;
# restart cron service
my $cron_cmd = qq(/sbin/service crond restart 1>/dev/null 2>/dev/null);
system($cron_cmd) == 0
or return Output->new(
error => 1,
message => "Failed restart crond service"
);
return $self->listSite( filters => { DBName => $kernel_name } );
}
# disable backup cron task for dbname
sub disableBackupForDB {
my $self = shift;
my $kernel_name = shift;
my $message_p = ( caller(0) )[3];
my $message_t = 'bxSite';
# get cron info
my $task_backup_v5 = '/opt/webdir/bin/bx_backup.sh';
my $task_backup_v4 = '/home/bitrix/backup/scripts/bxbackup.sh';
my $task_crontab = '/etc/crontab';
my $task_crontab_bak = '/etc/crontab.bak';
# this task can do replace for backup and we need temporary file
open( my $hb, ">$task_crontab_bak" )
or return Output->new(
error => 1,
message => "Cannot open $task_crontab_bak: $!",
);
open( my $hc, "$task_crontab" )
or return Output->new(
error => 1,
message => "Cannot open $task_crontab: $!",
);
my $is_found = 0;
while (<$hc>) {
s/^\s+//;
s/\s+$//;
my $str = $_;
if ( $str =~ /^$/ || $str =~ /^#/ ) {
print $hb $str, "\n";
next;
}
# min hour day month weekday user script site_name site_backup_folder
# new version has onw script with site_name and folder defined
if (
m:^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+\S+\s+$task_backup_v5\s+($kernel_name)\s+(\S+):
)
{
$is_found = 1;
next;
}
# old backup definitions
# min hour day month weekday user test -f script_name
if (
m|^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+\S+\s+test\s+\-f\s+$task_backup_v4|
)
{
$is_found = 1;
next;
}
print $hb $str, "\n";
}
close $hc;
close $hb;
unlink $task_crontab;
rename $task_crontab_bak, $task_crontab;
return $self->listSite( filters => { DBName => $kernel_name } );
}
# add or delete web role from server;
# in case creation second server in group with web role => create balancer
sub changeHostForWebCluster {
my ( $self, $host1, $action, $fstype ) = @_;
my $p = Pool->new( debug => $self->debug, );
my $get_hi = $p->get_inventory_hostname($host1);
return $get_hi if ( $get_hi->is_error );
my $server_name = $get_hi->data->[1];
my $message_p = ( caller(0) )[3];
my $message_t = 'bxSite';
my $logOutput = Output->new(
error => 0,
logfile => $self->logfile,
debug => $self->debug
);
if ( $action !~ /^(create_web|delete_web|web1|web2)$/ ) {
return Output->new(
error => 1,
message =>
"$message_p: action can hold 'create_web', 'delete_web', 'web1' or 'web2' values"
);
}
# csync or lsync
if ( not defined $fstype ) {
$fstype = "lsync";
}
$logOutput->log_data("$message_p: $action for $server_name");
my $host = Host->new( host => $server_name );
my $is_host_in_pool = $host->host_in_pool();
if ( $is_host_in_pool->is_error ) {
return $is_host_in_pool;
}
# test sites; scale and cluster modules + number kernels sites
if ( ( $action eq "web2" ) or ( $action eq "create_web" ) ) {
my $testClusterConfig = $self->testClusterConfig();
my $test_data = $testClusterConfig->get_data->[1];
if ( ( $test_data->{'test_kernels'} > 1 )
|| ( $test_data->{'test_without_scale'} > 0 )
|| ( $test_data->{'test_without_cluster'} > 0 ) )
{
return Output->new(
error => 1,
message =>
"$message_p: Found conditions when web-cluster configuration is disabled",
);
}
}
# create cluster and replica passwords
my $mysql_group = bxMysql->new();
my $mysql_options = $mysql_group->mysql_cluster_options;
$mysql_options->{'group'} = 'mysql';
# run ansible playbook
my $po = Pool->new();
my $ansData = $po->ansible_conf;
my $cmd_play = $ansData->{'playbook'};
my $cmd_conf = catfile( $ansData->{'base'}, "web.yml" );
$mysql_options->{manage_web} =
$action; # create_web, delete_web, web1 or web2
$mysql_options->{fstype} = $fstype;
# run playbook
if ( $action eq "delete_web" ) {
$mysql_options->{'deleted_web_server'} = $server_name;
}
else {
if ( $action eq "web1" ) {
delete $mysql_options->{cluster_password_file}
if ( defined $mysql_options->{cluster_password_file} );
delete $mysql_options->{replica_password_file}
if ( defined $mysql_options->{replica_password_file} );
}
$mysql_options->{'new_web_server'} = $server_name;
}
#print Dumper($mysql_options);
#exit;
my $dh = bxDaemon->new(
debug => $self->debug,
task_cmd => qq($cmd_play $cmd_conf)
);
my $created_process =
$dh->startAnsibleProcess( 'web_cluster', $mysql_options );
return $created_process;
}
# create site
# site_options define parametrs for new site
# mandatory:
# ServerName
# SiteInstall
sub CreateSite {
my $self = shift;
my $site_options = shift;
my $message_p = ( caller(0) )[3];
my $message_t = 'bxSite';
my $logOutput = Output->new(
error => 0,
logfile => $self->logfile,
debug => $self->debug
);
# set option to default values
if ( not defined $site_options->{'SiteInstall'} ) {
$site_options->{'SiteInstall'} = "link";
}
# test mandatory options
if ( not defined $site_options->{'ServerName'} ) {
if ( not defined $site_options->{'DocumentRoot'} ) {
return Output->new(
error => 1,
message =>
"$message_p: for site creation you must defined site_name or site_dir"
);
}
else {
if ( $site_options->{'SiteInstall'} =~ /^ext_kernel$/ ) {
$site_options->{'ServerName'} =
basename( $site_options->{'DocumentRoot'} );
}
else {
return Output->new(
error => 1,
message => "$message_p: for creation site "
. $site_options->{'SiteInstall'}
. " you must defined site_name"
);
}
}
}
# charset option test
if ( defined $site_options->{'SiteCharset'} ) {
if ( $site_options->{'SiteCharset'} !~ /^(utf-8|windows-1251)$/i ) {
return Output->new(
error => 1,
message => "$message_p: charset="
. $site_options->{'SiteCharset'}
. "; it can contain only 'utf-8' or 'windows-1251'",
);
}
$site_options->{'SiteCharset'} =~ tr/A-Z/a-z/;
}
# transform site_options to playbook options
my $p_opts = {
manage_web => "create_site",
web_site_name => $site_options->{'ServerName'},
web_site_type => $site_options->{'SiteInstall'},
};
if ( $site_options->{'DocumentRoot'} ) {
$p_opts->{'web_site_dir'} = $site_options->{'DocumentRoot'};
}
if ( $site_options->{'DBName'} ) {
$p_opts->{'web_site_db'} = $site_options->{'DBName'};
}
if ( $site_options->{'DBLogin'} ) {
$p_opts->{'web_site_dbuser'} = $site_options->{'DBLogin'};
}
if ( $site_options->{'DBPassword'} ) {
if ( not defined $site_options->{DBPasswordFile} ) {
my $tmp = File::Temp->new(
TEMPLATE => '.siteXXXXXXXX',
UNLINK => 0,
DIR => $TMPDIR,
);
print $tmp $site_options->{'DBPassword'};
$site_options->{DBPasswordFile} = $tmp->filename;
}
}
if ( $site_options->{'SiteKernelName'} ) {
$p_opts->{'web_kernel_site'} = $site_options->{'SiteKernelName'};
}
if ( $site_options->{'SiteKernelDir'} ) {
$p_opts->{'web_kernel_root'} = $site_options->{'SiteKernelDir'};
}
if ( $site_options->{'SiteCharset'} ) {
$p_opts->{'bitrix_site_charset'} = $site_options->{'SiteCharset'};
}
if ( $site_options->{'DBPasswordFile'} ) {
$p_opts->{'web_site_dbpass_file'} = $site_options->{'DBPasswordFile'};
}
# create cron settings for site or not
if ( $site_options->{'CronTask'} ) {
$p_opts->{'web_site_cron'} = 'enable';
}
else {
$p_opts->{'web_site_cron'} = 'disable';
}
# push
if ( $site_options->{NodeJSPush} ) {
$p_opts->{NodeJSPush} = "enable";
}
else {
$p_opts->{NodeJSPush} = "disable";
}
$logOutput->log_data( "$message_p: start creation of new site="
. $site_options->{'ServerName'} . "type="
. $site_options->{'SiteInstall'} );
# create site by ansible task
my $po = Pool->new();
my $ansData = $po->ansible_conf;
my $cmd_play = $ansData->{'playbook'};
my $cmd_conf = catfile( $ansData->{'base'}, "web.yml" );
my $dh = bxDaemon->new(
debug => $self->debug,
task_cmd => qq($cmd_play $cmd_conf)
);
my $created_process = $dh->startAnsibleProcess( "site_create", $p_opts );
return $created_process;
}
# delete site
# site_options define parametrs for new site
# mandatory:
# ServerName or DocumentRoot
sub DeleteSite {
my $self = shift;
my $site_options = shift;
my $message_p = ( caller(0) )[3];
my $message_t = 'bxSite';
my $logOutput = Output->new(
error => 0,
logfile => $self->logfile,
debug => $self->debug
);
# test mandatory options
if ( ( not defined $site_options->{'ServerName'} )
&& ( not defined $site_options->{'DocumentRoot'} ) )
{
return Output->new(
error => 1,
message =>
"$message_p: for site creation you must defined site_name or site_dir"
);
}
# transform site_options to playbook options
my $p_opts = { manage_web => "delete_site", };
my $log_str = "delete site";
if ( $site_options->{'DocumentRoot'} ) {
$p_opts->{'web_site_dir'} = $site_options->{'DocumentRoot'};
$log_str .= " site_dir=" . $site_options->{'DocumentRoot'};
}
if ( $site_options->{'ServerName'} ) {
$p_opts->{'web_site_name'} = $site_options->{'ServerName'};
$log_str .= " site_name=" . $site_options->{'ServerName'};
}
$logOutput->log_data("$message_p: $log_str");
# create site by ansible task
my $po = Pool->new();
my $ansData = $po->ansible_conf;
my $cmd_play = $ansData->{'playbook'};
my $cmd_conf = catfile( $ansData->{'base'}, "web.yml" );
my $dh = bxDaemon->new(
debug => $self->debug,
task_cmd => qq($cmd_play $cmd_conf)
);
my $created_process = $dh->startAnsibleProcess( "site_delete", $p_opts );
return $created_process;
}
# create NTLM settings for all sites on the server
sub changeNTLMForSite {
my ( $self, $domain, $fqdn, $ads, $login, $host, $dbname, $password_file )
= @_;
my $message_p = ( caller(0) )[3];
my $message_t = 'bxSite';
my $logOutput = Output->new(
error => 0,
logfile => $self->logfile,
debug => $self->debug
);
if ( !$domain
|| !$fqdn
|| !$ads
|| !$login
|| !$password_file
|| !$dbname )
{
return Output->new(
error => 1,
message =>
"$message_p: Options ntlm_domain= ntlm_fqdn= ntlm_ads= and password_file= dbname= are mandatory",
);
}
my $p_opts;
$p_opts->{'manage_web'} = 'ntlm_on';
$p_opts->{'ntlm_name'} = $domain;
$p_opts->{'ntlm_fqdn'} = $fqdn;
$p_opts->{'ntlm_dps'} = $ads;
$p_opts->{'manage_kernel'} = $dbname;
$p_opts->{'ntlm_user'} = $login;
$p_opts->{'ntlm_pass_file'} = $password_file;
if ($host) { $p_opts->{'ntlm_host'} = $host; }
# create ansible task
my $po = Pool->new();
my $ansData = $po->ansible_conf;
my $cmd_play = $ansData->{'playbook'};
my $cmd_conf = catfile( $ansData->{'base'}, "web.yml" );
my $dh = bxDaemon->new(
debug => $self->debug,
task_cmd => qq($cmd_play $cmd_conf)
);
$logOutput->log_data(
"$message_p: start create ntlm settings for host and db="
. $p_opts->{'manage_kernel'} );
my $created_process = $dh->startAnsibleProcess( 'change_ntlm', $p_opts );
return $created_process;
}
# update NTLM settings for all sites on the server
sub updateNTLMForSite {
my ( $self, $dbname ) = @_;
my $message_p = ( caller(0) )[3];
my $message_t = 'bxSite';
my $logOutput = Output->new(
error => 0,
logfile => $self->logfile,
debug => $self->debug
);
if ( !$dbname ) {
return Output->new(
error => 1,
message => "$message_p: Options dbname= are mandatory",
);
}
my $p_opts;
$p_opts->{'manage_web'} = 'ntlm_on';
$p_opts->{'manage_kernel'} = $dbname;
# start playbook
my $po = Pool->new();
my $ansData = $po->ansible_conf;
my $cmd_play = $ansData->{'playbook'};
my $cmd_conf = catfile( $ansData->{'base'}, "web.yml" );
my $dh = bxDaemon->new(
debug => $self->debug,
task_cmd => qq($cmd_play $cmd_conf)
);
$logOutput->log_data(
"$message_p: start create ntlm settings for host and db="
. $p_opts->{'manage_kernel'} );
my $created_process = $dh->startAnsibleProcess( 'change_ntlm', $p_opts );
return $created_process;
}
#NTLM Status
sub getNTLMServerStatus {
my ($self) = @_;
my $message_p = ( caller(0) )[3];
my $message_t = __PACKAGE__;
my $ntlm_options = {
'LDAPServer' => '',
'LDAPPort' => '',
'Realm' => '',
'BindPath' => '',
'KDCServer' => '',
'TimeOffset' => '',
'Status' => 'not_configured',
};
my $net_cmd = qq(/usr/bin/net ads info);
open( my $nh, "-|", "$net_cmd 2>/dev/null" )
or return Output->new(
error => 1,
message => "$message_p: command \`$net_cmd\` return error: $!",
);
while ( my $line = <$nh> ) {
chomp($line);
if ( $line =~ /^LDAP server name:\s+(\S+)/ ) {
$ntlm_options->{'LDAPServer'} = $1;
}
if ( $line =~ /^LDAP port:\s+(\S+)/ ) {
$ntlm_options->{'LDAPPort'} = $1;
}
if ( $line =~ /^Realm:\s+(\S+)/ ) {
$ntlm_options->{'Realm'} = $1;
}
if ( $line =~ /^Bind Path:\s+(\S+)/ ) {
$ntlm_options->{'BindPath'} = $1;
}
if ( $line =~ /^KDC server:\s+(\S+)/ ) {
$ntlm_options->{'KDCServer'} = $1;
}
if ( $line =~ /^Server time offset:\s+(\S+)/ ) {
$ntlm_options->{'TimeOffset'} = $1;
}
}
close $nh;
my $is_empty = 0;
foreach my $k ( keys %$ntlm_options ) {
if ( $ntlm_options->{$k} =~ /^$/ ) { $is_empty = 1 }
}
if ( $is_empty == 0 ) { $ntlm_options->{'Status'} = 'configured'; }
return Output->new(
error => 0,
data => [ 'NTLMStatus', $ntlm_options ]
);
}
# enable|disable php extension
sub php_extension {
my ( $self, $ext, $type ) = @_;
my $message_p = ( caller(0) )[3];
my $message_t = 'bxSite';
my $logOutput = Output->new(
error => 0,
logfile => $self->logfile,
debug => $self->debug
);
if ( !$ext || !$type ) {
return Output->new(
error => 1,
message => "$message_p: Options ext= and type= are mandatory",
);
}
my $p_opts;
$p_opts->{'extension'} = $ext;
$p_opts->{'type'} = $type;
$p_opts->{'manage_web'} = "php_extension";
# start playbook
my $po = Pool->new();
my $ansData = $po->ansible_conf;
my $cmd_play = $ansData->{'playbook'};
my $cmd_conf = catfile( $ansData->{'base'}, "web.yml" );
my $dh = bxDaemon->new(
debug => $self->debug,
task_cmd => qq($cmd_play $cmd_conf)
);
$logOutput->log_data( "$message_p: change settiongs for php extension=="
. $p_opts->{'extension'} );
my $created_process = $dh->startAnsibleProcess( 'php_ext', $p_opts );
return $created_process;
}
# configure Push Server
sub configurePushServer {
my ( $self, $host, $manage ) = @_;
my $message_p = ( caller(0) )[3];
my $message_t = 'bxPush';
my $p = Pool->new( debug => $self->debug, );
my $get_hi = $p->get_inventory_hostname($host);
return $get_hi if ( $get_hi->is_error );
my $hostname = $get_hi->data->[1];
my $logOutput = Output->new(
error => 0,
logfile => $self->logfile,
debug => $self->debug
);
if ( !$manage ) {
return Output->new(
error => 1,
message =>
"$message_p: Options hostname= and action= are mandatory",
);
}
$manage =~ s/^push_//;
my $p_opts;
$p_opts->{'hostname'} = $hostname;
$p_opts->{'manage'} = $manage;
# start playbook
my $po = Pool->new();
my $ansData = $po->ansible_conf;
my $cmd_play = $ansData->{'playbook'};
my $cmd_conf = catfile( $ansData->{'base'}, "push-server.yml" );
my $dh = bxDaemon->new(
debug => $self->debug,
task_cmd => qq($cmd_play $cmd_conf)
);
$logOutput->log_data(
"$message_p: $manage Push Nodejs server on hostname="
. $p_opts->{'hostname'} );
my $created_process = $dh->startAnsibleProcess( 'pushserver', $p_opts );
return $created_process;
}
sub testListSites {
my ( $self, $opts ) = @_;
my ( @site_names, $sites_filter, @special_sites );
my $message_p = ( caller(0) )[3];
my $message_t = __PACKAGE__;
if ( not defined $opts->{site_names} ) {
return Output->new(
error => 1,
message => "Sites list cannot be empty",
);
}
$opts->{site_names} =~ s/\s+//g;
my $configure_push_server = 0;
if ( $opts->{site_names} =~ /push-server/ ) {
$opts->{site_names} =~ s/^push-server,(.+)$/$1/;
$opts->{site_names} =~ s/(.+),push-server,(.+)$/$1,$2/;
$opts->{site_names} =~ s/(.+),push-server$/$1/;
$opts->{site_names} =~ s/^push-server$//;
push @special_sites, "push-server";
}
#print Dumper(\@special_sites);
# any special sites is updated by existen site; default-site is used by default
if ( $opts->{site_names} eq "" ) {
#$opts->{site_names} = "default";
return Output->new(
error => 0,
data =>
[ "site_names", \@site_names, $sites_filter, \@special_sites ],
);
}
# create list of sites
if ( $opts->{site_names} =~ /,/ ) {
@site_names = split( ",", $opts->{site_names} );
$sites_filter = join( '|', @site_names );
}
else {
push @site_names, $opts->{site_names};
$sites_filter = $opts->{site_names};
}
#print Dumper(\@site_names);
my $l = $self->listAllSite();
return $l if ( $l->is_error );
my $sites = $l->data->[1];
# test site list
foreach my $site_name (@site_names) {
if ( not defined $sites->{$site_name} ) {
return Output->new(
error => 1,
message =>
"$message_p: Not found site $site_name on the server",
);
}
my $site_install = $sites->{$site_name}->{'SiteInstall'};
my $site_status = $sites->{$site_name}->{'SiteStatus'};
if ( ( $site_install =~ /^$/ ) && ( $site_status =~ /^error$/ ) ) {
return Output->new(
error => 1,
message =>
"$message_p: Not found site $site_name on the server",
);
}
if ( $site_install =~ /^ext_kernel$/ ) {
return Output->new(
error => 1,
message =>
"$message_p: Site=$site_name hasn't nginx configs. Nothing to do.",
);
}
#print "$site_name $site_install $site_status\n";
}
return Output->new(
error => 0,
data => [ "site_names", \@site_names, $sites_filter, \@special_sites ],
);
}
sub statusCerts {
my ( $self, $opts ) = @_;
#print Dumper($opts);
my %certs;
my $cnt = 0;
my $message_p = ( caller(0) )[3];
my $message_t = __PACKAGE__;
if ( defined $opts->{cert} && ( $opts->{cert} !~ /^\// ) ) {
$opts->{cert} = catfile( "/etc/nginx", $opts->{cert} );
}
# sites
my $l = $self->listAllSite();
return $l if ( $l->is_error );
my $sites = $l->data->[1];
foreach my $s ( keys %$sites ) {
my $info = $sites->{$s};
if ( ( $info->{SiteInstall} eq 'kernel' )
|| ( $info->{SiteInstall} eq 'link' ) )
{
if ( defined $opts->{cert} ) {
if ( $info->{HTTPSCert} ne $opts->{cert} ) {
next;
}
}
my $cert_path = $info->{HTTPSCert};
if ( not defined $certs{$cert_path} ) {
$certs{$cert_path}->[0] = $s;
$cnt++;
}
else {
push @{ $certs{$cert_path} }, $s;
}
}
}
# push-stream-server
my $p = Pool->new();
my $pi = $p->get_ansible_data();
return $pi if ( $pi->is_error );
my $is_nginx_push = 1;
foreach my $h ( keys %{ $pi->{data}->[1] } ) {
my $hi = $pi->{data}->[1]->{$h};
if ( grep( /^push$/, keys %{ $hi->{groups} } ) ) {
$is_nginx_push = 0;
}
}
if ($is_nginx_push) {
my $push_config = '/etc/nginx/bx/conf/ssl-push.conf';
if ( -f $push_config ) {
my $hh;
my $cert_path = undef;
open( $hh, '<', $push_config )
or return Output->new(
error => 0,
data => [
"site_certs",
{
certs => \%certs,
cnt => $cnt
}
],
);
while (<$hh>) {
s/^\s+//;
s/\s+$//;
next if (/^#/);
next if (/^$/);
my $str = $_;
if ( $str =~ /^ssl_certificate\s+(\S+)/ ) {
$cert_path = $1;
$cert_path =~ s/['"]//g;
$cert_path =~ s/;$//;
}
}
close $hh;
#print "cert_path=> $cert_path\n";
if ( defined $cert_path ) {
my $add = 0;
if ( defined $opts->{cert} ) {
if ( $cert_path eq $opts->{cert} ) {
$add = 1;
}
}
else {
$add = 1;
}
if ($add) {
if ( not defined $certs{$cert_path} ) {
$certs{$cert_path}->[0] = 'push-server';
$cnt++;
}
else {
push @{ $certs{$cert_path} }, 'push-server';
}
}
}
}
}
return Output->new(
error => 0,
data => [
"site_certs",
{
certs => \%certs,
cnt => $cnt
}
],
);
}
sub configureLE {
my ( $self, $opts ) = @_;
my ( @site_names, @dns, $email, $sites_filter, @special_sites );
my $message_p = ( caller(0) )[3];
my $message_t = __PACKAGE__;
my $logOutput = Output->new(
error => 0,
logfile => $self->logfile,
debug => $self->debug
);
my $test_sites = $self->testListSites($opts);
return $test_sites if ( $test_sites->is_error );
@site_names = @{ $test_sites->data->[1] };
$sites_filter = $test_sites->data->[2];
@special_sites = @{ $test_sites->data->[3] };
my $sites_cnt = @site_names;
my $specs_cnt = @special_sites;
# test options
foreach my $k ( "dns", "email" ) {
if ( not defined $opts->{$k} ) {
return Output->new(
error => 1,
messages => "$message_p: Option $k cannot be empty",
);
}
}
if ( $opts->{dns} =~ /,/ ) {
@dns = map { s/\s+//g; $_ } split( ",", $opts->{dns} );
}
else {
$dns[0] = $opts->{dns};
}
my $a_opts = {
dns_names => \@dns,
sites_cnt => $sites_cnt,
email => $opts->{email},
manage_web => 'configure_le',
specs_cnt => $specs_cnt,
};
if ($sites_cnt) {
$a_opts->{site_names} = \@site_names;
$a_opts->{sites_filter} = $sites_filter;
}
if ( $specs_cnt > 0 && grep /^push-server$/, @special_sites ) {
$a_opts->{push_server} = 1;
}
$logOutput->log_data( "$message_p: configure LE certificate fo sites "
. $opts->{site_names} );
# create site by ansible task
my $po = Pool->new();
my $ansData = $po->ansible_conf;
my $cmd_play = $ansData->{'playbook'};
my $cmd_conf = catfile( $ansData->{'base'}, "web.yml" );
my $dh = bxDaemon->new(
debug => $self->debug,
task_cmd => qq($cmd_play $cmd_conf)
);
my $created_process =
$dh->startAnsibleProcess( "site_certificate", $a_opts );
return $created_process;
}
sub configureCert {
my ( $self, $opts ) = @_;
my ( @site_names, $sites_filter, @special_sites );
my $message_p = ( caller(0) )[3];
my $message_t = __PACKAGE__;
my $logOutput = Output->new(
error => 0,
logfile => $self->logfile,
debug => $self->debug
);
my $test_sites = $self->testListSites($opts);
return $test_sites if ( $test_sites->is_error );
@site_names = @{ $test_sites->data->[1] };
$sites_filter = $test_sites->data->[2];
@special_sites = @{ $test_sites->data->[3] };
my $sites_cnt = @site_names;
my $specs_cnt = @special_sites;
# test options
foreach my $k ( "private_key", "certificate" ) {
if ( not defined $opts->{$k} ) {
return Output->new(
error => 1,
messages => "$message_p: Option $k cannot be empty",
);
}
}
if ( not defined $opts->{certificate_chain} ) {
$opts->{certificate_chain} = "";
}
my $nginx_path = "/etc/nginx/certs";
foreach my $opt ( "certificate_chain", "certificate", "private_key" ) {
if ( ( $opt eq "certificate_chain" ) && ( $opts->{$opt} =~ /^$/ ) ) {
delete $opts->{$opt};
next;
}
if ( !-f $opts->{$opt} ) {
if ( -f catfile( $nginx_path, $opts->{$opt} ) ) {
$opts->{$opt} = catfile( $nginx_path, $opts->{$opt} );
}
else {
return Output->new(
error => 1,
message =>
"$message_p: Not found $opt=$opts->{$opt} on the server",
);
}
}
}
$opts->{manage_web} = "configure_cert";
$opts->{sites_cnt} = $sites_cnt;
$opts->{specs_cnt} = $specs_cnt;
if ($sites_cnt) {
$opts->{site_names} = \@site_names;
$opts->{sites_filter} = $sites_filter;
}
if ( $specs_cnt > 0 && grep /^push-server$/, @special_sites ) {
$opts->{push_server} = 1;
}
$logOutput->log_data( "$message_p: configure own certificate fo sites "
. $opts->{site_names} );
# create site by ansible task
my $po = Pool->new();
my $ansData = $po->ansible_conf;
my $cmd_play = $ansData->{'playbook'};
my $cmd_conf = catfile( $ansData->{'base'}, "web.yml" );
my $dh = bxDaemon->new(
debug => $self->debug,
task_cmd => qq($cmd_play $cmd_conf)
);
my $created_process = $dh->startAnsibleProcess( "site_certificate", $opts );
return $created_process;
}
sub resetCert {
my ( $self, $opts ) = @_;
my ( @site_names, $sites_filter, @special_sites );
my $message_p = ( caller(0) )[3];
my $message_t = __PACKAGE__;
my $logOutput = Output->new(
error => 0,
logfile => $self->logfile,
debug => $self->debug
);
my $test_sites = $self->testListSites($opts);
return $test_sites if ( $test_sites->is_error );
@site_names = @{ $test_sites->data->[1] };
$sites_filter = $test_sites->data->[2];
@special_sites = @{ $test_sites->data->[3] };
my $sites_cnt = @site_names;
my $specs_cnt = @special_sites;
$opts->{manage_web} = "reset_cert";
$opts->{sites_cnt} = $sites_cnt;
$opts->{specs_cnt} = $specs_cnt;
if ($sites_cnt) {
$opts->{site_names} = \@site_names;
$opts->{sites_filter} = $sites_filter;
}
if ( $specs_cnt > 0 && grep /^push-server$/, @special_sites ) {
$opts->{push_server} = 1;
}
$logOutput->log_data(
"$message_p: reset certificate configuration for sites "
. ( $opts->{site_names} ) ? $opts->{site_names}
: " " . ( $opts->{push_server} ) ? $opts->{push_server}
: ""
);
# create site by ansible task
my $po = Pool->new();
my $ansData = $po->ansible_conf;
my $cmd_play = $ansData->{'playbook'};
my $cmd_conf = catfile( $ansData->{'base'}, "web.yml" );
my $dh = bxDaemon->new(
debug => $self->debug,
task_cmd => qq($cmd_play $cmd_conf)
);
my $created_process = $dh->startAnsibleProcess( "site_certificate", $opts );
return $created_process;
}
# nginx_custom_site_settings|dbconn_temp_files
sub siteCustomSettings {
my ( $self, $opt, $val ) = @_;
my $message_p = ( caller(0) )[3];
my $message_t = __PACKAGE__;
my $logOutput = Output->new(
error => 0,
logfile => $self->logfile,
debug => $self->debug
);
my $a_opts = {
option => $opt,
value => $val,
manage_web => 'custom_configs',
};
# create site by ansible task
my $po = Pool->new();
my $ansData = $po->ansible_conf;
my $cmd_play = $ansData->{'playbook'};
my $cmd_conf = catfile( $ansData->{'base'}, "web.yml" );
my $dh = bxDaemon->new(
debug => $self->debug,
task_cmd => qq($cmd_play $cmd_conf)
);
my $created_process = $dh->startAnsibleProcess( "custom_configs", $a_opts );
return $created_process;
}
sub configureTransformer {
my ( $self, $opts ) = @_;
my $message_p = ( caller(0) )[3];
my $message_t = __PACKAGE__;
my $logOutput = Output->new(
error => 0,
logfile => $self->logfile,
debug => $self->debug
);
if ( not defined $opts->{transformer_host}
|| \not defined $opts->{web_site_name}
|| \not defined $opts->{web_site_dir} )
{
return Output->new(
error => 1,
message => "$message_p: Some mandatory options are missing.",
);
}
if ( not defined $opts->{transformer_domains} ){
$opts->{transformer_domains} = ['localhost', $opts->{web_site_name}];
}else{
if ( $opts->{transformer_domains} =~ /,/ ){
my @transformer_domains = split(',' , $opts->{transformer_domains} );
$opts->{transformer_domains} = \@transformer_domains;
}else{
$opts->{transformer_domains} = [$opts->{transformer_domains}];
}
}
my $host = Host->new( host => $opts->{transformer_host} );
my $is_host_in_pool = $host->host_in_pool();
if ( $is_host_in_pool->is_error ) {
return $is_host_in_pool;
}
my $bx_pool = Pool->new();
my $an_data = $bx_pool->ansible_conf;
my $host_vars_path =
catfile( $an_data->{'host_vars'}, $opts->{transformer_host} );
if ( -f $host_vars_path ){
my $inventory = get_from_yaml($host_vars_path);
if ( $inventory->is_error ){
return $inventory;
}
if (defined $inventory->{redis_password}){
$opts->{redis_password} = $inventory->{redis_password};
}
if (defined $inventory->{redis_root_password}){
$opts->{redis_root_password} = $inventory->{redis_root_password};
}
}
$opts->{is_create} = "true";
if ( not defined $opts->{redis_password} ) {
$opts->{redis_password} = generate_simple_password();
}
if ( not defined $opts->{redis_root_password} ) {
$opts->{redis_root_password} = generate_simple_password();
}
$logOutput->log_data(
"$message_p: configure transformer-service" . $opts->{web_site_name} );
# create site by ansible task
my $po = Pool->new();
my $ansData = $po->ansible_conf;
my $cmd_play = $ansData->{'playbook'};
my $cmd_conf = catfile( $ansData->{'base'}, "transformer.yml" );
my $dh = bxDaemon->new(
debug => $self->debug,
task_cmd => qq($cmd_play $cmd_conf)
);
my $created_process =
$dh->startAnsibleProcess( "configure_transformer", $opts );
return $created_process;
}
sub removeTransformer {
my ( $self, $opts ) = @_;
my $message_p = ( caller(0) )[3];
my $message_t = __PACKAGE__;
my $logOutput = Output->new(
error => 0,
logfile => $self->logfile,
debug => $self->debug
);
if ( not defined $opts->{transformer_host}
|| \not defined $opts->{web_site_name}
|| \not defined $opts->{web_site_dir} )
{
return Output->new(
error => 1,
message => "$message_p: Some mandatory options are missing.",
);
}
$opts->{is_remove} = "true";
$logOutput->log_data(
"$message_p: remove transformer-service" . $opts->{web_site_name} );
# create site by ansible task
my $po = Pool->new();
my $ansData = $po->ansible_conf;
my $cmd_play = $ansData->{'playbook'};
my $cmd_conf = catfile( $ansData->{'base'}, "transformer.yml" );
my $dh = bxDaemon->new(
debug => $self->debug,
task_cmd => qq($cmd_play $cmd_conf)
);
my $created_process =
$dh->startAnsibleProcess( "remove_transformer", $opts );
return $created_process;
}
1;