git-svn-id: https://svn.code.sf.net/p/xcat/code/xcat-core/trunk@14916 8638fb3e-16cb-4fca-ae20-7b5d299a9bcd
		
			
				
	
	
		
			827 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
			
		
		
	
	
			827 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
# IBM(c) 2012 EPL license http://www.eclipse.org/legal/epl-v10.html
 | 
						|
 | 
						|
package xCAT::CFMUtils;
 | 
						|
 | 
						|
BEGIN
 | 
						|
{
 | 
						|
  $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat';
 | 
						|
}
 | 
						|
use lib "$::XCATROOT/lib/perl";
 | 
						|
 | 
						|
use strict;
 | 
						|
use warnings;
 | 
						|
use File::Path;
 | 
						|
use File::Copy;
 | 
						|
use File::Find;
 | 
						|
use Getopt::Long;
 | 
						|
use Data::Dumper;
 | 
						|
use File::Basename;
 | 
						|
use xCAT::Table;
 | 
						|
use xCAT::Utils;
 | 
						|
use xCAT::MsgUtils;
 | 
						|
1;
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
 | 
						|
=head3 initCFMdir
 | 
						|
    Initialize CFM directories and files. The default layout under cfmdir is:
 | 
						|
    . 
 | 
						|
    |-- etc
 | 
						|
    | |-- group.merge -> /etc/group.merge
 | 
						|
    | |-- hosts -> /etc/hosts
 | 
						|
    | |-- passwd.merge -> /etc/passwd.merge
 | 
						|
    | |-- shadow.merge -> /etc/shadow.merge
 | 
						|
    |-- group.OS -> /etc/group.OS
 | 
						|
    |-- passwd.OS -> /etc/passwd.OS
 | 
						|
    |-- shadow.OS -> /etc/shadow.OS
 | 
						|
    Note: the *.OS files are the backups for the original /etc/passwd, shadow, group files
 | 
						|
 | 
						|
    Arguments:
 | 
						|
      $cfmdir
 | 
						|
    Returns:
 | 
						|
      0 - initialize successfully
 | 
						|
      1 - initialize failed
 | 
						|
    Globals:
 | 
						|
      none 
 | 
						|
    Error:
 | 
						|
      none
 | 
						|
    Example:
 | 
						|
      xCAT::CFMUtils->initCFMdir($cfmdir);
 | 
						|
 | 
						|
=cut
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
sub initCFMdir
 | 
						|
{
 | 
						|
    my ($class, $cfmdir) = @_;
 | 
						|
 | 
						|
    # below system files will be synced to all compute nodes
 | 
						|
    my @sysfiles = ("/etc/hosts");
 | 
						|
 | 
						|
    # the /etc/passwd, shadow, group files will be merged 
 | 
						|
    my @userfiles = ("/etc/passwd", "/etc/shadow", "/etc/group");
 | 
						|
 | 
						|
    # create the cfmdir
 | 
						|
    if (! -d $cfmdir)
 | 
						|
    {
 | 
						|
        mkpath $cfmdir;
 | 
						|
    }
 | 
						|
 | 
						|
    # backup the OS files and create links under cfmdir
 | 
						|
    foreach my $file (@userfiles)
 | 
						|
    {
 | 
						|
        my $backup = $file.".OS";
 | 
						|
        if (! -e $backup)
 | 
						|
        {
 | 
						|
            copy($file, $backup);
 | 
						|
        }
 | 
						|
 | 
						|
        if (! -e "$cfmdir/".basename($backup))
 | 
						|
        {
 | 
						|
            symlink($backup, "$cfmdir/".basename($backup));
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    # Initialize CFM directory and related files
 | 
						|
    if (! -d "$cfmdir/etc")
 | 
						|
    {
 | 
						|
        mkpath "$cfmdir/etc";
 | 
						|
    }
 | 
						|
 | 
						|
    # link the system files
 | 
						|
    foreach my $file (@sysfiles)
 | 
						|
    {
 | 
						|
        symlink($file, "$cfmdir/$file");
 | 
						|
    }
 | 
						|
    # touch and link the merge files for /etc/passwd, shadow, group
 | 
						|
    foreach my $file (@userfiles)
 | 
						|
    {
 | 
						|
        my $merge = $file.".merge";
 | 
						|
        if (! -e "$merge")
 | 
						|
        {
 | 
						|
            xCAT::Utils->runcmd("touch $merge", -1);
 | 
						|
        }
 | 
						|
 | 
						|
        if (! -e "$cfmdir/$merge")
 | 
						|
        {
 | 
						|
            symlink($merge, "$cfmdir/$merge");
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
 | 
						|
=head3 updateUserInfo
 | 
						|
    Update the /etc/passwd, shadow, group merge files under specified CFM directory
 | 
						|
 | 
						|
    Arguments:
 | 
						|
      $cfmdir - CFM directory for osimage      
 | 
						|
    Returns:
 | 
						|
      0 - update successfully
 | 
						|
      1 - update failed
 | 
						|
    Globals:
 | 
						|
      $::CALLBACK
 | 
						|
    Error:
 | 
						|
      none
 | 
						|
    Example:
 | 
						|
      my $ret = xCAT::CFMUtils->updateUserInfo($cfmdir);
 | 
						|
 | 
						|
=cut
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
sub updateUserInfo {
 | 
						|
    my ($class, $cfmdir) = @_;
 | 
						|
 | 
						|
    my @userfiles = ("/etc/passwd", "/etc/shadow", "/etc/group");
 | 
						|
 | 
						|
    my @osfiles = glob("$cfmdir/*.OS");
 | 
						|
    if (!@osfiles)
 | 
						|
    {
 | 
						|
        if ($::VERBOSE)
 | 
						|
        {
 | 
						|
            my $rsp = {};
 | 
						|
            $rsp->{data}->[0] = "Skiping the update of the /etc/passwd, shadow, group merge files under the CFM directory.";
 | 
						|
            xCAT::MsgUtils->message("I", $rsp, $::CALLBACK);
 | 
						|
        }
 | 
						|
	return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    foreach my $file (@userfiles)
 | 
						|
    {
 | 
						|
        my @oldrecords = ();
 | 
						|
        my @newrecords = ();
 | 
						|
        my $backup = basename($file).".OS";
 | 
						|
 | 
						|
        # get the records from /etc/passwd, shadow, group file and backup files(.OS files)
 | 
						|
        # and all the files from /install/osimages/$imgname/cfmdir directory  
 | 
						|
        foreach my $userinfo ($file, "$cfmdir/$backup") 
 | 
						|
        {
 | 
						|
            my $fp;
 | 
						|
            open($fp, $userinfo);
 | 
						|
            my @records = ();
 | 
						|
            while (<$fp>)
 | 
						|
            {
 | 
						|
                my $line = xCAT::CFMUtils->trim($_);
 | 
						|
                if (($line =~ /^#/) || ($line =~ /^\s*$/ ))
 | 
						|
                { #comment line or blank line
 | 
						|
                    next;
 | 
						|
                } else
 | 
						|
                {    
 | 
						|
                    push @records, $line;
 | 
						|
                }   
 | 
						|
            }
 | 
						|
            close($fp);
 | 
						|
 | 
						|
            # check the records from /etc/passwd, shadow, group file or backup
 | 
						|
            if ($userinfo =~ /^\/etc/ )
 | 
						|
            {
 | 
						|
                @newrecords = @records;
 | 
						|
            } else {
 | 
						|
                @oldrecords = @records;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        # update the merge file
 | 
						|
        my $mergefile = $cfmdir."/".$file.".merge";
 | 
						|
        my @diff = xCAT::CFMUtils->arrayops("D", \@newrecords, \@oldrecords);
 | 
						|
        # output the diff to merge files
 | 
						|
        my $fp;
 | 
						|
        open($fp, '>', $mergefile);
 | 
						|
        if (@diff)
 | 
						|
        {
 | 
						|
            for my $record (@diff)
 | 
						|
            {
 | 
						|
               print $fp "$record\n";
 | 
						|
            }
 | 
						|
        }
 | 
						|
        close ($fp);
 | 
						|
        
 | 
						|
    }
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
=head3 setCFMSynclistFile
 | 
						|
    Set osimage synclists attribute for CFM function, the CMF synclist file is:
 | 
						|
    /install/osimages/<imagename>/synclist.cfm
 | 
						|
 | 
						|
    Arguments:
 | 
						|
      $imagename - the specified osimage name
 | 
						|
    Returns:
 | 
						|
      It returns the cfmdir path if it is defined for an osimage object
 | 
						|
    Globals:
 | 
						|
      $::CALLBACK
 | 
						|
    Error:
 | 
						|
      none
 | 
						|
    Example:
 | 
						|
      my $cfmdir = xCAT::CFMUtils->setCFMSynclistFile($imagename);
 | 
						|
      if ($cfmdir) { # update the CFM synclist file }
 | 
						|
=cut
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
sub setCFMSynclistFile {
 | 
						|
    my ($class, $img) = @_;
 | 
						|
 | 
						|
    my $cfmdir;
 | 
						|
    my $synclists;
 | 
						|
    my $cfmsynclist = "/install/osimages/$img/synclist.cfm";
 | 
						|
 | 
						|
    # get the cfmdir and synclists attributes
 | 
						|
    my $osimage_t = xCAT::Table->new('osimage');
 | 
						|
    my $records = $osimage_t->getAttribs({imagename=>$img}, 'cfmdir', 'synclists');
 | 
						|
    if ($records)
 | 
						|
    {
 | 
						|
        if ($records->{'cfmdir'}) {$cfmdir = $records->{'cfmdir'}}
 | 
						|
        if ($records->{'synclists'}) {$synclists = $records->{'synclists'}}
 | 
						|
    } else {
 | 
						|
        if ($::VERBOSE)
 | 
						|
        {
 | 
						|
            my $rsp = {};
 | 
						|
            $rsp->{data}->[0] = "There are no records for cfmdir and synclists attribute in the osimage:$img. There is nothing to process.";
 | 
						|
            xCAT::MsgUtils->message("I", $rsp, $::CALLBACK);
 | 
						|
        }
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    # no cfmdir defined, return directly
 | 
						|
    if (!$cfmdir)
 | 
						|
    {
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    my $found = 0;
 | 
						|
    my $index = 0; 
 | 
						|
    if ($synclists)
 | 
						|
    {
 | 
						|
        # the synclists is a comma separated list
 | 
						|
        my @lists = split(/,/, $synclists);
 | 
						|
        foreach my $synclist (@lists)
 | 
						|
        {
 | 
						|
            # find the synclist configuration for CFM
 | 
						|
            if ($synclist eq $cfmsynclist) 
 | 
						|
            {
 | 
						|
                $found = 1;
 | 
						|
                last;
 | 
						|
            }
 | 
						|
            $index += 1;
 | 
						|
        }
 | 
						|
        if ($found == 0)
 | 
						|
        {
 | 
						|
            # the CFM synclist is not defined, append it to $synclists
 | 
						|
            $synclists = "$synclists,$cfmsynclist"; 
 | 
						|
            # set the synclists attribute 
 | 
						|
            $osimage_t->setAttribs({imagename=>$img}, {'synclists' => $synclists});
 | 
						|
        }
 | 
						|
    } else {
 | 
						|
        # no synclists defined, set it to CFM synclist file
 | 
						|
        if ($cfmdir) { $synclists = $cfmsynclist; }
 | 
						|
        $osimage_t->setAttribs({imagename=>$img}, {'synclists' => $synclists});
 | 
						|
    }
 | 
						|
 | 
						|
    return $cfmdir;   
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
 | 
						|
=head3 updateCFMSynclistFile
 | 
						|
    Update the synclist file(/install/osimages/<imagename>/synclist.cfm) for CFM function. 
 | 
						|
    It will recursively scan the files under cfmdir directory and then add them to CFM synclist file.
 | 
						|
    Note:
 | 
						|
    The files with suffix ".append" will be appended to the dest file(records in "APPEND:" section).
 | 
						|
    The files with suffix ".merge" will be merged to the dest file(records in "MERGE:" section).
 | 
						|
 | 
						|
    In addition, it will reserve the user specified records in the synclist file. The example synclist file:
 | 
						|
	<cfmdir>/etc/hosts -> /etc/hosts
 | 
						|
	/root/install.log -> /tmp/install.log
 | 
						|
	...
 | 
						|
 | 
						|
	APPEND:
 | 
						|
	<cfmdir>/etc/hosts.append -> /etc/hosts
 | 
						|
	/root/install.log.syslog -> /tmp/install.log
 | 
						|
	...
 | 
						|
	EXECUTE:
 | 
						|
	...
 | 
						|
	EXECUTEALWAYS:
 | 
						|
	...
 | 
						|
	MERGE:
 | 
						|
	<cfmdir>/etc/group.merge -> /etc/group
 | 
						|
	<cfmdir>/etc/shadow.merge -> /etc/shadow
 | 
						|
	<cfmdir>/etc/passwd.merge -> /etc/passwd
 | 
						|
 | 
						|
    Arguments:
 | 
						|
      \@imagenames - reference to the osimage names array
 | 
						|
    Returns:
 | 
						|
      0 - update successfully
 | 
						|
      1 - update failed
 | 
						|
    Globals:
 | 
						|
      $::CALLBACK
 | 
						|
    Error:
 | 
						|
      none
 | 
						|
    Example:
 | 
						|
      my $ret = CAT::CFMUtils->updateCFMSynclistFile(\@imagenames);
 | 
						|
 | 
						|
=cut
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
sub updateCFMSynclistFile {
 | 
						|
    my ($class, $imgs) = @_;
 | 
						|
 | 
						|
    my @osimgs = @$imgs;
 | 
						|
    if (!@osimgs)
 | 
						|
    {
 | 
						|
        my $rsp = {};
 | 
						|
        $rsp->{error}->[0] = "No osimage names specified to process.";
 | 
						|
        xCAT::MsgUtils->message("E", $rsp, $::CALLBACK);
 | 
						|
        return 1;
 | 
						|
    }
 | 
						|
 | 
						|
    foreach my $osimg (@osimgs)
 | 
						|
    {
 | 
						|
        my $cfmdir;
 | 
						|
        $cfmdir = xCAT::CFMUtils->setCFMSynclistFile($osimg);
 | 
						|
        if ($cfmdir)   # check for /install/osiamges/$osimg/cfmdir
 | 
						|
        {
 | 
						|
            my $cfmsynclist = "/install/osimages/$osimg/synclist.cfm";
 | 
						|
            if (! -d $cfmdir)
 | 
						|
            {
 | 
						|
                # skip this one go on to the next image, nothing to do for 
 | 
						|
                # CFMUtils in this image
 | 
						|
                next;
 | 
						|
            }
 | 
						|
            # create the parent directory of CFM synclist file
 | 
						|
            if (! -d dirname($cfmsynclist))
 | 
						|
            {
 | 
						|
                mkpath dirname($cfmsynclist);
 | 
						|
            }
 | 
						|
 | 
						|
            # update /etc/passwd, shadow, group merge files
 | 
						|
            my $ret = xCAT::CFMUtils->updateUserInfo($cfmdir);
 | 
						|
            if ($ret !=0 )
 | 
						|
            {
 | 
						|
                my $rsp = {};
 | 
						|
                $rsp->{error}->[0] = 
 | 
						|
                "Update /etc/passwd, shadow, group merge files failed.";
 | 
						|
                xCAT::MsgUtils->message("E", $rsp, $::CALLBACK);
 | 
						|
                return 1;
 | 
						|
            }
 | 
						|
 | 
						|
            # recursively list the files under cfm directory 
 | 
						|
            my @files = ();
 | 
						|
            find ( sub { push @files, $File::Find::name if (! -d) }, $cfmdir);
 | 
						|
            if (!@files) # not files under cfm directory, skip to next loop 
 | 
						|
            {
 | 
						|
                next;
 | 
						|
            }
 | 
						|
 | 
						|
            my $fp;
 | 
						|
            open($fp, '>', $cfmsynclist);
 | 
						|
            my @mergefiles = ();
 | 
						|
            my @appendfiles = ();
 | 
						|
            foreach my $file (@files)
 | 
						|
            {
 | 
						|
                my $name = basename($file);
 | 
						|
                #TODO: find a better way to get the suffix 
 | 
						|
                my $suffix = ($name =~ m/([^.]+)$/)[0];
 | 
						|
                my $dest = substr($file, length($cfmdir));
 | 
						|
                if ($suffix eq "OS") # skip the backup files
 | 
						|
                {
 | 
						|
                    next;
 | 
						|
                } elsif ($suffix eq "merge") # merge file
 | 
						|
                {
 | 
						|
                    push(@mergefiles, $file);
 | 
						|
                } elsif ($suffix eq "append") { # append file
 | 
						|
                    push(@appendfiles, $file); 
 | 
						|
                } else { # output the syncing files maintained by CFM
 | 
						|
                    print $fp "$file -> $dest\n";
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            # output the APPEND records maintained by CFM
 | 
						|
            if (@appendfiles) {
 | 
						|
                print $fp "APPEND:\n";
 | 
						|
            }
 | 
						|
            foreach my $file (@appendfiles)
 | 
						|
            { 
 | 
						|
                my $dest = substr($file, length($cfmdir), length($file) - length(".append") - length($cfmdir));
 | 
						|
                print $fp "$file -> $dest\n";
 | 
						|
            }
 | 
						|
 | 
						|
            # output the MERGE records maintained by CFM
 | 
						|
            if (@mergefiles) {
 | 
						|
                print $fp "MERGE:\n";
 | 
						|
            }
 | 
						|
            foreach my $file (@mergefiles)
 | 
						|
            {
 | 
						|
                my @userfiles = ("/etc/passwd", "/etc/shadow", "/etc/group");
 | 
						|
                my $dest = substr($file, length($cfmdir), length($file) - length(".merge") - length($cfmdir));
 | 
						|
                # only /etc/passwd, /etc/shadow, /etc/groups merging is supported
 | 
						|
                if (grep(/$dest/, @userfiles)) {		
 | 
						|
                    print $fp "$file -> $dest\n";
 | 
						|
                }
 | 
						|
            }
 | 
						|
            
 | 
						|
            # close the file 
 | 
						|
            close($fp);   
 | 
						|
        }
 | 
						|
    }
 | 
						|
 
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
=head3 setCFMPkglistFile
 | 
						|
    Set the pkglist attribute of linuximage object for CFM function
 | 
						|
 | 
						|
    Arguments:
 | 
						|
      $imagename - the specified linuximage name
 | 
						|
    Returns:
 | 
						|
      0 - update successfully
 | 
						|
      1 - update failed
 | 
						|
    Globals:
 | 
						|
      $::CALLBACK
 | 
						|
    Error:
 | 
						|
      none
 | 
						|
    Example:
 | 
						|
      my $ret = xCAT::CFMUtils->setCFMPkglistFile($imagename);
 | 
						|
=cut
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
sub setCFMPkglistFile {
 | 
						|
    my ($class, $img) = @_;
 | 
						|
 | 
						|
    my $pkglists = "";
 | 
						|
    my $cfmpkglist = "/install/osimages/$img/pkglist.cfm";
 | 
						|
 | 
						|
    # get the pkglist files
 | 
						|
    my $linuximage_t = xCAT::Table->new('linuximage');
 | 
						|
    my $records = $linuximage_t->getAttribs({imagename => $img}, 'pkglist');
 | 
						|
    if ($records)
 | 
						|
    {
 | 
						|
        if ($records->{'pkglist'}) { $pkglists = $records->{'pkglist'}; }
 | 
						|
    } else 
 | 
						|
    {
 | 
						|
        if ($::VERBOSE)
 | 
						|
        {
 | 
						|
            my $rsp = {};
 | 
						|
            $rsp->{data}->[0] = "There are no records for pkglist attribute in the linuximage:$img. There is nothing to process.";
 | 
						|
            xCAT::MsgUtils->message("I", $rsp, $::CALLBACK);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    my $found = 0;
 | 
						|
    if ($pkglists)
 | 
						|
    {
 | 
						|
        foreach my $pkglist (split(/,/, $pkglists))
 | 
						|
        {
 | 
						|
            if ($pkglist eq $cfmpkglist) # the pkglist file for CFM is found, exit the loop 
 | 
						|
            {
 | 
						|
                $found = 1;
 | 
						|
                last;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        # the pkglist file for CFM is not found, append it to $pkglits 
 | 
						|
        if (!$found) 
 | 
						|
        {
 | 
						|
            $pkglists = "$pkglists,$cfmpkglist"; 
 | 
						|
            # set the pkglist attribute for linuximage
 | 
						|
            $linuximage_t->setAttribs({imagename => $img}, {'pkglist' => $pkglists});
 | 
						|
        } 
 | 
						|
    } else 
 | 
						|
    {
 | 
						|
        # the pkglist file for linuximage is not defined, set it to $cfmpkglist
 | 
						|
        $pkglists = $cfmpkglist;
 | 
						|
        $linuximage_t->setAttribs({imagename => $img}, {'pkglist' => $pkglists});
 | 
						|
    }
 | 
						|
 | 
						|
    return 0;   
 | 
						|
}
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
 | 
						|
=head3 updateCFMPkglistFile
 | 
						|
    Update the ospkglist file
 | 
						|
 | 
						|
    Arguments:
 | 
						|
      $imagename - the specified linuximage name
 | 
						|
      @curospkgs - the currently selected OS packages list
 | 
						|
    Returns:
 | 
						|
      0 - update successfully
 | 
						|
      1 - update failed
 | 
						|
    Globals:
 | 
						|
      none
 | 
						|
    Error:
 | 
						|
      none
 | 
						|
    Example:
 | 
						|
      my $ret = CAT::CFMUtils->updateCFMPkglistFile($imagename, @cur_selected_pkgs);
 | 
						|
 | 
						|
=cut
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
sub updateCFMPkglistFile {
 | 
						|
    my ($class, $img, $ospkgs) = @_;
 | 
						|
     
 | 
						|
    my @cur_selected = @$ospkgs;
 | 
						|
    my $cfmpkglist = "/install/osimages/$img/pkglist.cfm";
 | 
						|
 | 
						|
    my $ret = xCAT::CFMUtils->setCFMPkglistFile($img);
 | 
						|
    if ($ret)
 | 
						|
    {
 | 
						|
        my $rsp = {};
 | 
						|
        $rsp->{error}->[0] = "Set pkglist attribute for CFM failed.";
 | 
						|
        xCAT::MsgUtils->message("E", $rsp, $::CALLBACK);
 | 
						|
        return 1;
 | 
						|
    }
 | 
						|
 | 
						|
    # check the parent directory of cfmpkglist file
 | 
						|
    if (! -d dirname($cfmpkglist))
 | 
						|
    {
 | 
						|
        mkpath dirname($cfmpkglist);
 | 
						|
    }
 | 
						|
 | 
						|
    # get previous selected and removed OS packages list from pkglist file
 | 
						|
    my ($pre_selected_ref, $pre_removed_ref) = xCAT::CFMUtils->getPreOSpkgsList($cfmpkglist);
 | 
						|
    my @pre_selected = @$pre_selected_ref;
 | 
						|
    my @pre_removed = @$pre_removed_ref;
 | 
						|
 | 
						|
    # get the #INCLUDE file from cfmpkglist file
 | 
						|
    my @incfiles = xCAT::CFMUtils->getIncludefiles($cfmpkglist);
 | 
						|
    # get the packages list in the #INCLUDE files
 | 
						|
    my @basepkgs = ();
 | 
						|
    foreach my $inc (@incfiles)
 | 
						|
    {
 | 
						|
        my ($selected_ref, $removed_ref) = xCAT::CFMUtils->getPreOSpkgsList($inc);
 | 
						|
        my @selected = @$selected_ref;
 | 
						|
        @basepkgs = xCAT::CFMUtils->arrayops("U", \@basepkgs, \@selected);
 | 
						|
    }
 | 
						|
 | 
						|
    # get diff between previous and current selected OS packages lists    
 | 
						|
    my @diff = xCAT::CFMUtils->getPkgsDiff(\@pre_selected, \@cur_selected);
 | 
						|
 
 | 
						|
    # merge the diff to previous removed OS packages list
 | 
						|
    my @all_removed = xCAT::CFMUtils->arrayops("U", \@pre_removed, \@diff);
 | 
						|
 | 
						|
    # get the rollbacked OS packages list, the packages are existing in both removed and selected lists
 | 
						|
    # if so, we should remove the rollbacked OS packages from removed list
 | 
						|
    my @rollback = xCAT::CFMUtils->arrayops("I", \@all_removed, \@cur_selected);
 | 
						|
    my @cur_removed = xCAT::CFMUtils->arrayops("D", \@all_removed, \@rollback);
 | 
						|
 | 
						|
    # remove the BASE packages from selected pakages
 | 
						|
    @basepkgs = xCAT::CFMUtils->arrayops("I", \@basepkgs, \@cur_selected);
 | 
						|
    @cur_selected = xCAT::CFMUtils->arrayops("D", \@cur_selected, \@basepkgs);
 | 
						|
 | 
						|
    # update the pkglist file
 | 
						|
    my $fp;
 | 
						|
    open($fp, '>', $cfmpkglist);
 | 
						|
    foreach my $inc (@incfiles)
 | 
						|
    {
 | 
						|
        print $fp "#INCLUDE:$inc#\n";
 | 
						|
    }
 | 
						|
    # the pacakges be installed
 | 
						|
    if (@cur_selected)
 | 
						|
    {
 | 
						|
        foreach my $pkg (@cur_selected)
 | 
						|
        {
 | 
						|
            print $fp "$pkg\n";
 | 
						|
        }
 | 
						|
    }
 | 
						|
    # the packages be removed
 | 
						|
    if (@cur_removed)
 | 
						|
    {
 | 
						|
        foreach my $pkg (@cur_removed)
 | 
						|
        {
 | 
						|
            print $fp "-$pkg\n";
 | 
						|
        }
 | 
						|
    }
 | 
						|
    # close the file
 | 
						|
    close($fp);
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
 | 
						|
=head3 getPreOSpkgsList
 | 
						|
    Get previously selected and removed OS packages lists from pkglist file
 | 
						|
 | 
						|
    Arguments:
 | 
						|
      $ospkglist - the path for ospkglist file
 | 
						|
    Returns:
 | 
						|
      refs for selected and removed OS packages arrays
 | 
						|
    Globals:
 | 
						|
      none
 | 
						|
    Error:
 | 
						|
      none
 | 
						|
    Example:
 | 
						|
      my ($pre_selected_ref, $pre_removed_ref) = xCAT::CFMUtils->getPreOSpkgsList($ospkglist);
 | 
						|
      my @pre_selected = @$pre_selected_ref;
 | 
						|
      my @pre_removed = @$pre_removed_ref;
 | 
						|
 | 
						|
=cut
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
sub getPreOSpkgsList {
 | 
						|
    my ($class, $pkglist) = @_;
 | 
						|
    my @selected = ();
 | 
						|
    my @removed = ();
 | 
						|
    my @pkglistfiles = ();
 | 
						|
 | 
						|
    # get the #INCLUDE file from cfmpkglist file
 | 
						|
    my @incfiles = xCAT::CFMUtils->getIncludefiles($pkglist);
 | 
						|
    foreach my $inc (@incfiles)
 | 
						|
    {
 | 
						|
        push @pkglistfiles, $inc;
 | 
						|
    }
 | 
						|
    # assume the #INCLUDE file includes the BASE packages
 | 
						|
    push @pkglistfiles, $pkglist;
 | 
						|
 | 
						|
    foreach my $file (@pkglistfiles)
 | 
						|
    {
 | 
						|
        my $pkglistfp;
 | 
						|
        open($pkglistfp, xCAT::CFMUtils->trim($file));
 | 
						|
        while (<$pkglistfp>)
 | 
						|
        {
 | 
						|
            my $line = xCAT::CFMUtils->trim($_);
 | 
						|
            if (($line =~ /^#/) || ($line =~ /^\s*$/ ))
 | 
						|
            { #comment line or blank line
 | 
						|
                next;
 | 
						|
            } else
 | 
						|
            {
 | 
						|
                if ($line =~ /^-/)
 | 
						|
                { # the package be removed
 | 
						|
                    push @removed, substr($line, 1);
 | 
						|
                } else
 | 
						|
                { # the package be installed
 | 
						|
                    push @selected, $line;
 | 
						|
                } 
 | 
						|
            }
 | 
						|
        }    
 | 
						|
        close($pkglistfp);
 | 
						|
    }
 | 
						|
 | 
						|
    # delete the removed packages from selected list
 | 
						|
    my @intersection = xCAT::CFMUtils->arrayops("I", \@removed, \@selected);
 | 
						|
    @selected = xCAT::CFMUtils->arrayops("D", \@selected, \@intersection);
 | 
						|
 | 
						|
    return (\@selected, \@removed);
 | 
						|
}
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
 | 
						|
=head3 getPkgsDiff
 | 
						|
    Get the differences between previous and current packages list
 | 
						|
 | 
						|
    Arguments:
 | 
						|
      @pre - previous selected packages list
 | 
						|
      @cur - current selected packages list
 | 
						|
    Returns:
 | 
						|
      @diff - the differencen list
 | 
						|
    Globals:
 | 
						|
      none
 | 
						|
    Error:
 | 
						|
      none
 | 
						|
    Example:
 | 
						|
      my @diff = xCAT::CFMUtils->getPkgsDiff(\@pre_selected, \@cur_selected);
 | 
						|
 | 
						|
=cut
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
sub getPkgsDiff {
 | 
						|
    my ($class, $pre, $cur) = @_;
 | 
						|
 | 
						|
    # get the intersection firstly
 | 
						|
    my @tmp = xCAT::CFMUtils->arrayops("I", \@$pre, \@$cur);
 | 
						|
 | 
						|
    # get the difference
 | 
						|
    my @diff = xCAT::CFMUtils->arrayops("D", \@$pre, \@tmp);
 | 
						|
    #print Dumper(@diff);
 | 
						|
 | 
						|
    return @diff;
 | 
						|
}
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
 | 
						|
=head3 getIncludefiles 
 | 
						|
    Get the #INCLUDE files from the given file 
 | 
						|
 | 
						|
    Arguments:
 | 
						|
      $file - the given file
 | 
						|
    Returns:
 | 
						|
      @files - the #INCLUDE files list
 | 
						|
    Globals:
 | 
						|
      none
 | 
						|
    Error:
 | 
						|
      none
 | 
						|
    Example:
 | 
						|
      my @diff = xCAT::CFMUtils->getIncludefiles($file);
 | 
						|
 | 
						|
=cut
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
sub getIncludefiles {
 | 
						|
    my ($class, $file) = @_;
 | 
						|
    my @files = ();
 | 
						|
 | 
						|
    my $fp;
 | 
						|
    open($fp, $file);
 | 
						|
    while (<$fp>)
 | 
						|
    {
 | 
						|
        my $line = xCAT::CFMUtils->trim($_);
 | 
						|
        if ($line =~ /^\s*$/)
 | 
						|
        { # blank line
 | 
						|
            next;
 | 
						|
        }
 | 
						|
        # find the #INCLUDE line
 | 
						|
        if ($line =~ /^\s*#INCLUDE:[^#^\n]+#/)
 | 
						|
        {
 | 
						|
            #print "The line is: [$line]\n";
 | 
						|
            my $incfile = substr($line, length("#INCLUDE:"), length($line)-length("#INCLUDE:")-1);
 | 
						|
            push @files, $incfile;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    close($fp);
 | 
						|
 | 
						|
    return @files;
 | 
						|
}
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
 | 
						|
=head3 trim
 | 
						|
    Strip left and right whitspaces for a string 
 | 
						|
 | 
						|
    Arguments:
 | 
						|
      $string
 | 
						|
    Returns:
 | 
						|
      @string
 | 
						|
    Globals:
 | 
						|
      none
 | 
						|
    Error:
 | 
						|
      none
 | 
						|
    Example:
 | 
						|
      my @new_string = xCAT::CFMUtils->trim($string);
 | 
						|
 | 
						|
=cut
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
sub trim {
 | 
						|
    my ($class, $string) = @_;
 | 
						|
 | 
						|
    # trim the left whitespaces
 | 
						|
    $string =~ s/^\s*//;
 | 
						|
 | 
						|
    # trim the right whitespaces
 | 
						|
    $string =~ s/\s*$//;
 | 
						|
 | 
						|
    return $string;
 | 
						|
}
 | 
						|
 | 
						|
# Function: compute Union, Intersection or Difference of unique lists
 | 
						|
# Usage: arrayops ("U"/"I"/"D", @a, @b)
 | 
						|
# Return: @union/@intersection/@difference
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
 | 
						|
=head3 arrayops
 | 
						|
    Compute Union/Intersection/Difference for 2 unique lists
 | 
						|
 | 
						|
    Arguments:
 | 
						|
      $flag - "U"/"I"/"D"
 | 
						|
      \@array1 - reference to an arrary
 | 
						|
      \@array2 - reference to an arrary
 | 
						|
    Returns:
 | 
						|
      @union/@intersection/@difference
 | 
						|
    Globals:
 | 
						|
      none
 | 
						|
    Error:
 | 
						|
      none
 | 
						|
    Example:
 | 
						|
      my @array = xCAT::CFMUtils->arrayops(\@array1, \@array2);
 | 
						|
 | 
						|
=cut
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
sub arrayops {
 | 
						|
    my ($class, $ops, $array1, $array2) = @_;
 | 
						|
 | 
						|
    my @union = ();
 | 
						|
    my @intersection = ();
 | 
						|
    my @difference = ();
 | 
						|
    my %count = ();
 | 
						|
    foreach my $element (@$array1, @$array2) 
 | 
						|
    { 
 | 
						|
        $count{$element}++ 
 | 
						|
    }
 | 
						|
 | 
						|
    foreach my $element (keys %count) {
 | 
						|
        push @union, $element;
 | 
						|
        push @{ $count{$element} > 1 ? \@intersection : \@difference }, $element;
 | 
						|
    }
 | 
						|
 | 
						|
    if ($ops eq "U") { return @union; }
 | 
						|
   
 | 
						|
    if ($ops eq "I") { return @intersection; }
 | 
						|
 | 
						|
    if ($ops eq "D") { return @difference; }
 | 
						|
 | 
						|
    #return (\@union, \@intersection, \@difference);
 | 
						|
}
 |