#! /usr/bin/env perl
# initialize the instance using the user data
# jvehent - v20110806
use strict;
use warnings;
use Config::Simple;
use Sys::Syslog;
use File::Copy qw(cp move);
use Getopt::Std;
use Data::Dumper;
use File::Basename;

# global variables
our $cfg = new Config::Simple();
our %opts;
our ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time);
$year+=1900;
$mon+=1;
our $now = "$year-$mon-$mday $hour:$min:$sec";

# the action map contains the mapping between command line
# actions and internal function
our %action_map = ( set_hostname => \&set_hostname,
                    set_loc_ip_alias => \&set_loc_ip_alias,
                    set_local_dns => \&set_local_dns,
                    mount_ebs_lvm => \&mount_ebs_lvm,
                    bind_vols => \&bind_vols,
                    lighttpd_init => \&lighttpd_init,
                    haproxy_init => \&haproxy_init,
                    haproxy_reload => \&haproxy_reload,
                    mysqld_init => \&mysqld_init,
                    sendmail_init => \&sendmail_init,
                    cyrus_init => \&cyrus_init,
                    nfs_server_init => \&nfs_server_init,
                    nfs_client_init => \&nfs_client_init,
                    postfix_init => \&postfix_init,
                    alp_env_init => \&alp_env_init,
                    oracle_init => \&oracle_init,
                    nat_gateway_init => \&nat_gateway_init
    );


sub logmsg{
    my @msg = @_;
    print "@msg\n";
    syslog('info', '%s', "@msg");
}

sub usage{
    print   "\nusage: instance-init.pl -f <config file>\n\n",
            "\t-f: configuration file (REQUIRED)\n\n",
            "\t-a: actions list - put all multiple actions in quotations, separated by commas :\n",
                "\t\tmultiple actions are processed in the given order\n",
                "\t\tactions:\n",
                "\t\t* set_hostname (included in macro <system_init>)\n",
                "\t\t* set_loc_ip_alias (included in macro <system_init>)\n",
                "\t\t* set_local_dns (included in macro <system_init>)\n",
                "\t\t* mount_ebs_lvm (included in macro <system_init>)\n",
                "\t\t* bind_vols (included in macro <system_init>)\n",
                "\t\t* lighttpd_init\n",
                "\t\t* haproxy_init\n",
                "\t\t* haproxy_reload\n",
                "\t\t* mysqld_init\n",
                "\t\t* sendmail_init\n",
                "\t\t* cyrus_init\n",
                "\t\t* nfs_server_init\n",
                "\t\t* nfs_client_init\n",
                "\t\t* postfix_init\n",
                "\t\t* alp_env_init\n",
                "\t\t* oracle_init\n",
                "\t\t* nat_gateway_init\n",
            "\tactions can be defined in the configuration file using the \'services\' parameter\n",
            "\tcommand line arguments override the configuration\n\n",
            "\t-h: this help\n";

    exit -1;
}

# a wrapper to execute system commands and check the return code
sub sys_exec{
    my $command;
    foreach(@_){$command = $_." ";}   

    system("$command");

    if ($? == -1) {
        logmsg("failed to execute $command: $!");
    }
    elsif ($? & 127) {
        my $error = "child died with signal ".($? & 127).($? & 128) ? 'with' : 'without'." coredump";
        logmsg("$error");
    }
    else {
        my $rcode = $? >> 8;
        logmsg("Succesfully executed command \'$command\'");
    }
}

# validate and change system hostname --------------------------
sub set_hostname{
    if( !defined $cfg->param('hostname') 
        ){
        logmsg("Hostname is missing, can't configure hostname...");
    }
    else{
        my $hostname;
        $hostname = $cfg->param('hostname');
        if($hostname =~ /^(?=.{1,255}$)[0-9A-Za-z](?:(?:[0-9A-Za-z]|\b-){0,61}[0-9A-Za-z])?(?:\.[0-9A-Za-z](?:(?:[0-9A-Za-z]|\b-){0,61}[0-9A-Za-z])?)*\.?$/i)
        {
            # change hostname
            my $command = "/bin/hostname $hostname";
            sys_exec($command);
    
            # add hostname - Local IP to /etc/hosts
            my $local_ip = get_local_ip();
            my @short_hostname = split(/\./,$hostname);

            open(HOSTS,">/etc/hosts");
            print HOSTS "127.0.0.1   localhost   localhost.localdomain\n";
            print HOSTS "$local_ip $hostname $short_hostname[0]\n";
            close HOSTS;

            # add hostname to network file
            open(NETWORK,"> /etc/sysconfig/network");
            print NETWORK "HOSTNAME=$hostname\nNETWORKING=yes\nNOZEROCONF=true\nNETWORKING_IPV6=no\n";
            close NETWORK;
        }else{
            logmsg("Invalid hostname \'$hostname\'. Skipping hostname configuration.");
        }
    }
}


# extract the local ip of eth0 from ifconfig
sub get_local_ip{
    open(IFCONFIG,"/sbin/ifconfig eth0 |");
    while (<IFCONFIG>){
        if ($_ =~ /inet addr/){
            my @addr = split(/:/,$_);
            my @local_ip = split(/\s+/,$addr[1]);
            if ($local_ip[0] =~ /((\d){1,3}\.){3}(\d){1,3}/){
                return $local_ip[0];
            }
        }
    }
    close(IFCONFIG);
}


# create local IPs aliases -------------------------------------
sub set_loc_ip_alias{
    if( !defined $cfg->param('local_netmask') 
        || !defined $cfg->param('local_ip')
        ){
        logmsg("Local IP parameters are missing, can't configure local ip...");
    }
    else{
    	my $local_netmask;
    	$local_netmask = $cfg->param('local_netmask');
    	unless($local_netmask =~ /255.255.((\d){1,3}\.)(\d){1,3}/){
    	    logmsg("Invalid subnet mask \'$local_netmask\'. Using default 255.255.255.0.");
    	    $local_netmask = "255.255.255.248";
    	}
    	
    	my @local_ip;
    	@local_ip = $cfg->param('local_ip');
    	my $eth_alias_count = 1;
    	foreach (@local_ip)
    	{
    	    if($_ =~ /((\d){1,3}\.){3}(\d){1,3}/)
    	    {
    	        my $command = "/sbin/ifconfig eth0:$eth_alias_count $_ netmask $local_netmask";
    	        sys_exec($command);
    	        $eth_alias_count++;
    	    }else{
    	        logmsg("Invalid IP address \'$_\'. Skipping this alias.");
    	    }
    	}
    }
}
# set resolv.conf to use unbound@localhost ---------------------
sub set_local_dns{
    if( !defined $cfg->param('use_local_dns_resolver') 
        ){
        logmsg("DNS parameters are missing, can't set local dns...");
    }
    else{
        my $use_local_dns_resolver;
        $use_local_dns_resolver = $cfg->param('use_local_dns_resolver');
        if($use_local_dns_resolver eq "true" || $use_local_dns_resolver eq 1)
        {
            my $command = "cp /etc/resolv.{conf,conf.orig} && echo 'nameserver 127.0.0.1' > /etc/resolv.conf && grep nameserver /etc/resolv.conf.orig >> /etc/resolv.conf";
            sys_exec($command);
        }
    }
}

# attach EBS/LVM to /mnt/store ---------------------------------
sub mount_ebs_lvm{
    if( !defined $cfg->param('ebs_volumes') 
        ){
        logmsg("EBS parameters are missing, can't mount EBS...");
    }
    else{
        my @ebs_vols;
        @ebs_vols = $cfg->param('ebs_volumes');
        foreach (@ebs_vols){
            my($dev,$mountpoint,$type) = split(":",$_);
    
            # type allow to transform the device path for LVM
            my $device = $dev;
            if($type eq 'LVM'){
                my @dev_path = split('/',$dev);
                $device = "/$dev_path[1]/mapper/";
                for my $i (2 .. $#dev_path){
                    if($i!=2){ $device .= "-"; }
                    $device .= "$dev_path[$i]";
                }
            }
            # check if mounpoint is already used, or dev already mounted
            my $already_mounted = 0;
            open(MOUNTED,"mount |");
            while (<MOUNTED>){
                chomp $_;
                if($_ =~ /^$device\s/){
                    logmsg("$dev is already mounted : $_. Skipping it.");
                    $already_mounted = 1;
                }
            }
    
            # if not mounted already, do it now
            unless($already_mounted){
                if(! -e $mountpoint){
                    my $command = "mkdir -p $mountpoint";
                    sys_exec($command);
                }
                # write that to fstab
                logmsg("populating fstab with $device");
                open(FSTAB,">>/etc/fstab");
                print FSTAB "$device\t$mountpoint\txfs\tnoatime,nodiratime,osyncisdsync\t0 0\n";
                close FSTAB;

                my $command = "mount -a";
                sys_exec($command);
            }
        }
    }
}

# bind folders, rsync if necessary
sub bind_vols{
    if( !defined $cfg->param('mount_bind') 
        ){
        logmsg("Binding parameters are missing, can't mount bind...");
    }
    else{
       my @bind_vols;
       @bind_vols = $cfg->param('mount_bind');
       foreach (@bind_vols){
   
           my($mountorigin,$mountdest) = split(":",$_);
   
           my $already_mounted = 0;
           open(MOUNTED,"mount |");
           while (<MOUNTED>){
               chomp $_;
               if($_ =~ /^$mountorigin on $mountdest\s/){
                   logmsg("$mountorigin is already bound to $mountdest : $_. Skipping it.");
                   $already_mounted = 1;
               }
           }
   
           # if not mounted already, do it now
           unless($already_mounted){
                # write that to fstab
                logmsg("populating fstab with bind of $mountorigin to $mountdest");
                open(FSTAB,">>/etc/fstab");
                print FSTAB "$mountorigin\t$mountdest\tnone\tdefaults,bind\t0 0\n";
                close FSTAB;

                my $command = "mount -a";
                sys_exec($command);
   
               # if the folder was /var/log, restart syslog-ng
               if($mountdest =~ /^\/var\/log/){
                   my $command = "service syslog-ng restart";
                   sys_exec($command);
               }
           }
   
       }
    }
}


# Prepare and start lighttpd
sub lighttpd_init{
    if( !defined $cfg->param('lighttpd_symlinks') 
        || !defined $cfg->param('lighttpd_user')
        || !defined $cfg->param('lighttpd_group')
        ){
        logmsg("Lighttpd parameters are missing, can't configure lighttpd...");
    }
    else{
        # get lighttpd user uid and gid
        my $lighttpd_uid = getpwnam($cfg->param('lighttpd_user'));
        my $lighttpd_gid = getgrnam($cfg->param('lighttpd_group'));

        #process all symlinks from lighttpd_symlinks
        my @folders;
        @folders = $cfg->param('lighttpd_symlinks');
        foreach (@folders){
            my($source,$symlink,$type) = split(":",$_);

            mkdir($source) unless( -d $source);
            logmsg("folder ". $source ." ok and owned by ".$lighttpd_uid.":".$lighttpd_gid);

            # symlink the source into lighttpd folder (if doesn't exist or type is force)
            if( ! -e $symlink or $type eq "force"){
                unlink($symlink) if (-e $symlink);
                symlink($source,$symlink);
                chown($lighttpd_uid,$lighttpd_gid,$symlink);
            }            
        }

        # start the service
        my $command = "service lighttpd restart";
        sys_exec($command);

        # register it to start after reboot
        $command = "chkconfig lighttpd on";
        sys_exec($command);

    }
}
sub haproxy_conf_edit{

     # open configuration file, edit the backends and save
     if(! -r $cfg->param('haproxy_conf')){
      logmsg("No haproxy configuration at \'$cfg->param('haproxy_conf')\'. Skipping it");
      return -1;
     }

     # get list of alp-app backends
     my @alpapps = $cfg->param('haproxy_backend_alp-app');
     my $alpapps_index = 0;
     # get list of alp-station backends
     my @alpstation = $cfg->param('haproxy_backend_alp-station');
     my $alpstation_index = 0;

     open(HACONF,$cfg->param('haproxy_conf'));
     open(NEWHACONF,"> $cfg->param('haproxy_conf').new");
     my @haconf = <HACONF>;
     my $process_mode = 'none';

     print NEWHACONF "#haproxy configuration generated on " . $now . "\n";

     for my $i (0 .. $#haconf){

      # new_line will be set to 1 if the line from the original conf
      # needs is replaced by one from the new_conf
      my $new_line = 0;

      # skip empty lines and comments
      if($haconf[$i] =~ /^((\s+?$)|(\#))/){next;}

      # if we reached the start of a backend section, change the process mode accordingly
      if($haconf[$i] =~ /^backend/){
          if($haconf[$i] =~ /alp-app/){
           $process_mode = "alp-app";
          }elsif($haconf[$i] =~ /alp-station/){
           $process_mode = "alp-station";
          }
      }


      if($process_mode eq 'alp-app'){

          # does the line start with "server" ?
          if($haconf[$i] =~ /^\s+server/){

           # replace the server line with the one from @alpapps
           # if there is no server left in @alpapps, just do nothing
           if($alpapps_index <= $#alpapps){

            # split the syntax from alpapps
            my @server = split(/:/,$alpapps[$alpapps_index]);
            # replace the line from haconf with the one from alpapps
             print NEWHACONF "    server $server[0] $server[1]:$server[2] cookie $server[0] maxconn 256 maxqueue 5000 weight $server[3] check inter 2s\n";

            # set the index to the next server in the new configuration
            $alpapps_index++;
           }
           # we do not keep the original line
           $new_line = 1;
          }

      }elsif($process_mode eq 'alp-station'){

          # does the line start with "server" ?
          if($haconf[$i] =~ /^\s+server/){

           # replace the server line with the one from @alpstation
           # if there is no server left in @alpstation, just delete the line
           if($alpstation_index <= $#alpstation){

            # split the syntax from alpstation
            my @server = split(/:/,$alpstation[$alpstation_index]);
            # replace the line from haconf with the one from alpstation
            print NEWHACONF "    server $server[0] $server[1]:$server[2] cookie $server[0] maxconn 256 maxqueue 5000 weight $server[3] check inter 2s\n";

            # set the index to the next server in the configuration
            $alpstation_index++;

           }
           # we do not keep the original line
           $new_line = 1;
          }
      }

      # if we didn't replace the line, write the oriinal one to the new conf
      if($new_line == 0 ){
          chomp $haconf[$i];
          print NEWHACONF "$haconf[$i]\n";
      }

      # if the next line start with a new section keyword, or we are at the end of the file
      # we have reached the end of a backend section
      # if there are any server left in the new configuration, add them now
      if( (($i+1 < $#haconf) and ($haconf[$i+1] =~ /^(global|defaults|frontend|listen|backend)/))
          or ($i+1 >= $#haconf)
      ){
          if(($process_mode eq 'alp-app')){
           while($alpapps_index <= $#alpapps){
            my @server = split(/:/,$alpapps[$alpapps_index]);
            print NEWHACONF "    server $server[0] $server[1]:$server[2] cookie $server[0] maxconn 256 maxqueue 5000 weight $server[3] check inter 2s\n";
            $alpapps_index++;
           }
          }elsif($process_mode eq 'alp-station'){
           while($alpstation_index <= $#alpstation){
            my @server = split(/:/,$alpstation[$alpstation_index]);
            print NEWHACONF "    server $server[0] $server[1]:$server[2] cookie $server[0] maxconn 256 maxqueue 5000 weight $server[3] check inter 2s\n";
            $alpstation_index++;
           }
          }

          # add a blank line and reset the process mode
          print NEWHACONF "\n";
          $process_mode = 'none';
      }
     }

     close HACONF;
     close NEWHACONF;

     # swap the old and new configurations
     my $haconf_bkp = $cfg->param('haproxy_conf') . "." . time();
     move($cfg->param('haproxy_conf'),$haconf_bkp);
     move("$cfg->param('haproxy_conf').new",$cfg->param('haproxy_conf'));

}

sub haproxy_init{
    if( !defined $cfg->param('haproxy_conf') 
        || !defined $cfg->param('haproxy_backend_alp-app')
        || !defined $cfg->param('haproxy_backend_alp-station')
        ){
        logmsg("Haproxy parameters are missing, can't configure haproxy...");
    }
    else{

        # edit the configuration
        haproxy_conf_edit();

        # start haproxy
        my $command = "service haproxy restart";
        sys_exec($command);

        # register it to start after reboot
        $command = "chkconfig haproxy on";
        sys_exec($command);
   }
}

sub haproxy_reload{
    if( !defined $cfg->param('haproxy_conf') 
        || !defined $cfg->param('haproxy_backend_alp-app')
        || !defined $cfg->param('haproxy_backend_alp-station')
        ){
        logmsg("Haproxy parameters are missing, can't configure haproxy...");
    }
    else{

        # edit the configuration
        haproxy_conf_edit();

        # start haproxy
        my $command = "service haproxy reload";
        sys_exec($command);

   }
}

sub mysqld_init{
    my $command = "service mysqld restart";
    sys_exec($command);
    $command = "chkconfig mysqld on";
    sys_exec($command);
}

sub sendmail_init{
    my $command = "service sendmail restart";
    sys_exec($command);
    $command = "chkconfig sendmail on";
    sys_exec($command);
}

sub cyrus_init{
    my $command = "service cyrus-imapd restart";
    sys_exec($command);
    $command = "chkconfig cyrus-imapd on";
    sys_exec($command);
}

sub nfs_server_init{
    if( !defined $cfg->param('nfs_export_folder') 
        ){
        logmsg("NFS export parameter is missing, can't configure nfs exports...");
    }
    else{

        # start portmap
        my $command = "service portmap restart";
        sys_exec($command);

        # populate /etc/exports
        my @nfs_exports = $cfg->param('nfs_export_folder');
        
        # do not backup the old /etc/exports, just overwrite it
        open(EXPORTS,">/etc/exports");
        foreach (@nfs_exports){
            chomp $_;
            my @nfs_share = split(/:/,$_);
            print EXPORTS "$nfs_share[0] $nfs_share[1]($nfs_share[2],sync,no_subtree_check,insecure,anonuid=505,anongid=505,all_squash)\n";
        }
        close EXPORTS;

        # reload the exports
        $command = "exportfs -fa";
        sys_exec($command);

        # register it to start after reboot
        $command = "chkconfig portmap on";
        sys_exec($command);
        $command = "chkconfig nfs on";
        sys_exec($command);
    }
}

sub nfs_client_init{
    if( !defined $cfg->param('nfs_mount_folder') 
        ){
        logmsg("NFS mount parameter is missing, can't mount nfs shares...");
    }
    else{

        # start portmap
        my $command = "service portmap restart";
        sys_exec($command);

        # get list of shares and populate /etc/fstab with it
        logmsg("backing up /etc/fstab into /etc/.fstab.bkp.$now");
        cp("/etc/fstab","/etc/.fstab.bkp.$now");

        my @nfs_mounts = $cfg->param('nfs_mount_folder');
      
        open(FSTAB,">>/etc/fstab");
        foreach (@nfs_mounts){
            chomp $_;
            my @nfstab = split(/:/,$_);
            print FSTAB "$nfstab[0]:$nfstab[1]\t$nfstab[2]\tnfs\t$nfstab[3]\t0 0\n";
        }
        close FSTAB;

        # reload the fstab
        $command = "mount -a";
        sys_exec($command);
    }
}


# replace values in the environment.conf
# move it to /opt/alp
# configure, deploy and start the env
sub alp_env_init{

        # get the info about the alp user
        my($alp_name,$alp_passwd,$alp_uid,$alp_gid,$alp_quota,$alp_comment,$alp_gcos,$alp_home,$alp_shell,$alp_expire) = getpwnam($cfg->param('alp_user'));
        # what environment are we in
        my $target_env;
        if($cfg->param('hostname') =~ /^t/){
            $target_env = "TEST";
        }elsif($cfg->param('hostname') =~ /^q/){
            $target_env = "QA";
        }elsif($cfg->param('hostname') =~ /^p/){
            $target_env = "PROD";
        }else{
            logmsg("Cannot determine the target environment.");
        }
        if(defined $target_env){

            #open source and target
            open(ORIGALPENV,"/root/userdata/$target_env-alpsetup-environment.conf") or die $!;
            open(DESTALPENV,"> $alp_home/environment.conf") or die $!;

            while(<ORIGALPENV>){

                chomp;

                #if a line contains %INSTANCE-INIT%<param>
                #replace the value with the <param> taken from $cfg
                if ($_ =~ /\%INSTANCE-INIT\%/){

                    # split on equal sign, 0 is the param, 1 is the value
                    my @param_line = split(/=/,$_);
                    
                    # split the value to the substitution param name
                    my @sub_param = split(/\%INSTANCE-INIT\%/,$param_line[1]);

                    if(defined $cfg->param($sub_param[1])){
                        #replace the line with the new value
                        #if the new value is an array, do it in a loop and separate with commas
                        if(ref($cfg->param($sub_param[1])) eq "ARRAY"){
                            print DESTALPENV $param_line[0] . "=";
                            my @params = $cfg->param($sub_param[1]);
                            for my $i (0 .. $#params){
                                print DESTALPENV "$params[$i]";
                                if($i!=$#params){print DESTALPENV ", ";}
                                else{ print DESTALPENV "\n";}
                            }
                            logmsg("substituted $param_line[1] with the array $sub_param[1] in alpsetup environment.conf");
                        # single value, just replace the line
                        }else{
                            print DESTALPENV $param_line[0] . "=" . $cfg->param($sub_param[1]) . "\n";
                            logmsg("substituted $param_line[1] with $cfg->param($sub_param[1]) in alpsetup environment.conf");
                        }
                    }
                    else{ logmsg("cannot find configuration parameter $sub_param[1] for substitution with $param_line[1]");}
                    
                }else{  # just copy the line from the origin
                    print DESTALPENV "$_\n";
                }
            }
            close ORIGALPENV;
            close DESTALPENV;

            # chown the file to user alp
            chown($alp_uid,$alp_gid,"$alp_home/environment.conf");
            # configure, deploy and start the env
            my $command = "su - $alp_name -c \'alpsetup configure env\'";
            sys_exec($command);
            $command = "su - $alp_name -c \'alpsetup deploy env\'";
            sys_exec($command);
            $command = "su - $alp_name -c \'alpsetup start env\'";
            sys_exec($command);
        }
}


# init the oracle database
sub oracle_init{
    my $command = "/etc/init.d/dbora stop";
    sys_exec($command);
    $command = "/etc/init.d/dbora start";
    sys_exec($command);
    $command = "chkconfig dbora on";
    sys_exec($command);
}


# init the nat_gateway setup
# depending on the instance, either configure the NAT, or configure
# the routes to connect to the NAT
sub nat_gateway_init{
    if( !defined $cfg->param('nat_gateway') 
        || !defined $cfg->param('ops_net')
        ){
        logmsg("NAT/IP parameters are missing, cannot configure the NAT/Routes");
    }
    else{
        
        my $local_ip = get_local_ip();

        # if the nat_gateway is the local host, configure the NAT
        if($cfg->param('nat_gateway') eq $local_ip )
        {
            logmsg("Instance is the NAT Gateway, configuring ip_forward and Netfilter");

            # activate ip forwarding
            my $command = "echo 1 > /proc/sys/net/ipv4/ip_forward";
            sys_exec($command);
            # flush the NAT table
            $command = "/sbin/iptables -t nat -F";
            sys_exec($command);
            # add the NAT rule
            $command = "iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE";
            sys_exec($command);

            # edit /etc/sysctl.conf to make the ip_forward persistent
            my $sysctl_file = "/etc/sysctl.conf";
            my $found=0;
            local @ARGV = ($sysctl_file);
            local $^I = '.bac';
            while(<>){
                if(s/ip_forward = 0/ip_forward = 1/ig){
                    $found = 1;
                    print;
                }
                else{
                    print;
                }
            }   # if we didn't find it, add it at the end

            # save the iptables rules
            $command = "service iptables save";
            sys_exec($command);

        # otherwise, add the routes to connect to the NAT Gateway
        }else{
            logmsg("Configuring the routing");
        
            # get the original default gateway
            my $orig_default_gw="";
            open(IPR,"ip route |");
            while(<IPR>){
                if($_ =~ /^default/){
                    my @tmp = split(/ /,$_);
                    $orig_default_gw = $tmp[2];
                }
            }
            close IPR;
            my $command = "/sbin/ip route add " . $cfg->param('ops_net') . " via $orig_default_gw";
            sys_exec($command);

            # add the new default gateway to network file
            my $natgwinitscript = "/etc/init.d/nat_default_gateway";
            open(NATDEFGW,">> $natgwinitscript");
            print NATDEFGW "#!/bin/bash\n# chkconfig: 345 99 10\n# description: Change default gateway to route through the NAT\n";
            print NATDEFGW "case \"\$1\" in\n";
            print NATDEFGW "\tstart)\n\t\t/sbin/ip route change default via " . $cfg->param('nat_gateway') . "\n\t\t;;\n";
            print NATDEFGW "\tstop)\n\t\techo nothing to do here\n\t\t;;\nesac\nexit \$?\n";
            close NATDEFGW;

            chmod(0700, $natgwinitscript);
            sys_exec($natgwinitscript);
            my $natgwinitscript_filename = basename( $natgwinitscript );
            $command = "chkconfig --add $natgwinitscript_filename";
            sys_exec($command);
            $command = "chkconfig $natgwinitscript_filename on";
            sys_exec($command);
            $command = "service $natgwinitscript_filename start";
            sys_exec($command);

            # add the route to static file for reboot persistance
            open(ROUTESCRIPT,"> /etc/sysconfig/network-scripts/route-eth0");
            print ROUTESCRIPT $cfg->param('ops_net') . " via $orig_default_gw\n";
            close ROUTESCRIPT;
        }
    }
}
# take a list of actions and execute them
sub actions_exec{
    my $args;foreach(@_){$args.=$_;}
    # split the list of actions in an array and process it sequentially
    my @action;
    if($args =~ /,/){
        @action = split(/,\s?/,$args);
    }else{
        @action = @_;
    }
    print Dumper(@action);
    foreach(@action){
        if(exists($action_map{$_})){
            # call the function
            $action_map{$_}->();

        #expand the macro "system_init"
        }elsif($_ =~ /^system_init$/){
            set_hostname();
            set_loc_ip_alias();
            set_local_dns();
            mount_ebs_lvm();
            bind_vols();

        }else{ logmsg("unknown action '$_'");}
    }
}


# parse command line args
getopts('ha:f:', \%opts);

usage() if ($opts{'h'});

if(!defined($opts{'f'})){
    logmsg("Missing configuration file");
    usage();
}

# init syslog
openlog("instance-init.pl", "pid", 'LOG_USER');
logmsg("Starting instance initialization with file '$opts{'f'}' and actions '$opts{'a'}'");

# parse user data into configuration table
$cfg->read($opts{'f'});
logmsg("Loading configuration from $opts{'f'}");


# process the actions from the command line
if(exists $opts{'a'}){
    actions_exec($opts{'a'});
}elsif(defined $cfg->param('services')){
    actions_exec($cfg->param('services'));
}else{
    logmsg("No action argument or services in configuration, nothing to do...");
}

# close syslog
closelog();
exit 0;

