File: cluster-cron-lib.pl

package info (click to toggle)
webmin-cluster 1.180-1
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 1,124 kB
  • ctags: 97
  • sloc: perl: 8,823; makefile: 85; sh: 22
file content (191 lines) | stat: -rwxr-xr-x 4,646 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# cluster-cron-lib.pl
# XXX environment variables??
#	XXX create script to run, which sets vars and includes input as << ?

do '../web-lib.pl';
&init_config();
require '../ui-lib.pl';
&foreign_require("cron", "cron-lib.pl");
&foreign_require("servers", "servers-lib.pl");

$cluster_cron_cmd = "$module_config_directory/cron.pl";
$jobs_directory = "$module_config_directory/jobs";

# list_cluster_jobs()
# Returns an array of cron jobs that are run on multiple servers
sub list_cluster_jobs
{
local @rv;
foreach $j (&cron::list_cron_jobs()) {
	if ($j->{'user'} eq 'root' &&
	    $j->{'command'} =~ /^$cluster_cron_cmd\s+(\S+)$/) {
		$j->{'cluster_id'} = $1;
		&read_file("$jobs_directory/$1", $j) || next;
		push(@rv, $j);
		}
	}
return @rv;
}

# create_cluster_job(&job)
# Create a new cluster cron job
sub create_cluster_job
{
mkdir($jobs_directory, 0700);
&lock_file("$jobs_directory/$_[0]->{'cluster_id'}");
&write_file("$jobs_directory/$_[0]->{'cluster_id'}", &cluster_params($_[0]));
&unlock_file("$jobs_directory/$_[0]->{'cluster_id'}");
&lock_file("$cron::config{'cron_dir'}/$_[0]->{'user'}");
&cron::create_cron_job($_[0]);
&unlock_file("$cron::config{'cron_dir'}/$_[0]->{'user'}");
}

# modify_cluster_job(&job)
# Update an existing cluster cron job
sub modify_cluster_job
{
&lock_file("$jobs_directory/$_[0]->{'cluster_id'}");
&write_file("$jobs_directory/$_[0]->{'cluster_id'}", &cluster_params($_[0]));
&unlock_file("$jobs_directory/$_[0]->{'cluster_id'}");
&lock_file($_[0]->{'file'});
&cron::change_cron_job($_[0]);
&unlock_file($_[0]->{'file'});
}

# delete_cluster_job(&job)
sub delete_cluster_job
{
&lock_file("$jobs_directory/$_[0]->{'cluster_id'}");
unlink("$jobs_directory/$_[0]->{'cluster_id'}");
&unlock_file("$jobs_directory/$_[0]->{'cluster_id'}");
&lock_file($_[0]->{'file'});
&cron::delete_cron_job($_[0]);
&unlock_file($_[0]->{'file'});
}

sub cluster_params
{
local %rv;
foreach $k (keys %{$_[0]}) {
	$rv{$k} = $_[0]->{$k} if ($k =~ /^cluster_/);
	}
return \%rv;
}

# run_cluster_job(&job, &callback)
# Runs a cluster cron job on all configured servers, and for each result calls
# the callback function with parameters 0/1, a server object, and the output
# or error message
sub run_cluster_job
{
local @rv;
local $func = $_[1];

# Work out which servers to run on
local @servers = &servers::list_servers();
local @groups = &servers::list_all_groups(\@servers);
local @run;
foreach $s (split(/\s+/, $_[0]->{'cluster_server'})) {
	if ($s =~ /^group_(.*)$/) {
		# All members of a group
		($group) = grep { $_->{'name'} eq $1 } @groups;
		foreach $m (@{$group->{'members'}}) {
			push(@run, grep { $_->{'host'} eq $m && $_->{'user'} }
					@servers);
			}
		}
	elsif ($s eq '*') {
		# This server
		push(@run, ( { 'desc' => $text{'edit_this'} } ));
		}
	else {
		# A single remote server
		push(@run, grep { $_->{'host'} eq $s } @servers);
		}
	}
@run = &unique(@run);

# Setup error handler for down hosts
sub inst_error
{
$inst_error_msg = join("", @_);
}
&remote_error_setup(\&inst_error);

# Create a local temp file containing input
local $ltemp = &tempname();
open(TEMP, ">$ltemp");
local $inp = $_[0]->{'cluster_input'};
$inp =~ s/\\%/\0/g;
@lines = split(/%/, $inp);
foreach $l (@lines) {
	$l =~ s/\0/%/g;
	print TEMP $l,"\n";
	}
close(TEMP);

# Run one each one in parallel and display the output
$p = 0;
foreach $s (@run) {
	local ($rh = "READ$p", $wh = "WRITE$p");
	pipe($rh, $wh);
	select($wh); $| = 1; select(STDOUT);
	if (!fork()) {
		# Run the command in a subprocess
		close($rh);

		&remote_foreign_require($s->{'host'}, "webmin",
					"webmin-lib.pl");
		if ($inst_error_msg) {
			# Failed to contact host ..
			print $wh &serialise_variable([ 0, $inst_error_msg ]);
			exit;
			}

		# Send any input to a temp file
		local $rtemp = &remote_write($s->{'host'}, $ltemp);

		# Run the command and capture output
		local $q = quotemeta($_[0]->{'cluster_command'});
		local $rv;
		if ($_[0]->{'cluster_user'} eq 'root') {
			$rv = &remote_eval($s->{'host'}, "webmin",
			    "\$x=`($q) <$rtemp 2>&1`");
			}
		else {
			$q = quotemeta($q);
			$rv = &remote_eval($s->{'host'}, "webmin",
			    "\$x=`(su $_[0]->{'cluster_user'} -c $q) <$rtemp 2>&1`");
			}
		&remote_eval($s->{'host'}, "unlink('$rtemp')");

		print $wh &serialise_variable([ 1, $rv ]);
		close($wh);
		exit;
		}
	close($wh);
	$p++;
	}

# Get back all the results
$p = 0;
foreach $s (@run) {
	local $rh = "READ$p";
	local $line = <$rh>;
	close($rh);
	local $rv = &unserialise_variable($line);

	if (!$line) {
		&$func(0, $s, "Unknown reason");
		}
	else {
		&$func($rv->[0], $s, $rv->[1]);
		}
	$p++;
	}
unlink($ltemp);
return @run;
}

1;