source: ProjectBuilder/projects/casparbuster/devel/bin/cbusterize@ 1665

Last change on this file since 1665 was 1665, checked in by Bruno Cornec, 12 years ago
  • cbusemachines is also obsolete for cbusterize
  • Property svn:executable set to *
File size: 12.4 KB
Line 
1#!/usr/bin/perl -w
2#
3=head1 NAME
4
5cbusterize - Creates the correct CasparBuster structure in your CMS environment
6
7=head1 SYNOPSIS
8
9cbusterize [options] [--source /path/to/file/to/CasparBusterize | --plugin PluginName]
10
11 Options:
12 --debug |-d debug mode
13 --help |-h brief help message
14 --man full documentation
15 --force |-f force copy of files, even if they exist
16 --source |-s <file/dir> directory or files to copy in the CasparBuster tree (',' separated if many)
17 --plugin |-p <plugin name> plugin defining what to copy in the CasparBuster tree (',' separated if many)
18 --machine|-m <machine> machine to consider in the subtree
19
20=head1 OPTIONS
21
22=over 4
23
24=item B<--debug>
25
26Enter debug mode. This will print what would be done. No commands are executed,
27so this is safe to use when testing.
28
29=item B<--help>
30
31Print a brief help message and exits.
32
33=item B<--man>
34
35Prints the manual page and exits.
36
37=item B<--machine> I<machine name>
38
39Specify the machine to consider when dealing with the CasparBuster structure.
40The file will be taken from this machine, and a subdirectory named after the machine
41will be used under the basedir to host the directory structure to manage
42
43=item B<--source> I<path>
44
45Specify the path to the source file or directory to manage with CasparBuster. Multiple paths can be specified separated by ','.
46
47=item B<--plugin> I<name>
48
49Specify the name of the plugin to manage with CasparBuster. Multiple plugins can be specified separated by ','.
50A plugin defines a set of files (with their mode and owner), a set of directories (with their mode and owner) and a set of scripts to launch once the files are copied remotely.
51
52=back
53
54=head1 DESCRIPTION
55
56Creates a directory under the machine dir passed as parameter in working
57directory or directory passed as parameter named like the last path
58element of the parameter, and creates the standard CasparBuster setup
59that refers to the parameter path in the new directory, and configuration
60files when possible. It also copies the original config file into the new dir.
61Is reasonably picky about path names, tries to avoid common errors.
62
63Schema looks like:
64
65Base dir
66 |
67 |- machine1 (optional)
68 | |
69 | |-- conf dir1
70 | | |
71 | | |- conf file 1
72 | [...] [...]
73 |
74 |- machine2 (optional)
75 | |
76 | |-- conf dir2
77 | | |
78 | | |- conf file 2
79 | [...] [...]
80
81Use of machines require use of option -m
82
83=head1 EXAMPLES
84
85 # this will create the appropriate CasparBuster environment
86 # under the base ~/prj/musique-ancienne.org directory (Cf cbbasedir in cb.conf)
87 # containing the directory victoria2 for this machine
88 # under which it will copy the required structure if needed (/etc/ssh)
89 # to finaly put a copy of the file sshd_conf in it from the victoria2 machine
90
91 cbusterize -m victoria2 -s /etc/ssh/sshd_config
92
93=head1 AUTHOR
94
95=over 4
96
97Bruno Cornec, http://brunocornec.wordpress.com
98
99=back
100
101=head1 LICENSE
102
103Copyright (C) 2012 Bruno Cornec <bruno@project-builder.org>
104Released under the GPLv2 or the Artistic license at your will.
105
106=cut
107use strict;
108use CasparBuster::Version;
109use CasparBuster::Env;
110use CasparBuster::Plugin;
111use File::Basename;
112use File::Path;
113use Getopt::Long;
114use Pod::Usage;
115use Data::Dumper;
116use Net::SSH2;
117use Cwd;
118use Archive::Tar;
119use Carp;
120use ProjectBuilder::Base;
121use ProjectBuilder::Conf;
122use ProjectBuilder::VCS;
123
124# settings
125my $debug = 0;
126my $help = undef;
127my $man = undef;
128my $source = undef;
129my $machine = undef;
130my $plugin = undef;
131my $quiet = undef;
132my $force = undef;
133my $log = undef;
134my $LOG = undef;
135my $ssh2 = undef;
136my $chan = undef;
137
138my ($cbver,$cbrev) = cb_version_init();
139my $appname = "cb";
140$ENV{'PBPROJ'} = $appname;
141pb_temp_init();
142
143# Initialize the syntax string
144pb_syntax_init("$appname (aka CasparBuster) Version $cbver-$cbrev\n");
145
146# parse command-line options
147GetOptions(
148 'machine|m=s' => \$machine,
149 'debug|d+' => \$debug,
150 'help|h' => \$help,
151 'quiet|q' => \$quiet,
152 'force|f' => \$force,
153 'man' => \$man,
154 'logfile|l=s' => \$log,
155 'source|s=s' => \$source,
156 'plugin|p=s' => \$plugin,
157) || pb_syntax(-1,0);
158
159if (defined $help) {
160 pb_syntax(0,1);
161}
162if (defined $man) {
163 pb_syntax(0,2);
164}
165if (defined $quiet) {
166 $debug=-1;
167}
168if (defined $log) {
169 open(LOG,"> $log") || die "Unable to log to $log: $!";
170 $LOG = \*LOG;
171 $debug = 0 if ($debug == -1);
172}
173
174$pbdebug = $debug;
175pb_log_init($debug, $LOG);
176pb_log(0,"Starting cbusterize\n");
177
178# Get conf file in context
179pb_conf_init($appname);
180# The personal one if there is such
181pb_conf_add("$ENV{'HOME'}/.cbrc") if (-f "$ENV{'HOME'}/.cbrc");
182# The system one
183pb_conf_add(cb_env_conffile());
184
185# Get configuration parameters
186my %cb;
187my $cb = \%cb;
188($cb->{'basedir'},$cb->{'cms'}) = pb_conf_get("cbbasedir","cbcms");
189pb_log(2,"%cb: ",Dumper($cb));
190
191# Check for mandatory params
192pod2usage("Error: --source or --plugin is a mandatory argument\n") if ((not defined $source) && (not defined $plugin));
193pod2usage("Error: --machine is a mandatory argument\n") if (not defined $machine);
194
195if (defined $plugin) {
196 # Load plugins
197 cb_plugin_load();
198}
199
200my $basedir = $cb->{'basedir'}->{$appname};
201eval { $basedir =~ s/(\$ENV.+\})/$1/eeg };
202
203pb_log(1, "DEBUG MODE, not doing anything, just printing\nDEBUG: basedir = $basedir\n");
204pb_log(1, "DEBUG: source = $source\n") if (defined $source);
205pb_log(1, "DEBUG: machine = $machine\n") if (defined $machine);
206
207# Use potentially a remote account if defined
208my $account = undef;
209my $remote = undef;
210($account) = pb_conf_get_if("cbaccount") if (defined $machine);
211$remote = $account->{$machine} if ((defined $account) && (defined $machine) && (defined $account->{$machine}));
212pb_log(1, "DEBUG: remote account1 = $remote\n") if (defined $remote);
213$remote = getpwuid($<) if (not defined $remote);
214pb_log(1, "DEBUG: remote account2 = $remote\n");
215
216# Create basedir if it doesn't exist
217if (not -d $basedir) {
218 if ($debug) {
219 pb_log(1, "DEBUG: Creating recursively directory $basedir\n");
220 } else {
221 pb_mkdir_p($basedir) || die "Unable to recursively create $basedir: $!";
222 }
223}
224
225if (defined $source) {
226 foreach my $f (split(/,/,$source)) {
227 cb_busterize($f,"true");
228 }
229}
230
231# Now handle plugins if any
232my $cbp = ();
233
234if (defined $plugin) {
235 foreach my $p (split(/,/,$plugin)) {
236 pb_log(1,"Getting context for plugin $p\n");
237 $cbp = cb_plugin_get($p,$cbp);
238 pb_log(2,"cbp: ".Dumper($cbp)."\n");
239 foreach my $k (keys %{$cbp->{$plugin}->{'dirsandfiles'}}) {
240 cb_busterize($k,"true");
241 }
242 foreach my $k ((keys %{$cbp->{$plugin}->{'dirs'}}),(keys %{$cbp->{$plugin}->{'files'}})) {
243 cb_busterize($k,"false");
244 }
245 }
246}
247
248$ssh2->disconnect() if (defined $machine);
249
250sub cb_busterize {
251
252my $source = shift;
253my $recur = shift;
254
255pb_log(2,"Entering cb_busterize source: $source\n");
256# Is the source a file or a dir ? Split the source parameter in 2
257my $srcdir = undef;
258my $srcfile = undef;
259my $cmd = undef;
260my $sftp;
261
262if (not defined $machine) {
263 if (-d $source) {
264 $srcdir = $source;
265 } else {
266 $srcdir = dirname($source);
267 $srcfile = basename($source);
268 }
269} else {
270 if (not defined $ssh2) {
271 pb_log(1,"DEBUG: First time so we need to create the SSH::Net2 object\n");
272 $ssh2 = Net::SSH2->new();
273 if ($debug >= 3) {
274 $ssh2->debug(1);
275 }
276 $ssh2->connect($machine) || confess "Unable to connect to $remote\@$machine: $!";
277 my $hdir = (getpwnam(getpwuid($<)))[7];
278 confess "Unable to connect to $remote\@$machine: $!" if (not $ssh2->auth_publickey($remote,"$hdir/.ssh/id_dsa.pub","$hdir/.ssh/id_dsa"));
279 $chan = $ssh2->channel();
280 die "Unable to create channel for $remote\@$machine: $!" if (not defined $chan);
281 my ($code, $error_name, $error_string) = $ssh2->error();
282 if ($code ne 0) {
283 pb_log(0,"code = $code");
284 pb_log(0,"error_name = $error_name");
285 pb_log(0,"error_string = $error_string");
286 }
287 #$ssh2->blocking(0);
288 if ($debug) {
289 pb_log(1,"DEBUG: launching a shell via Net:SSH2 ($remote\@$machine)");
290 }
291 confess "Unable to launch remote shell through Net:SSH2 ($remote\@$machine)" if (not $chan->shell());
292 }
293 $sftp = $ssh2->sftp;
294 die "Unable to create sftp channel for $remote\@$machine: $!" if (not defined $sftp);
295 my %dirs = $sftp->stat("$source/.");
296 my $res = 0;
297 $res = -1 if (not defined $dirs{'mode'});
298 pb_log(2,"DEBUG: Found res = $res\n");
299 if ($res == 0) {
300 $srcdir = $source;
301 pb_log(1,"DEBUG: Found remote dir = $source\n");
302 } else {
303 $srcdir = dirname($source);
304 $srcfile = basename($source);
305 pb_log(1,"DEBUG: Found remote file = $source\n");
306 }
307}
308
309pb_log(1,"DEBUG: Found srcdir = $srcdir\n");
310if (defined $srcfile) {
311 pb_log(1,"DEBUG: Found srcfile = $srcfile\n");
312} else {
313 pb_log(1,"DEBUG: Found no srcfile\n");
314}
315
316# Deduce the target directory from the local structure and the source
317my $target = $basedir;
318$target .= "/$machine" if defined ($machine);
319$target .= "$srcdir";
320
321my $scheme = $cb->{'cms'}->{$appname};
322
323# If both source and target are dirs, then copy into the parent of the target
324$target = basename($target) if ((not defined $srcfile) && (-d $target));
325
326# Create target if it doesn't exist
327if (not -d $target) {
328 if ($debug) {
329 pb_log(1,"DEBUG: Creating recursively directory $target\n");
330 } else {
331 pb_mkdir_p($target) || confess "Unable to recursively create $target: $!";
332 }
333 # Add all the dirs in it to VCS (in reverse order)
334 my $tdir = $target;
335 my @tab = ();
336 while ($tdir ne $basedir) {
337 push(@tab,$tdir);
338 $tdir = dirname($tdir);
339 pb_log(3,"tdir is now $tdir\n");
340 }
341 if ($debug) {
342 pb_log(0,"INFO: Added to your $scheme system the dirs: ".join(' ',reverse(@tab))."\n");
343 } else {
344 pb_vcs_add($scheme,reverse(@tab));
345 }
346}
347
348# For local handling
349my $cmdopt = "";
350# Recursive if we copy dirs
351$cmdopt = "-r" if (not defined $srcfile);
352$cmd = "cp -p $cmdopt $source $target";
353
354if (defined $srcfile) {
355 # File case
356 if ((! -f "$target/$srcfile") || (defined $force)) {
357 # doesn't exist locally
358 if (defined $machine) {
359 # Remote
360 cb_ssh_do($remote,$source,$target,$debug);
361 } else {
362 # Local
363 if ($debug) {
364 pb_log(1,"DEBUG: launching $cmd\n");
365 } else {
366 pb_system($cmd);
367 }
368 }
369 if ($debug) {
370 pb_log(1,"DEBUG: Adding $target/$srcfile to your $scheme system\n");
371 } else {
372 pb_vcs_add($scheme,"$target/$srcfile");
373 pb_log(0,"INFO: Created $target/$srcfile and added it to your $scheme system\n");
374 }
375 } else {
376 pb_log(0,"INFO: File $target/$srcfile already there\n");
377 }
378} else {
379 # Directory case
380 if ($recur eq "true") {
381 # with files in it
382 if ((! -d "$target") || (defined $force)) {
383 # doesn't exist locally
384 if (defined $machine) {
385 # Remote
386 cb_ssh_do($remote,$source,$target,$debug);
387 } else {
388 # Local
389 if ($debug) {
390 pb_log(1,"DEBUG: launching $cmd\n");
391 } else {
392 pb_system($cmd);
393 }
394 }
395 } else {
396 pb_log(0,"INFO: Directory $target already there\n");
397 }
398 } else {
399 # Only deal with that dir, nothing below, so just created locally
400 if ($debug) {
401 pb_log(1,"DEBUG: mkdir -p $target\n");
402 } else {
403 pb_mkdir_p("$target");
404 }
405 }
406 if ($debug) {
407 pb_log(1,"DEBUG: Adding $target to your $scheme system\n");
408 } else {
409 pb_vcs_add($scheme,"$target");
410 pb_log(0,"INFO: Created $target and added it to your $scheme system\n");
411 }
412}
413
414pb_log(2,"Exiting cb_busterize\n");
415}
416
417sub cb_ssh_do {
418
419my $remote = shift;
420my $source = shift;
421my $target = shift;
422my $debug = shift;
423
424# TODO: if sudo asks a passwd it won't work.
425my $cmd = "sudo tar cvhf /tmp/cb.$$.tar $source\n";
426if ($debug) {
427 pb_log(1,"DEBUG: launching through Net:SSH2 ($remote\@$machine) $cmd");
428}
429print $chan "$cmd";
430pb_log(1,"DEBUG: LINE : $_") while <$chan>;
431$cmd = "sudo chmod 600 /tmp/cb.$$.tar\n";
432if ($debug) {
433 pb_log(1,"DEBUG: launching through Net:SSH2 ($remote\@$machine) $cmd");
434}
435print $chan "$cmd";
436pb_log(1,"DEBUG: LINE : $_") while <$chan>;
437$cmd = "sudo chown $remote /tmp/cb.$$.tar\n";
438if ($debug) {
439 pb_log(1,"DEBUG: launching through Net:SSH2 ($remote\@$machine) $cmd");
440}
441print $chan "$cmd";
442pb_log(1,"DEBUG: LINE : $_") while <$chan>;
443if ($debug) {
444 pb_log(1,"DEBUG: gettting through Net:SSH2 ($remote\@$machine) /tmp/cb.$$.tar\n");
445}
446$ssh2->scp_get("/tmp/cb.$$.tar","/tmp/cb.$$.tar");
447if ($debug) {
448 pb_log(1,"DEBUG: erasing through Net:SSH2 ($remote\@$machine) /tmp/cb.$$.tar\n");
449} else {
450 print $chan "sudo rm -f /tmp/cb.$$.tar\n";
451}
452my $tar = Archive::Tar->new("/tmp/cb.$$.tar");
453$tar->setcwd($target);
454if ($debug) {
455 pb_log(1,"DEBUG: Extracting /tmp/cb.$$.tar\n");
456 foreach my $f ($tar->list_files()) {
457 pb_log(1,"DEBUG: $f\n");
458 }
459} else {
460 $tar->extract();
461}
462if ($debug) {
463 pb_log(1,"DEBUG: cleanup\n");
464} else {
465 $tar->clear;
466 unlink("/tmp/cb.$$.tar");
467}
468}
Note: See TracBrowser for help on using the repository browser.