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

Last change on this file was 1679, checked in by Bruno Cornec, 11 years ago
  • Start working on plugins
  • Property svn:executable set to *
File size: 11.7 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 CasparBuster::SSH;
112use File::Basename;
113use File::Path;
114use Getopt::Long;
115use Pod::Usage;
116use Data::Dumper;
117use Net::SSH2;
118use Cwd;
119use Archive::Tar;
120use Carp;
121use ProjectBuilder::Base;
122use ProjectBuilder::Conf;
123use ProjectBuilder::VCS;
124
125# settings
126my $debug = 0;
127my $help = undef;
128my $man = undef;
129my $source = undef;
130my $machine = undef;
131my $plugin = undef;
132my $quiet = undef;
133my $force = undef;
134my $log = undef;
135my $LOG = undef;
136my $ssh2 = undef;
137my $chan = undef;
138
139my ($cbver,$cbrev) = cb_version_init();
140my $appname = "cb";
141$ENV{'PBPROJ'} = $appname;
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_temp_init();
177pb_log(1,"Starting cbusterize\n");
178
179# Get conf file in context
180pb_conf_init($appname);
181# The personal one if there is such
182pb_conf_add("$ENV{'HOME'}/.cbrc") if (-f "$ENV{'HOME'}/.cbrc");
183# The system one
184pb_conf_add(cb_env_conffile());
185
186# Get configuration parameters
187my %cb;
188my $cb = \%cb;
189($cb->{'basedir'},$cb->{'cms'}) = pb_conf_get("cbbasedir","cbcms");
190pb_log(2,"%cb: ",Dumper($cb));
191
192# Check for mandatory params
193pod2usage("Error: --source or --plugin is a mandatory argument\n") if ((not defined $source) && (not defined $plugin));
194pod2usage("Error: --machine is a mandatory argument\n") if (not defined $machine);
195
196if (defined $plugin) {
197 # Load plugins
198 cb_plugin_load();
199}
200
201my $basedir = $cb->{'basedir'}->{$appname};
202eval { $basedir =~ s/(\$ENV.+\})/$1/eeg };
203
204pb_log(1, "DEBUG MODE, not doing anything, just printing\nDEBUG: basedir = $basedir\n");
205pb_log(1, "DEBUG: source = $source\n") if (defined $source);
206pb_log(1, "DEBUG: machine = $machine\n");
207
208# Use potentially a remote account if defined
209my $account = undef;
210my $remote = undef;
211($account) = pb_conf_get_if("cbaccount");
212$remote = $account->{$machine} if ((defined $account) && (defined $account->{$machine}));
213pb_log(1, "DEBUG: remote account1 = $remote\n") if (defined $remote);
214$remote = getpwuid($<) if (not defined $remote);
215pb_log(1, "DEBUG: remote account2 = $remote\n");
216
217$ssh2 = cb_ssh_init($remote,$machine,$debug) if (not defined $ssh2);
218
219# Create basedir if it doesn't exist
220if (not -d $basedir) {
221 if ($debug) {
222 pb_log(1, "DEBUG: Creating recursively directory $basedir\n");
223 } else {
224 pb_mkdir_p($basedir) || die "Unable to recursively create $basedir: $!";
225 }
226}
227
228if (defined $source) {
229 foreach my $f (split(/,/,$source)) {
230 cb_busterize($f,"true");
231 }
232}
233
234# Now handle plugins if any
235my $cbp = ();
236
237if (defined $plugin) {
238 foreach my $p (split(/,/,$plugin)) {
239 pb_log(1,"Getting context for plugin $p\n");
240 $cbp = cb_plugin_get($p,$cbp,$remote,$machine,$debug,$ssh2);
241 pb_log(2,"cbp: ".Dumper($cbp)."\n");
242 foreach my $k (keys %{$cbp->{$plugin}->{'dirsandfiles'}}) {
243 cb_busterize($k,"true");
244 }
245 foreach my $k ((keys %{$cbp->{$plugin}->{'dirs'}}),(keys %{$cbp->{$plugin}->{'files'}})) {
246 cb_busterize($k,"false");
247 }
248 }
249}
250
251cb_ssh_close($ssh2);
252
253sub cb_busterize {
254
255my $source = shift;
256my $recur = shift;
257
258pb_log(2,"Entering cb_busterize source: $source\n");
259# Is the source a file or a dir ? Split the source parameter in 2
260my $srcdir = undef;
261my $srcfile = undef;
262my $realsrc = "";
263my $type = "";
264
265$chan = $ssh2->channel();
266pb_log(3,"DEBUG: SSH2 chan called\n");
267confess "Unable to create channel for $remote\@$machine: $!" if (not defined $chan);
268if ($debug) {
269 pb_log(1,"DEBUG: launching a shell via Net:SSH2 ($remote\@$machine)\n");
270}
271confess "Unable to launch remote shell through Net:SSH2 ($remote\@$machine)" if (not $chan->shell());
272pb_log(3,"DEBUG: SSH2 shell called\n");
273
274pb_log(2,"DEBUG: Calling readlink -f $source\n");
275print $chan "readlink -f $source\n";
276while (<$chan>) {
277 $realsrc = $_;
278 chomp($realsrc);
279 pb_log(3,"DEBUG: Found $realsrc");
280}
281pb_log(2,"DEBUG: Found realsrc = $realsrc\n");
282print $chan "LANGUAGE=C stat -c '%F' $realsrc\n";
283pb_log(2,"DEBUG: Calling LANGUAGE=C stat -c '%F' $realsrc\n");
284while (<$chan>) {
285 $type = $_;
286 chomp($type);
287 pb_log(3,"DEBUG: Found $type");
288}
289pb_log(2,"DEBUG: Found type = $type\n");
290if ($type =~ /directory/) {
291 $srcdir = $source;
292 pb_log(1,"DEBUG: Found remote dir = $source\n");
293} else {
294 # File or something else potentially
295 $srcdir = dirname($source);
296 $srcfile = basename($source);
297 pb_log(1,"DEBUG: Found remote file = $source\n");
298}
299
300pb_log(1,"DEBUG: Found srcdir = $srcdir\n");
301if (defined $srcfile) {
302 pb_log(1,"DEBUG: Found srcfile = $srcfile\n");
303} else {
304 pb_log(1,"DEBUG: Found no srcfile\n");
305}
306
307my $scheme = $cb->{'cms'}->{$appname};
308
309# Deduce the target directory from the local structure and the source
310my $target = "$basedir/$machine/$srcdir";
311
312# If both source and target are dirs, then copy into the parent of the target
313#$target = basename($target) if ((not defined $srcfile) && (-d $target));
314
315# Create target if it doesn't exist
316if (not -d $target) {
317 if ($debug) {
318 pb_log(1,"DEBUG: Creating recursively directory $target\n");
319 } else {
320 pb_mkdir_p($target) || confess "Unable to recursively create $target: $!";
321 }
322 # Add all the dirs in it to VCS (in reverse order) if not already in it
323 my $tdir = $target;
324 my @tab = ();
325 while ($tdir ne $basedir) {
326 push(@tab,$tdir);
327 $tdir = dirname($tdir);
328 pb_log(3,"tdir is now $tdir\n");
329 }
330 if (not $debug) {
331 pb_vcs_add_if_not_in($scheme,reverse(@tab));
332 }
333 pb_log(0,"INFO: Added to your $scheme system the dirs: ".join(' ',reverse(@tab))."\n");
334}
335
336if (defined $srcfile) {
337 # File case
338 if ((! -f "$target/$srcfile") || (defined $force)) {
339 $ssh2->scp_put($source,"$target/$srcfile");
340 #cb_ssh_do($remote,$machine,$source,"$basedir/$machine",$debug,$ssh2,$chan);
341
342 if (not $debug) {
343 pb_vcs_add_if_not_in($scheme,"$target/$srcfile");
344 }
345 pb_log(0,"INFO: Created $target/$srcfile and added it to your $scheme system if needed\n");
346 } else {
347 pb_log(0,"INFO: File $target/$srcfile already there. Will not add without forcing with -f\n");
348 }
349} else {
350 # Directory case
351 if ($recur eq "true") {
352 # with files in it
353 # TODO: if sudo asks a passwd it won't work.
354 my $cmd = "sudo tar cvhf /tmp/cb.$$.tar $source\n";
355 if ($debug) {
356 pb_log(1,"DEBUG: launching through Net:SSH2 ($remote\@$machine) $cmd");
357 }
358 print $chan "$cmd";
359 pb_log(1,"DEBUG: LINE : $_") while <$chan>;
360 $cmd = "sudo chmod 600 /tmp/cb.$$.tar\n";
361 if ($debug) {
362 pb_log(1,"DEBUG: launching through Net:SSH2 ($remote\@$machine) $cmd");
363 }
364 print $chan "$cmd";
365 pb_log(1,"DEBUG: LINE : $_") while <$chan>;
366 $cmd = "sudo chown $remote /tmp/cb.$$.tar\n";
367 if ($debug) {
368 pb_log(1,"DEBUG: launching through Net:SSH2 ($remote\@$machine) $cmd");
369 }
370 print $chan "$cmd";
371 pb_log(1,"DEBUG: LINE : $_") while <$chan>;
372
373 if ($debug) {
374 pb_log(1,"DEBUG: gettting through Net:SSH2 ($remote\@$machine) /tmp/cb.$$.tar\n");
375 }
376
377 $ssh2->scp_get("/tmp/cb.$$.tar","/tmp/cb.$$.tar");
378
379 if ($debug) {
380 pb_log(1,"DEBUG: erasing through Net:SSH2 ($remote\@$machine) /tmp/cb.$$.tar\n");
381 } else {
382 print $chan "sudo rm -f /tmp/cb.$$.tar\n";
383 }
384 my $tar = Archive::Tar->new("/tmp/cb.$$.tar");
385 $tar->setcwd("$basedir/$machine");
386 if ($debug) {
387 pb_log(1,"DEBUG: Extracting /tmp/cb.$$.tar\n");
388 foreach my $f ($tar->list_files()) {
389 pb_log(1,"DEBUG: $f\n");
390 }
391 } else {
392 foreach my $f ($tar->get_files) {
393 my $sname = $f->name;
394 $sname =~ s|/$||;
395 next if ($srcdir =~ /$sname/);
396 if ($f->is_file) {
397 #pb_log(0,"Extracting and Adding ".$f->name." to your $scheme system\n");
398 $tar->extract($f);
399 pb_vcs_add_if_not_in($scheme,"$basedir/$machine/".$f->name);
400 } elsif ($f->is_dir) {
401 pb_log(0,"Calling recursively cbusterize on ".$f->name."\n");
402 cb_busterize("/".$f->name,$recur);
403 } else {
404 pb_log(0,"File type $f->type for $f->name is not handled yet\n");
405 }
406 }
407 }
408 if ($debug) {
409 pb_log(1,"DEBUG: please cleanup manually /tmp/cb.$$.tar\n");
410 } else {
411 $tar->clear;
412 unlink("/tmp/cb.$$.tar");
413 }
414 } else {
415 # Only deal with that dir, nothing below, so just created locally
416 if ($debug) {
417 pb_log(1,"DEBUG: Creatng and Adding $target to your $scheme system\n");
418 } else {
419 pb_mkdir_p("$target");
420 pb_vcs_add_if_not_in($scheme,"$target");
421 }
422 }
423}
424$chan->close();
425
426pb_log(2,"Exiting cb_busterize\n");
427}
428
429
Note: See TracBrowser for help on using the repository browser.