#!/usr/bin/perl -w # =head1 NAME cb - CasparBuster looks at the structure in your CMS environment and deploy it to the target systems as needed =head1 SYNOPSIS cb [options] Options: --debug |-d debug mode --help |-h brief help message --man full documentation --force |-f force copy of files, even if they exist --source |-s directory or files to copy from the CasparBuster tree (',' separated if many) to the target --plugin |-p plugin defining what to copy from the CasparBuster tree (',' separated if many) to the target --machine|-m machine to deploy on. =head1 OPTIONS =over 4 =item B<--debug> Enter debug mode. This will print what would be done. No commands are executed, so this is safe to use when testing. =item B<--help> Print a brief help message and exits. =item B<--man> Prints the manual page and exits. =item B<--machine> I Specify the machine to consider when dealing with the CasparBuster structure. The files will be pushed to this machine, and a subdirectory named after the machine will be used under the basedir to look at the directory structure to deploy =item B<--source> I Specify the path of the source file or directory to deploy with CasparBuster. Multiple paths can be specified separated by ','. =item B<--plugin> I Specify the name of the plugin to deploy with CasparBuster. Multiple plugins can be specified separated by ','. A 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. =back =head1 DESCRIPTION Deploy the standard CasparBuster structure created by I. It will reinstall all files and directory in the plugin, with correct owner, group and mode, and launch at the end the script to re-enable potentially the service using the updated files. =head1 EXAMPLES # this will deploy the appropriate CasparBuster environment for DHCP # from the base ~/prj/musique-ancienne.org directory (Cf cbbasedir in cb.conf) # containing the directory victoria2 for this machine # to which it will copy the required files cb -m victoria2 -p dhcpd =head1 AUTHOR =over 4 Bruno Cornec, http://brunocornec.wordpress.com =back =head1 LICENSE Copyright (C) 2012 Bruno Cornec Released under the GPLv2 or the Artistic license at your will. =cut use strict; use CasparBuster::Version; use CasparBuster::Env; use CasparBuster::Plugin; #use Cwd 'realpath'; use File::Find; use File::Copy; use File::Basename; use File::Path; use File::Glob ':glob'; use Getopt::Long; use Pod::Usage; use Data::Dumper; use List::Util qw(first); use ProjectBuilder::Base; use ProjectBuilder::Conf; use ProjectBuilder::VCS; use DBI; use DBD::SQLite; # settings my $debug = 0; my $help = undef; my $man = undef; my $source = undef; my $machine = undef; my $plugin = undef; my $quiet = undef; my $force = undef; my $log = undef; my $LOG = undef; my ($cbver,$cbrev) = cb_version_init(); my $appname = "cb"; $ENV{'PBPROJ'} = $appname; pb_temp_init(); # Initialize the syntax string pb_syntax_init("$appname (aka CasparBuster) Version $cbver-$cbrev\n"); # parse command-line options GetOptions( 'machine|m=s' => \$machine, 'debug|d+' => \$debug, 'help|h' => \$help, 'quiet|q' => \$quiet, 'force|f' => \$force, 'man' => \$man, 'logfile|l=s' => \$log, 'source|s=s' => \$source, 'plugin|p=s' => \$plugin, ) || pb_syntax(-1,0); if (defined $help) { pb_syntax(0,1); } if (defined $man) { pb_syntax(0,2); } if (defined $quiet) { $debug=-1; } if (defined $log) { open(LOG,"> $log") || die "Unable to log to $log: $!"; $LOG = \*LOG; $debug = 0 if ($debug == -1); } $pbdebug = $debug; pb_log_init($debug, $LOG); pb_log(0,"Starting cb\n"); # Get conf file in context pb_conf_init($appname); # The personal one if there is such pb_conf_add("$ENV{'HOME'}/.cbrc") if (-f "$ENV{'HOME'}/.cbrc"); # The system one pb_conf_add(cb_env_conffile()); # Get configuration parameters my %cb; my $cb = \%cb; ($cb->{'basedir'},$cb->{'usemachines'},$cb->{'cms'},$cb->{'database'}) = pb_conf_get("cbbasedir","cbusemachines","cbcms","cbdatabase"); pb_log(2,"%cb: ",Dumper($cb)); # Check for mandatory params if (defined $plugin) { # Load plugins cb_plugin_load(); } my $basedir = $cb->{'basedir'}->{$appname}; eval { $basedir =~ s/(\$ENV.+\})/$1/eeg }; pb_log(1, "DEBUG MODE, not doing anything, just printing\nDEBUG: basedir = $basedir\n"); # Use potentially a remote account if defined my $account = undef; my $remote = $machine; ($account) = pb_conf_get_if("cbaccount") if (defined $machine); $remote = $account->{$machine}."@".$machine if ((defined $account) && (defined $account->{$machine})); pb_log(1, "DEBUG: remote = $remote\n") if (defined $remote); # Create basedir if it doesn't exist die "Unable to find base directory at $basedir" if (not -d $basedir); # Now handle plugins if any my $cbp = (); if (defined $plugin) { foreach my $p (split(/,/,$plugin)) { pb_log(1,"Getting context for plugin $p\n"); $cbp = cb_plugin_get($p,$cbp); pb_log(2,"cbp: ".Dumper($cbp)."\n"); } } my $db = "$cb->{'basedir'}/$cb->{'database'}"; my $precmd = ""; if (! -f $db) { $precmd = "CREATE TABLE dates (id INTEGER PRIMARY KEY AUTOINCREMENT, date DATE, file VARCHAR[65535])"; } my $dbh = DBI->connect("dbi:SQLite:dbname=$db","","", { RaiseError => 1, AutoCommit => 1 }) || die "Unable to connect to $db"; if ($precmd ne "") { my $sth = $dbh->prepare(qq{$precmd}) || die "Unable to create table into $db"; if ($debug) { pb_log(1,"DEBUG: Creatung DB $db\n"); pb_log(1,"DEBUG: with command $precmd\n"); } else { $sth->execute(); } } my $dest = "$ENV{'TMPDIR'}/vcs.$$"; pb_vcs_export(,$cb->{'basedir'},$dest); # Is the source a file or a dir ? Split the source parameter in 2 my $srcdir = undef; my $srcfile = undef; my $cmd = undef; if (not defined $machine) { if (-d $source) { $srcdir = $source; } else { $srcdir = dirname($source); $srcfile = basename($source); } } else { $cmd = "ssh -q $remote \'echo \"if [ -d $source ]; then exit 0; else exit -1; fi\" | sudo bash\'"; my $res = pb_system($cmd,"","quiet"); pb_log(2,"DEBUG: Found res = $res\n"); if ($res == 0) { $srcdir = $source; pb_log(1,"DEBUG: Found remote dir = $source\n"); } else { $srcdir = dirname($source); $srcfile = basename($source); pb_log(1,"DEBUG: Found remote file = $source\n"); } } pb_log(1,"DEBUG: Found srcdir = $srcdir\n"); if (defined $srcfile) { pb_log(1,"DEBUG: Found srcfile = $srcfile\n"); } else { pb_log(1,"DEBUG: Found no srcfile\n"); } # Deduce the target directory from the local structure and the source my $target = $basedir; $target .= "/$machine" if defined ($machine); $target .= "$srcdir"; my $scheme = $cb->{'cms'}->{$appname}; # If both source and target are dirs, then copy into the parent of the target $target = basename($target) if ((not defined $srcfile) && (-d $target)); # Create target if it doesn't exist when we have to copy a file if ((not -d $target) && (defined $srcfile)) { if ($debug) { pb_log(1,"DEBUG: Creating recursively directory $target\n"); } else { pb_mkdir_p($target) || die "Unable to recursively create $target: $!"; pb_vcs_add($scheme,$target); pb_log(0,"INFO: Created $target and added it to your $scheme system\n"); } } # We need to know where to get the content from my $cmdopt = ""; # Recursive if we copy dirs $cmdopt = "-r" if (not defined $srcfile); if (defined $machine) { $cmd = "scp -p -q $cmdopt $remote:$source $target"; } else { $cmd = "cp -p $cmdopt $source $target"; } # Now add content if not already there if (defined $srcfile) { # File case if ((! -f "$target/$srcfile") || (defined $force)){ if ($debug) { pb_log(1,"DEBUG: launching $cmd\n"); } else { pb_system($cmd); pb_vcs_add($scheme,"$target/$srcfile"); pb_log(0,"INFO: Created $target/$srcfile and added it to your $scheme system\n"); } } else { pb_log(0,"INFO: File $target/$srcfile already there\n"); } } else { # Directory case if ($debug) { pb_log(1,"DEBUG: launching $cmd\n"); } else { pb_system($cmd); pb_vcs_add($scheme,"$target"); pb_log(0,"INFO: Created $target and added it to your $scheme system\n"); } } pb_log(2,"Exiting cb_busterize\n"); }