# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
#-------------------------------------------------------

=head1
  xCAT plugin package to handle Kit management
=cut

#-------------------------------------------------------
package xCAT_plugin::kit;

BEGIN
{
    $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat';
}
use lib "$::XCATROOT/lib/perl";

use xCAT::Table;
use xCAT::Utils;
use xCAT::MsgUtils;
use Getopt::Long;

#use Data::Dumper;
use File::Basename;
use File::Path;
use Cwd;

my $kitconf = "kit.conf";

# kit framework version for this xcat.
$::KITFRAMEWORK = "2";

# this code is compatible with other kits that are at framework 0 or 1.
$::COMPATIBLE_KITFRAMEWORKS = "0,1,2";

my $debianflag = 0;
my $tempstring = xCAT::Utils->osver(all);
if ($tempstring =~ /debian/ || $tempstring =~ /ubuntu/) {
    $debianflag = 1;

    #    print "debian";
}

#-------------------------------------------------------

=head3  handled_commands

Return list of commands handled by this plugin

=cut

#-------------------------------------------------------

sub handled_commands
{
    return {
        lskit            => "kit",
        addkit           => "kit",
        rmkit            => "kit",
        lskitcomp        => "kit",
        addkitcomp       => "kit",
        rmkitcomp        => "kit",
        chkkitcomp       => "kit",
        lskitdeployparam => "kit",
    };
}

#-------------------------------------------------------

=head3  process_request

  Process the command

=cut

#-------------------------------------------------------
sub process_request
{

    my $request         = shift;
    my $callback        = shift;
    my $request_command = shift;
    $::CALLBACK = $callback;
    $::args     = $request->{arg};

    my $lock;
    my $locked = xCAT::Utils->is_locked("kit", 1);
    if (!locked) {
        $lock = xCAT::Utils->acquire_lock("kit", 1);
        unless ($lock) {
            $callback->({ error => ["Can not acquire lock."], errorcode => [1] });
            return 1;
        }
    } else {
        if ($::PID and $::PID != $$) {
            $callback->({ error => ["Can not acquire lock, another process is running."], errorcode => [1] });
            return 1;
        }

        $::PID = $$;
    }

    my ($sysos, $sysver) = split /,/, $tempstring;
    if ( ($sysos =~ /rh/) && ($sysver ge 8) ) {
        $callback->({ error => ["KITs are not supported on rhel8 or above"], errorcode => [1] });
        return 1;
    }

    my $command = $request->{command}->[0];
    my $rc;

    if ($command eq "lskit") {
        $rc = lskit($request, $callback, $request_command);
    } elsif ($command eq "addkit") {
        $rc = addkit($request, $callback, $request_command);
        system("rm -rf /tmp/tmpkit/");
    } elsif ($command eq "rmkit") {
        $rc = rmkit($request, $callback, $request_command);
    } elsif ($command eq "lskitcomp") {
        $rc = lskitcomp($request, $callback, $request_command);
    } elsif ($command eq "addkitcomp") {
        $rc = addkitcomp($request, $callback, $request_command);
    } elsif ($command eq "rmkitcomp") {
        $rc = rmkitcomp($request, $callback, $request_command);
    } elsif ($command eq "chkkitcomp") {
        $rc = chkkitcomp($request, $callback, $request_command);
    } elsif ($command eq "lskitdeployparam") {
        $rc = lskitdeployparam($request, $callback, $request_command);
    } else {
        $callback->({ error => ["Error: $command not found in this module."], errorcode => [1] });
        xCAT::Utils->release_lock($lock, 1);
        return 1;
    }

    if ($lock) {
        xCAT::Utils->release_lock($lock, 1);
    }
    return $rc;

}

#-------------------------------------------------------

=head3  get_highest_version

  Return the highest version and release for a list of
  kit, kitrepo, or kitcomponent names.

  Input: @entries: the arrary contains all the data
         $key: the key name in entries hash.
         $version: compare version nums.
         $release: compare release nums.

=cut

#-------------------------------------------------------
sub get_highest_version
{
    my $key     = shift;
    my $version = shift;
    my $release = shift;
    my @entries = @_;

    my $highest;

    foreach my $entry (@entries) {
        $highest = $entry if (!$highest);

        my $rc = compare_version($highest, $entry, $key, $version, $release);
        if ($rc == 1) {
            $highest = $entry;
        }
    }

    return $highest->{$key};
}

#-------------------------------------------------------

=head3  compare_version

  Compare the version and release between two kit/kitrepo/kitcomp

  Input: $highest: the current highest version or release
         $entry: the new entry
         $key: the key name in entries hash.
         $version: compare version nums.
         $release: compare release nums.

=cut

#-------------------------------------------------------
sub compare_version
{
    my $highest = shift;
    my $entry   = shift;
    my $key     = shift;
    my $version = shift;
    my $release = shift;

    my @a1 = split(/\./, $highest->{$version});
    my @a2 = split(/\./, $entry->{$version});

    my ($len, $num1, $num2);
    if (($release = 'release') &&
        (defined $highest->{$release}) && (defined $entry->{$release})) {
        $len = (scalar(@a1) > scalar(@a2) ? scalar(@a1) : scalar(@a2));
    } else {
        $len = (scalar(@a1) < scalar(@a2) ? scalar(@a1) : scalar(@a2));
    }

    $#a1 = $len - 1;  # make the arrays the same length before appending release
    $#a2 = $len - 1;

    if ($release = 'release') {
        push @a1, split(/\./, $highest->{$release});
        push @a2, split(/\./, $entry->{$release});
    }

    $len = (scalar(@a1) < scalar(@a2) ? scalar(@a1) : scalar(@a2));

    $num1 = '';
    $num2 = '';

    for (my $i = 0 ; $i < $len ; $i++)
    {
        my ($d1, $w1) = $a1[$i] =~ /^(\d*)(\w*)/;
        my ($d2, $w2) = $a2[$i] =~ /^(\d*)(\w*)/;


        my $diff = length($d1) - length($d2);
        if ($diff > 0)    # pad d2
        {
            $num1 .= $d1;
            $num2 .= ('0' x $diff) . $d2;
        }
        elsif ($diff < 0)    # pad d1
        {
            $num1 .= ('0' x abs($diff)) . $d1;
            $num2 .= $d2;
        }
        else                 # they are the same length
        {
            $num1 .= $d1;
            $num2 .= $d2;
        }

        if ($w1 && $w2)
        {
            my ($w_to_d1, $w_to_d2) = comp_word($w1, $w2);
            $num1 .= $w_to_d1;
            $num2 .= $w_to_d2;
        }
    }

    # Remove the leading 0s or perl will interpret the numbers as octal
    $num1 =~ s/^0+//;
    $num2 =~ s/^0+//;

    #SuSE Changes
    # if $num1="", the "eval '$num1 $operator $num2'" will fail. So MUSTBE be sure that $num1 is not a "".
    if (length($num1) == 0) { $num1 = 0; }
    if (length($num2) == 0) { $num2 = 0; }

    return 1 if ($num2 > $num1);
    return 0 if ($num2 == $num1);
    return -1;
}

#--------------------------------------------------------------------------------

=head3    comp_word

        Compare version1 word and version2 word. This subroutine can only be used to compare
        one section in version number, and this section cannot start with number.

        Arguments:
                $w1:
                $w2
        Returns:
                if $w1 > $w2, return (1,0)
                if $w1 < $w2, return (0,1)
                if $w1 == $w2, return (undef, undef)
        Globals:
                none

        Example:
                if ($self->comp_word ( "adfadsfa","acc2")
                return (0,1)
        Comments:
                the version word cannot contain ".", and cannot start with number.
                For examples, following version words cannot be compared by this subroutine:
                (123.321, 123.322)  You need use subroutine testVersion to do the version comparision
                (123abc,12bcd)      You need use subroutine testVersion to do the version comparision

=cut

#--------------------------------------------------------------------------------

sub comp_word
{
    my $self = shift;

    my ($w1, $w2) = @_;

    return (undef, undef) if (!$w1 || !$w2);

    my @strList1 = unpack "C*", $w1;
    my @strList2 = unpack "C*", $w2;

    my $len = scalar(@strList1) < scalar(@strList2) ? scalar(@strList1) : scalar(@strList2);

    for (my $i = 0 ; $i < $len ; $i++)
    {
        next if ($strList1[$i] == $strList2[$i]);
        return (0, 1) if ($strList1[$i] < $strList2[$i]);
        return (1, 0);
    }
    return (undef, undef);

}


#-------------------------------------------------------

=head3 check_newinstall

  Check if the given kitcomp list are in NEW_INSTALL_LIST
  or not.  If so, set $::noupgrade all other kitcomp
  depending on this list will be put into NEW_INSTALL_LIST
  also

=cut

#-------------------------------------------------------
sub check_newinstall
{
    my $kitcomponents = shift;
    my @lines         = @_;

    my @kitcomps = split /,/, $kitcomponents;

    foreach my $kitcomp (@kitcomps) {
        last if ($::noupgrade);

        my $match_newinstall = 0;
        foreach my $line (@lines) {
            chomp($line);
            if ($line =~ /^#NEW_INSTALL_LIST#$/) {
                $match_newinstall = 1;
            }
            if ($line =~ /\/$kitcomp$/) {
                if ($match_newinstall) {
                    $::noupgrade = 1;
                }
                last;
            }
        }
    }

}


#-------------------------------------------------------

=head3 get local otherpkgdir

  otherpkgdir can be mixed http source and local dir
  Split otherpkgdir
  get local otherpkgdir

=cut

#-------------------------------------------------------

sub get_local_otherpkgdir
{
    $otherpkgdir = shift;
    my $localdir = "";
    my @tempdirarray = split /,/, $otherpkgdir;
    foreach my $tempdir (@tempdirarray) {
        $tempdir =~ s/(^ +| +$)//g;
        if ($tempdir !~ /^http.*/) {
            $localdir = $tempdir;
            last;
        }
    }
    return $localdir;

}


#-------------------------------------------------------

=head3 assign_to_osimage

  Assign a kit component to osimage

=cut

#-------------------------------------------------------
sub assign_to_osimage
{
    my $osimage  = shift;
    my $kitcomp  = shift;
    my $callback = shift;
    my $tabs     = shift;

    (my $kitcomptable) = $tabs->{kitcomponent}->getAttribs({ kitcompname => $kitcomp }, 'kitname', 'kitreponame', 'basename', 'kitcompdeps', 'kitpkgdeps', 'prerequisite', 'exlist', 'genimage_postinstall', 'postbootscripts', 'driverpacks');
    (my $osimagetable) = $tabs->{osimage}->getAttribs({ imagename => $osimage }, 'provmethod', 'osarch', 'postbootscripts', 'kitcomponents', 'osvers');
    (my $linuximagetable) = $tabs->{linuximage}->getAttribs({ imagename => $osimage }, 'rootimgdir', 'exlist', 'postinstall', 'otherpkglist', 'otherpkgdir', 'driverupdatesrc');

    # Reading installdir.
    my $installdir = xCAT::TableUtils->getInstallDir();
    unless ($installdir) {
        $installdir = '/install';
    }
    $installdir =~ s/\/$//;


    # Reading kitdir
    my $kittable;
    if ($kitcomptable and $kitcomptable->{kitname}) {
        ($kittable) = $tabs->{kit}->getAttribs({ kitname => $kitcomptable->{kitname} }, 'kitdir', 'kitdeployparams');
    }

    # Create osimage direcotry to save kit tmp files
    mkpath("$installdir/osimages/$osimage/kits/");

    # Adding genimage_postinstall script to linuximage.postintall attribute for diskless image or osimage.postbootscripts for diskfull image.
    if ($kitcomptable and $kitcomptable->{genimage_postinstall}) {
        my @kitcompscripts = split ',', $kitcomptable->{genimage_postinstall};
        foreach my $kitcompscript (@kitcompscripts) {
            if ($osimagetable) {
                my $otherpkgdir;
                my $rootimgdir;
                if ($linuximagetable and $linuximagetable->{otherpkgdir}) {
                    $otherpkgdir = $linuximagetable->{otherpkgdir};

                    #support mixed otherpkgdir http and local dir
                    $otherpkgdir = get_local_otherpkgdir($otherpkgdir);

                } else {
                    $callback->({ error => ["Could not read otherpkgdir from osimage $osimage"], errorcode => [1] });
                    return 1;
                }

                if ($osimagetable->{provmethod} =~ /install/) {

                    # If the script is only for diskless node
                    my $cmd = "grep \"#FLAG FOR DISKLESS ONLY#\" $installdir/postscripts/$kitcompscript";
                    my $res = xCAT::Utils->runcmd($cmd, -1);
                    if ($res eq "#FLAG FOR DISKLESS ONLY#") {
                        next;
                    }

                    # for diskfull node
                    my $match = 0;
                    my @scripts = split ',', $osimagetable->{postbootscripts};
                    foreach my $script (@scripts) {
                        if ($script =~ /^KIT_$osimage.postbootscripts/) {
                            $match = 1;
                            last;
                        }
                    }

                    if (!-e "$installdir/postscripts/KIT_$osimage.postbootscripts") {
                        if (open(FILE, ">", "$installdir/postscripts/KIT_$osimage.postbootscripts")) {
                            print FILE "#!/bin/sh\n\n";
                            close(FILE);
                            chmod(0755, "$installdir/postscripts/KIT_$osimage.postbootscripts");
                        }
                    }

                    my @postbootlines;
                    if (open(POSTBOOTSCRIPTS, "<", "$installdir/postscripts/KIT_$osimage.postbootscripts")) {
                        @postbootlines = <POSTBOOTSCRIPTS>;
                        close(POSTBOOTSCRIPTS);
                        if ($::VERBOSE) {
                            $callback->({ data => ["\nCreating osimage postbootscripts file $installdir/postscripts/KIT_$osimage.postbootscripts"] });
                        }
                    }

                    unless (grep(/$kitcompscript/, @postbootlines)) {
                        if (open(NEWLIST, ">>", "$installdir/postscripts/KIT_$osimage.postbootscripts")) {
                            print NEWLIST "otherpkgdir=$otherpkgdir $kitcompscript\n";
                            close(NEWLIST);
                        }
                    }

                    if (!$match) {
                        $osimagetable->{postbootscripts} = "KIT_$osimage.postbootscripts," . $osimagetable->{postbootscripts};
                        $osimagetable->{postbootscripts} =~ s/^,//;
                    }

                } else {

                    # for diskless node

                    if ($linuximagetable and $linuximagetable->{rootimgdir}) {
                        $rootimgdir = $linuximagetable->{rootimgdir} . "/rootimg";
                    } else {
                        $callback->({ error => ["Could not read rootimgdir from osimage $osimage"], errorcode => [1] });
                        return 1;
                    }

                    if (!-e "$installdir/osimages/$osimage/kits/KIT_COMPONENTS.postinstall") {
                        if (open(FILE, ">", "$installdir/osimages/$osimage/kits/KIT_COMPONENTS.postinstall")) {
                            print FILE "#!/bin/sh\n\n";
                            close(FILE);
                            chmod(0755, "$installdir/osimages/$osimage/kits/KIT_COMPONENTS.postinstall");
                        }
                    }

                    my @postinstalllines;
                    if (open(POSTINSTALL, "<", "$installdir/osimages/$osimage/kits/KIT_COMPONENTS.postinstall")) {
                        @postinstalllines = <POSTINSTALL>;
                        close(POSTINSTALL);

                        if ($::VERBOSE) {
                            $callback->({ data => ["\nReading osimage postinstall scripts file $installdir/osimages/$osimage/kits/KIT_COMPONENTS.postinstall"] });
                        }
                    }

                    unless (grep(/$kitcompscript/, @postinstalllines)) {
                        my $deployparams;
                        my @ls;
                        if ($kittable and $kittable->{kitdeployparams} and $kittable->{kitdir}) {
                            if (open(DEPLOYPARAM, "<", "$kittable->{kitdir}/other_files/$kittable->{kitdeployparams}")) {
                                @ls = <DEPLOYPARAM>;
                                close(DEPLOYPARAM);
                            }
                        }

                        my @newlines;
                        foreach my $line (@ls) {
                            if ($line =~ /^#ENV:.+=.+#$/) {
                                chomp($line);
                                $line =~ s/^#ENV://;
                                $line =~ s/#$//;
                                push @newlines, $line;
                            }
                        }

                        $deployparams = join ' ', @newlines;

                        if (open(NEWLIST, ">>", "$installdir/osimages/$osimage/kits/KIT_COMPONENTS.postinstall")) {
                            if ($deployparams) {
                                print NEWLIST "installroot=$rootimgdir otherpkgdir=$otherpkgdir $deployparams $installdir/postscripts/$kitcompscript\n";
                            } else {
                                print NEWLIST "installroot=$rootimgdir otherpkgdir=$otherpkgdir $installdir/postscripts/$kitcompscript\n";
                            }
                            close(NEWLIST);
                        }
                    }

                    my $match = 0;
                    my @scripts = split ',', $linuximagetable->{postinstall};
                    foreach my $script (@scripts) {
                        if ($script =~ /KIT_COMPONENTS.postinstall/) {
                            $match = 1;
                            last;
                        }
                    }

                    if (!$match) {
                        $linuximagetable->{postinstall} = $linuximagetable->{postinstall} . ",$installdir/osimages/$osimage/kits/KIT_COMPONENTS.postinstall";
                    }
                    $linuximagetable->{postinstall} =~ s/^,//;
                }
            }
        }
    }

    # Adding postbootscrits to osimage.postbootscripts
    if (!$::noscripts and $kitcomptable and $kitcomptable->{postbootscripts}) {
        my @kitcompscripts = split ',', $kitcomptable->{postbootscripts};
        foreach my $kitcompscript (@kitcompscripts) {

            my $formatedkitcomp = $kitcompscript;

            if ($osimagetable) {
                if ($osimagetable->{postbootscripts}) {
                    my $match = 0;
                    my $added = 0;
                    my @newscripts;
                    my @scripts = split ',', $osimagetable->{postbootscripts};
                    foreach my $script (@scripts) {
                        if ($script =~ /^$formatedkitcomp$/) {
                            $match = 1;
                            last;
                        }

                        if ($script !~ /^BASEXCAT_/ and $script !~ /^KIT_/) {
                            unless ($added) {
                                push @newscripts, $formatedkitcomp;
                                $added = 1;
                            }
                        }


                        push @newscripts, $script;

                    }

                    if ($match) {
                        next;
                    } elsif (!$added) {
                        push @newscripts, $formatedkitcomp;
                    }

                    my $osimagescripts = join ',', @newscripts;
                    $osimagetable->{postbootscripts} = $osimagescripts;
                } else {
                    $osimagetable->{postbootscripts} = $formatedkitcomp;
                }

            }

        }
    }

    # Adding kit component's repodir to osimage.otherpkgdir
    if ($kitcomptable and $kitcomptable->{kitreponame}) {

        (my $kitrepotable) = $tabs->{kitrepo}->getAttribs({ kitreponame => $kitcomptable->{kitreponame} }, 'kitrepodir');
        if ($kitrepotable and $kitrepotable->{kitrepodir}) {

            if ($linuximagetable and $linuximagetable->{otherpkgdir}) {
                my $otherpkgdir = $linuximagetable->{otherpkgdir};

                #support mixed otherpkgdir http and local dir
                $otherpkgdir = get_local_otherpkgdir($otherpkgdir);
                my $kitrepodir = $kitrepotable->{kitrepodir};

                # Create otherpkgdir if it doesn't exist
                unless (-d "$otherpkgdir") {
                    mkpath("$otherpkgdir");
                }

                # Consider the mixed environment
                my $imagerhelflag = 0;
                if ($osimagetable and $osimagetable->{osvers}) {
                    if ($osimagetable->{osvers} =~ /rhel/) {
                        $imagerhelflag = 1;
                    }
                }


                if ($debianflag && $imagerhelflag)
                {
                    unless (-d "$otherpkgdir/$kitcomptable->{kitreponame}")
                    {
                        system("ln -sf $kitrepodir $otherpkgdir/$kitcomptable->{kitreponame} ");
                    }
                }


                elsif ($debianflag)
                {
                    unless (-d "$otherpkgdir/$kitcomptable->{kitreponame}")
                    {
                        # system("mkdir -p $otherpkgdir/$kitcomptable->{kitreponame}");
                        # print "mkdir -p $otherpkgdir/$kitcomptable->{kitreponame}";
                        system("cp -Rf $kitrepodir $otherpkgdir/");
                        print "cp -Rf $kitrepodir $otherpkgdir/";
                    }
                }
                else
                {
                    # Create symlink if doesn't exist
                    unless (-d "$otherpkgdir/$kitcomptable->{kitreponame}") {
                        system("ln -sf $kitrepodir $otherpkgdir/$kitcomptable->{kitreponame} ");
                    }
                }
            } else {
                $callback->({ error => ["Cannot open linuximage table or otherpkgdir do not exist"], errorcode => [1] });
                return 1;
            }
        } else {
            $callback->({ error => ["Cannot open kit table or kitdir do not exist"], errorcode => [1] });
            return 1;
        }
    }

    # Adding kitcomponent.exlist to osimage.exlist
    if ($kitcomptable and $kitcomptable->{exlist} and $kittable and $kittable->{kitdir}) {

        my @lines;
        my $exlistfile = $kitcomptable->{exlist};
        my $kitdir     = $kittable->{kitdir};


        # Adding kit component exlist file to KIT_COMPONENTS.exlist file
        mkpath("$installdir/osimages/$osimage/kits/");
        if (-e "$installdir/osimages/$osimage/kits/KIT_COMPONENTS.exlist") {
            if (open(EXLIST, "<", "$installdir/osimages/$osimage/kits/KIT_COMPONENTS.exlist")) {
                @lines = <EXLIST>;
                close(EXLIST);
                if ($::VERBOSE) {
                    $callback->({ data => ["\nReading kit component exlist file $installdir/osimages/$osimage/kits/KIT_COMPONENTS.exlist\n"] });
                }
            } else {
                $callback->({ error => ["Could not open kit component exlist file $installdir/osimages/$osimage/kits/KIT_COMPONENTS.exlist"], errorcode => [1] });
                return 1;
            }
        }
        unless (grep(/^#INCLUDE:$kitdir\/other_files\/$exlistfile#$/, @lines)) {
            if (open(NEWEXLIST, ">>", "$installdir/osimages/$osimage/kits/KIT_COMPONENTS.exlist")) {

                print NEWEXLIST "#INCLUDE:$kitdir/other_files/$exlistfile#\n";
                close(NEWEXLIST);
            }
        }

        # Adding KIT_COMPONENTS.exlist file to osimage.exlist if not existing.
        if ($linuximagetable) {
            if ($linuximagetable->{exlist}) {
                my $match = 0;
                my @exlists = split ',', $linuximagetable->{exlist};
                foreach my $exlist (@exlists) {
                    if ($exlist =~ /^$installdir\/osimages\/$osimage\/kits\/KIT_COMPONENTS.exlist$/) {
                        $match = 1;
                        last;
                    }
                }
                unless ($match) {
                    $linuximagetable->{exlist} = $linuximagetable->{exlist} . ',' . "$installdir/osimages/$osimage/kits/KIT_COMPONENTS.exlist";
                }
            } else {
                $linuximagetable->{exlist} = "$installdir/osimages/$osimage/kits/KIT_COMPONENTS.exlist";
            }

        }

    }

    # Adding kitdeployparams to a otherpkglist file in osimage
    my @kitdeployparams;
    if ($kittable and $kittable->{kitdeployparams} and $kittable->{kitdir}) {

        # Creating kit deployparams file
        my @lines;
        mkpath("$installdir/osimages/$osimage/kits/");
        if (-e "$installdir/osimages/$osimage/kits/KIT_DEPLOY_PARAMS.otherpkgs.pkglist") {
            if (open(DEPLOYPARAM, "<", "$installdir/osimages/$osimage/kits/KIT_DEPLOY_PARAMS.otherpkgs.pkglist")) {
                @lines = <DEPLOYPARAM>;
                close(DEPLOYPARAM);
                if ($::VERBOSE) {
                    $callback->({ data => ["\nReading kit deployparams file $installdir/osimages/$osimage/kits/KIT_DEPLOY_PARAMS.otherpkgs.pkglist\n"] });
                }
            } else {
                $callback->({ error => ["Could not open kit deployparams file $installdir/osimages/$osimage/kits/KIT_DEPLOY_PARAMS.otherpkgs.pkglist"], errorcode => [1] });
                return 1;
            }
        }

        # Checking if the kit deployparams have been written in the generated kit deployparams file.
        my @l;
        my $matched = 0;
        foreach my $line (@lines) {
            chomp $line;
            if ($line =~ m!$kittable->{kitdir}/other_files/$kittable->{kitdeployparams}!) {
                $matched = 1;
                last;
            }
        }

        unless ($matched) {
            push @l, "#INCLUDE:$kittable->{kitdir}/other_files/$kittable->{kitdeployparams}#\n";
        }

        # Write the missing lines to kit deployparams file
        if (open(NEWDEPLOYPARAM, ">>", "$installdir/osimages/$osimage/kits/KIT_DEPLOY_PARAMS.otherpkgs.pkglist")) {
            print NEWDEPLOYPARAM @l;
            close(NEWDEPLOYPARAM);
        }

        # Write this kit deployparams to osimage.otherpkglist if not existing.
        if ($linuximagetable) {
            if ($linuximagetable->{otherpkglist}) {
                my $match = 0;
                my @otherpkglists = split ',', $linuximagetable->{otherpkglist};
                foreach my $otherpkglist (@otherpkglists) {
                    if ($otherpkglist =~ /^$installdir\/osimages\/$osimage\/kits\/KIT_DEPLOY_PARAMS.otherpkgs.pkglist$/) {
                        $match = 1;
                        last;
                    }
                }
                unless ($match) {
                    $linuximagetable->{otherpkglist} = "$installdir/osimages/$osimage/kits/KIT_DEPLOY_PARAMS.otherpkgs.pkglist" . ',' . "$linuximagetable->{otherpkglist}";
                }
            } else {
                $linuximagetable->{otherpkglist} = "$installdir/osimages/$osimage/kits/KIT_DEPLOY_PARAMS.otherpkgs.pkglist";
            }
        }

    }

    # Adding kit component basename to osimage.otherpkgs.pkglist
    if ($kitcomptable and $kitcomptable->{basename} and $kitcomptable->{kitreponame}) {

        my @lines;
        my $basename    = $kitcomptable->{basename};
        my $kitreponame = $kitcomptable->{kitreponame};

        # Adding kit component basename to KIT_COMPONENTS.otherpkgs.pkglist file
        mkpath("$installdir/osimages/$osimage/kits");
        if (-e "$installdir/osimages/$osimage/kits/KIT_COMPONENTS.otherpkgs.pkglist") {
            if (open(OTHERPKGLIST, "<", "$installdir/osimages/$osimage/kits/KIT_COMPONENTS.otherpkgs.pkglist")) {
                @lines = <OTHERPKGLIST>;
                close(OTHERPKGLIST);
                if ($::VERBOSE) {
                    $callback->({ data => ["\nReading kit component otherpkg file $installdir/osimages/$osimage/kits/KIT_COMPONENTS.otherpkgs.pkglist\n"] });
                }
            } else {
                $callback->({ error => ["Could not open kit component otherpkg file $installdir/osimages/$osimage/kits/KIT_COMPONENTS.otherpkgs.pkglist"], errorcode => [1] });
                return 1;
            }
        }
        unless (grep(/^$kitreponame\/$basename$/, @lines)) {
            if (open(NEWOTHERPKGLIST, ">", "$installdir/osimages/$osimage/kits/KIT_COMPONENTS.otherpkgs.pkglist")) {
                if ($kitcomptable and $kitcomptable->{prerequisite}) {
                    push @lines, "#NEW_INSTALL_LIST#\n";

                    if ($kittable and $kittable->{kitdeployparams} and $kittable->{kitdir}) {
                        push @lines, "#INCLUDE:$kittable->{kitdir}/other_files/$kittable->{kitdeployparams}#\n";
                    }
                    push @lines, "$kitreponame/$kitcomptable->{prerequisite}\n";
                    $::noupgrade = 1;
                }

                # Check if this kitcomponent's kitcompdeps are in NEW_INSTALL_LIST or not.
                # If so, set $::noupgrade to put this kitcomp in a new NEW_INSTALL_LIST
                if ($kitcomptable and $kitcomptable->{kitcompdeps}) {
                    check_newinstall($kitcomptable->{kitcompdeps}, @lines);
                }

                if ($::noupgrade) {
                    push @lines, "#NEW_INSTALL_LIST#\n";

                    if ($kittable and $kittable->{kitdeployparams} and $kittable->{kitdir}) {
                        push @lines, "#INCLUDE:$kittable->{kitdir}/other_files/$kittable->{kitdeployparams}#\n";
                    }

                    push @lines, "$kitreponame/$basename\n";
                    print NEWOTHERPKGLIST @lines;
                } else {
                    print NEWOTHERPKGLIST "$kitreponame/$basename\n";
                    print NEWOTHERPKGLIST @lines;
                }

                close(NEWOTHERPKGLIST);
            }
        }

        # Write this kit component otherpkgs pkgfile to osimage.otherpkglist if not existing.
        if ($linuximagetable) {
            if ($linuximagetable->{otherpkglist}) {
                my $match = 0;
                my @otherpkglists = split ',', $linuximagetable->{otherpkglist};
                foreach my $otherpkglist (@otherpkglists) {
                    if ($otherpkglist =~ /^$installdir\/osimages\/$osimage\/kits\/KIT_COMPONENTS.otherpkgs.pkglist$/) {
                        $match = 1;
                        last;
                    }
                }
                unless ($match) {
                    $linuximagetable->{otherpkglist} = $linuximagetable->{otherpkglist} . ',' . "$installdir/osimages/$osimage/kits/KIT_COMPONENTS.otherpkgs.pkglist";
                }
            } else {
                $linuximagetable->{otherpkglist} = "$installdir/osimages/$osimage/kits/KIT_COMPONENTS.otherpkgs.pkglist";
            }

        }

        # Remove this component basename and pkgnames from KIT_RMPKGS.otherpkg.pkglist
        my @lines = ();

        my @kitpkgdeps = split ',', $kitcomptable->{kitpkgdeps};

        # Remove prerequisite from KIT_RMPKGS.otherpkg.pkglist
        if ($kitcomptable and $kitcomptable->{prerequisite}) {
            my @kitpreps = split /,/, $kitcomptable->{prerequisite};
            foreach my $kitprep (@kitpreps) {
                push @kitpkgdeps, $kitprep;
            }
        }

        push @kitpkgdeps, $basename;

        my @l = ();
        if (-e "$installdir/osimages/$osimage/kits/KIT_RMPKGS.otherpkgs.pkglist") {
            if (open(RMOTHERPKGLIST, "<", "$installdir/osimages/$osimage/kits/KIT_RMPKGS.otherpkgs.pkglist")) {
                @lines = <RMOTHERPKGLIST>;
                close(RMOTHERPKGLIST);
                if ($::VERBOSE) {
                    $callback->({ data => ["\nReading kit component rmpkgs file $installdir/osimages/$osimage/kits/KIT_RMPKGS.otherpkgs.pkglist\n"] });
                }
            } else {
                $callback->({ error => ["Could not open kit component rmpkgs file $installdir/osimages/$osimage/kits/KIT_RMPKGS.otherpkgs.pkglist"], errorcode => [1] });
                return 1;
            }
        }

        my $changed = 0;
        foreach my $line (@lines) {
            chomp $line;
            my $matched = 0;
            foreach my $kitpkgdep (@kitpkgdeps) {
                if ($line =~ /^-$kitpkgdep$/) {
                    $matched = 1;
                    $changed = 1;
                    last;
                }
            }
            if ($line =~ /^-prep_$basename$/) {
                $matched = 1;
                $changed = 1;
            }
            unless ($matched) {
                push @l, "$line\n";
            }

            my $lastline = pop @l;
            while ($lastline =~ /^#NEW_INSTALL_LIST#$/) {
                $lastline = pop @l;
            }
            push @l, $lastline if ($lastline);
        }

        if ($changed) {
            if (open(RMPKGLIST, ">", "$installdir/osimages/$osimage/kits/KIT_RMPKGS.otherpkgs.pkglist")) {
                print RMPKGLIST @l;
                close(RMPKGLIST);
            }
        }

    } else {
        $callback->({ error => ["Could not open kit component table and read basename for kit component $kitcomp"], errorcode => [1] });
        return 1;
    }

    # Now writing kit component to osimage.kitcomponents
    if ($::VERBOSE) {
        $callback->({ data => ["\nAdding this kitcomponent to osimage.kitcomponents\n"] });
    }
    if ($osimagetable) {
        if ($osimagetable->{kitcomponents}) {
            $osimagetable->{kitcomponents} = join(',', $osimagetable->{kitcomponents}, $kitcomp);
        } else {
            $osimagetable->{kitcomponents} = $kitcomp;
        }
    }

    # Adding driverpacks

    if ($linuximagetable and $linuximagetable->{driverupdatesrc}) {
        if ($kitcomptable and $kitcomptable->{driverpacks}) {
            my @driverpacks = split ',', $kitcomptable->{driverpacks};
            my @driverupdatesrcs = split ',', $linuximagetable->{driverupdatesrc};

            my @newdriverupdatesrcs = @driverupdatesrcs;

            foreach my $driverpack (@driverpacks) {
                my $matched = 0;
                foreach my $driverupdatesrc (@driverupdatesrcs) {
                    if ($driverpack eq $driverupdatesrc) {
                        $matched = 1;
                        last;
                    }
                }

                unless ($matched) {
                    push @newdriverupdatesrcs, $driverpack;
                }
            }

            my $newdriverupdatesrc = join ',', @newdriverupdatesrcs;
            $linuximagetable->{driverupdatesrc} = $newdriverupdatesrc;
        }
    }


    # Write linuximage table with all the above udpates.
    $tabs->{linuximage}->setAttribs({ imagename => $osimage }, \%{$linuximagetable});

    # Write osimage table with all the above udpates.
    $tabs->{osimage}->setAttribs({ imagename => $osimage }, \%{$osimagetable});

}


#-------------------------------------------------------

=head3 check_kit_config

  Check if this kit is suitable to add

=cut

#-------------------------------------------------------

sub check_kit_config
{

    my $tabs        = shift;
    my $kit         = shift;
    my $kithash     = shift;
    my $kitrepohash = shift;
    my $kitcomphash = shift;

    #TODO:  add check to see the the attributes name are acceptable by xCAT DB.
    #TODO: need to check if the files are existing or not, like exlist,

    unless (keys %{$kithash}) {
        my %rsp;
        push @{ $rsp{data} }, "Failed to add kit $kit because kit.conf is invalid";
        xCAT::MsgUtils->message("E", \%rsp, $::CALLBACK);
        return 1;
    }

    if ($::INSPECTION) {
        my %rsp;
        push @{ $rsp{data} }, "kitname=$kithash->{kitname}";
        push @{ $rsp{data} }, "    description=$kithash->{description}";
        push @{ $rsp{data} }, "    version=$kithash->{version}";
        push @{ $rsp{data} }, "    ostype=$kithash->{ostype}";
        xCAT::MsgUtils->message("I", \%rsp, $::CALLBACK);
        next;
    }

    (my $ref1) = $tabs->{kit}->getAttribs({ kitname => $kithash->{kitname} }, 'basename');
    if ($ref1 and $ref1->{'basename'}) {
        my %rsp;
        push @{ $rsp{data} }, "Failed to add kit $kithash->{kitname} because it is already existing";
        xCAT::MsgUtils->message("E", \%rsp, $::CALLBACK);
        return 1;
    }


    # Check if the kitcomponent is existing
    my @kitcomps = $tabs->{kitcomponent}->getAllAttribs('kitcompname');
    foreach my $kitcomp (@kitcomps) {
        if ($kitcomp->{kitcompname}) {
            foreach my $kitcompid (keys %{$kitcomphash}) {
                if ($kitcomphash->{$kitcompid}->{kitcompname} and $kitcomphash->{$kitcompid}->{kitcompname} =~ /$kitcomp->{kitcompname}/) {
                    my %rsp;
                    push @{ $rsp{data} }, "Failed to add kitcomponent $kitcomp->{kitcompname} because it is already existing";
                    xCAT::MsgUtils->message("E", \%rsp, $::CALLBACK);
                    return 1;
                }
            }
        }
    }

}


#-------------------------------------------------------

=head3 read_kit_config

  Read kit configuration file

=cut

#-------------------------------------------------------

sub read_kit_config
{

    my $fn    = shift;
    my @lines = @$fn;

    my $sec;
    my $kitname;
    my $kitreponame;
    my $kitcompname;
    my $scripts;
    my $kitrepoid = 0;
    my $kitcompid = 0;

    my %kithash;
    my %kitrepohash;
    my %kitcomphash;

    foreach my $line (@lines) {

        # Read through each line of kit.conf.
        my $key, $value;
        chomp $line;
        next if ($line =~ /^$/);
        next if ($line =~ /^\s*#/);

        # Split the kit.conf to different parts: kit, kitrepo, kitcomponent.
        if ($line =~ /kit:/) {
            $sec = "KIT";
            next;
        } elsif ($line =~ /kitrepo:/) {
            $sec       = "KITREPO";
            $kitrepoid = $kitrepoid + 1;
            next;
        } elsif ($line =~ /kitcomponent:/) {
            $sec       = "KITCOMPONENT";
            $kitcompid = $kitcompid + 1;
            next;
        } else {
            if ($line =~ /kitcompdeps/) {
                $key   = $line;
                $value = $line;
                $key =~ s/\s+(\w+)\s+=.*$/$1/;
                $key =~ s/\s+//g;
                $value =~ s/$key\s+=(.*)$/$1/;
                $key =~ s/\s+//g;
                $value =~ s/\s+//g;
            } else {
                ($key, $value) = split(/=/, $line,2);
            }
        }

        # Remove spaces in each lines.
        $key =~ s/^\s+|\s+$//g;
        $value =~ s/^\s+|\s+$//g;

        # Add each attribute to different hash.
        if ($sec =~ /KIT$/) {
            $kithash{$key} = $value;
        } elsif ($sec =~ /KITREPO$/) {
            $kitrepohash{$kitrepoid}{$key} = $value;
        } elsif ($sec =~ /KITCOMPONENT$/) {
            if ($key =~ /postbootscripts/ or $key =~ /genimage_postinstall/) {
                $scripts = $scripts . ',' . $value;
                $kitcomphash{$kitcompid}{$key} = $value;
            } else {
                $kitcomphash{$kitcompid}{$key} = $value;
            }
        }
    }

    return (\%kithash, \%kitrepohash, \%kitcomphash, $scripts);

}

#-------------------------------------------------------

=head3 addkit

  Add Kits into xCAT

=cut

#-------------------------------------------------------
sub addkit
{
    my $request         = shift;
    my $callback        = shift;
    my $request_command = shift;

    my $path;
    my $rc;

    my $xusage = sub {
        my %rsp;
        push @{ $rsp{data} }, "Usage: addkit - Adds product software Kits to an xCAT cluster environment.";
        push @{ $rsp{data} }, "\taddkit [-h|--help]";
        push @{ $rsp{data} }, "\taddkit [-v|--version]";
        push @{ $rsp{data} }, "\taddkit [-i|--inspection] <kitlist>]";
        push @{ $rsp{data} }, "\taddkit [-V|--verbose] [-p|--path <path>] <kitlist>]";
        xCAT::MsgUtils->message("I", \%rsp, $callback);
    };

    if ($^O ne 'linux') {
        my $rsp = {};
        push @{ $rsp->{data} }, "The addkit command is only supported on Linux.\n";
        xCAT::MsgUtils->message("E", $rsp, $::CALLBACK);
        return 1;
    }

    unless (defined($request->{arg})) { $xusage->(1); return; }
    @ARGV = @{ $request->{arg} };
    if ($#ARGV == -1) {
        $xusage->(1);
        return;
    }

    GetOptions(
        'h|help'       => \$help,
        'V|verbose'    => \$::VERBOSE,
        'v|version'    => \$vers,
        'i|inspection' => \$::INSPECTION,
        'p|path=s'     => \$path,
    );

    if ($help) {
        $xusage->(0);
        return;
    }

    # Option -v for version
    if (defined($vers)) {
        create_version_response('addkit');
        return 1;    # no usage - just exit
    }

    my %tabs   = ();
    my @tables = qw(kit kitrepo kitcomponent);
    foreach my $t (@tables) {
        $tabs{$t} = xCAT::Table->new($t, -create => 1, -autocommit => 1);

        if (!exists($tabs{$t})) {
            my %rsp;
            push @{ $rsp{data} }, "Could not open xCAT table $t";
            xCAT::MsgUtils->message("E", \%rsp, $callback);
            return 1;
        }
    }

    my $basename;
    my $des = shift @ARGV;

    my @kits = split ',', $des;
    my @kitnames;
    my $hasplugin = 0;
    foreach my $kit (@kits) {

        my $kitdir    = '';
        my $kittmpdir = '';

        if ($kit =~ /NEED_PRODUCT_PKGS/) {
            my %rsp;
            push @{ $rsp{data} }, "Not allowed to add partial kit $kit";
            xCAT::MsgUtils->message("E", \%rsp, $callback);
            return 1;
        }

        # extract the Kit to kitdir
        my $installdir = xCAT::TableUtils->getInstallDir();
        unless ($installdir) {
            $installdir = '/install';
        }
        $installdir =~ s/\/$//;

        my $dir = $request->{cwd};    #getcwd;
        $dir = $dir->[0];

        unless (-r $kit) {
            $kit = "$dir/$kit";
        }

        unless (-r $kit) {
            my %rsp;
            push @{ $rsp{data} }, "Can not find $kit";
            xCAT::MsgUtils->message("E", \%rsp, $callback);
            return 1;
        }


        if (-d "$kit") {

            # This is a directory.
            # TODO: check if this is a valid kit directory.

            $kittmpdir = $kit;
        } else {

            # should be a tar.bz2 file

            system("rm -rf /tmp/tmpkit/");
            mkpath("/tmp/tmpkit/");

            if ($::VERBOSE) {
                my %rsp;
                push @{ $rsp{data} }, "Extract Kit $kit to /tmp/tmpkit";
                xCAT::MsgUtils->message("I", \%rsp, $callback);
            }

            #support white space in the dir name
            $kit =~ s/(\s)/\\$1/g;

            $rc = system("tar jxvf $kit -C /tmp/tmpkit/");

            opendir($dir, "/tmp/tmpkit/");
            my @files = readdir($dir);

            foreach my $file (@files) {
                next if ($file eq '.' || $file eq '..');
                $kittmpdir = "/tmp/tmpkit/$file";
                last;
            }

            if (!$kittmpdir) {
                my %rsp;
                push @{ $rsp{data} }, "Could not find extracted kit.conf in /tmp/tmpkit";
                xCAT::MsgUtils->message("E", \%rsp, $callback);
                return 1;
            }
        }


        if ($rc) {
            my %rsp;
            push @{ $rsp{data} }, "Failed to extract kit.conf from $kit, (Maybe there was no space left?)";
            xCAT::MsgUtils->message("E", \%rsp, $callback);
            return 1;
        }


        # Read kit info from kit.conf
        my @lines;
        if (open(KITCONF, "<$kittmpdir/$kitconf")) {
            @lines = <KITCONF>;
            close(KITCONF);
            if ($::VERBOSE) {
                my %rsp;
                push @{ $rsp{data} }, "Reading kit configuration file $kittmpdir/$kitconf";
                xCAT::MsgUtils->message("I", \%rsp, $callback);
            }
        } else {
            my %rsp;
            push @{ $rsp{data} }, "Could not open kit configuration file $kittmpdir/$kitconf";
            xCAT::MsgUtils->message("E", \%rsp, $callback);
            return 1;
        }


        #
        # check contents of kit.conf to make sure the framework
        #       is compatible with this codes framework
        if (&check_framework(\@lines)) {
            return 1;
        }

        # Read kit configuration file
        my ($kithash, $kitrepohash, $kitcomphash, $scripts) = read_kit_config(\@lines);

        # Check if this kit is allowed to add.
        if (&check_kit_config(\%tabs, $kit, $kithash, $kitrepohash, $kitcomphash)) {
            return 1;
        }

        my %rsp;
        push @{ $rsp{data} }, "Adding Kit $kithash->{kitname}";
        xCAT::MsgUtils->message("I", \%rsp, $callback);

        # Moving kits from tmp directory to kitdir
        if (!$path) {
            $kitdir = $installdir . "/kits";
        } else {
            $kitdir = $path;
        }

        $kitdir =~ s/\/$//;
        $kitdir = $kitdir . "/" . $kithash->{kitname};

        if ($::VERBOSE) {
            my %rsp;
            push @{ $rsp{data} }, "Create Kit directory $kitdir";
            xCAT::MsgUtils->message("I", \%rsp, $callback);
        }
        mkpath($kitdir);

        # Set kitdir and kitrepodir
        $kithash->{kitdir} = $kitdir;

        foreach my $kitrepoid (keys %{$kitrepohash}) {
            $kitrepohash->{$kitrepoid}->{kitrepodir} = $kitdir . "/repos/" . $kitrepohash->{$kitrepoid}->{kitreponame};
        }

        if ($::VERBOSE) {
            my %rsp;
            push @{ $rsp{data} }, "Copying Kit from $kittmpdir to $kitdir";
            xCAT::MsgUtils->message("I", \%rsp, $callback);
            $rc = system("cp -rfv $kittmpdir/* $kitdir");
        } else {
            $rc = system("cp -rf $kittmpdir/* $kitdir");
        }

        # Coying scripts to /installdir/postscripts/
        if ($::VERBOSE) {
            my %rsp;
            push @{ $rsp{data} }, "Copying kit scripts from $kitdir/other_files/ to $installdir/postscripts";
            xCAT::MsgUtils->message("I", \%rsp, $callback);
        }

        my @script = split ',', $scripts;
        foreach (@script) {
            next unless ($_);
            if ($::VERBOSE) {
                $rc = system("cp -rfv $kitdir/other_files/$_ $installdir/postscripts/");
            } else {
                $rc = system("cp -rf $kitdir/other_files/$_ $installdir/postscripts/");
            }
            if ($rc && !-e "$installdir/postscripts/$_") {
                my %rsp;
                push @{ $rsp{data} }, "Failed to copy scripts from $kitdir/scripts/ to $installdir/postscripts";
                xCAT::MsgUtils->message("E", \%rsp, $callback);
                return 1;
            }
            $rc = 0;
            chmod(0755, "$installdir/postscripts/$_");
        }

        # Copying plugins to /opt/xcat/lib/perl/xCAT_plugin/
        if (-d "$kitdir/plugins/") {

            chmod(644, "$kitdir/plugins/*");

            opendir($dir, "$kitdir/plugins/");
            if (grep { ($_ ne '.') && ($_ ne '..') } readdir($dir)) {
                if ($::VERBOSE) {
                    my %rsp;
                    push @{ $rsp{data} }, "Copying kit plugins from $kitdir/plugins/ to $::XCATROOT/lib/perl/xCAT_plugin";
                    xCAT::MsgUtils->message("I", \%rsp, $callback);

                    $rc = system("cp -rfv $kitdir/plugins/* $::XCATROOT/lib/perl/xCAT_plugin/");
                } else {
                    $rc = system("cp -rf $kitdir/plugins/* $::XCATROOT/lib/perl/xCAT_plugin/");
                }

                $hasplugin = 1;
            }
        }

        if ($rc) {
            my %rsp;
            push @{ $rsp{data} }, "Failed to copy plugins from $kitdir/plugins/ to $::XCATROOT/lib/perl/xCAT_plugin";
            xCAT::MsgUtils->message("E", \%rsp, $callback);
            return 1;
        }

        # Write to DB
        if ($::VERBOSE) {
            my %rsp;
            push @{ $rsp{data} }, "Writing kit configuration into xCAT DB";
            xCAT::MsgUtils->message("I", \%rsp, $callback);
        }

        $rc = $tabs{kit}->setAttribs({ kitname => $kithash->{kitname} }, $kithash);
        if ($rc) {
            my %rsp;
            push @{ $rsp{data} }, "Failed to write kit object into xCAT DB";
            xCAT::MsgUtils->message("E", \%rsp, $callback);
            return 1;
        }

        foreach my $kitrepoid (keys %{$kitrepohash}) {
            $rc = $tabs{kitrepo}->setAttribs({ kitreponame => $kitrepohash->{$kitrepoid}->{kitreponame} }, \%{ $kitrepohash->{$kitrepoid} });
            if ($rc) {
                my %rsp;
                push @{ $rsp{data} }, "Failed to write kitrepo $kitrepohash->{$kitrepoid}->{kitreponame} into xCAT DB";
                xCAT::MsgUtils->message("E", \%rsp, $callback);
                return 1;
            }
        }

        foreach my $kitcompid (keys %{$kitcomphash}) {
            $rc = $tabs{kitcomponent}->setAttribs({ kitcompname => $kitcomphash->{$kitcompid}->{kitcompname} }, \%{ $kitcomphash->{$kitcompid} });
            if ($rc) {
                my %rsp;
                push @{ $rsp{data} }, "Failed to write kitcomponent $kitcomphash->{$kitcompid}->{kitcompname} xCAT DB";
                xCAT::MsgUtils->message("E", \%rsp, $callback);
                return 1;
            }
        }

        push @kitnames, $kithash->{kitname};
    }

    unless ($::INSPECTION) {
        my $kitlist = join ',', @kitnames;
        my %rsp;
        push @{ $rsp{data} }, "Kit $kitlist was successfully added.";
        xCAT::MsgUtils->message("I", \%rsp, $callback);

        if ($hasplugin) {

            # Issue rescanplugins to have xcatd load the new plugins
            xCAT::Utils->runxcmd({ command => ['rescanplugins'], },
                $request_command, 0, 1);

        }
    }
}


#-------------------------------------------------------

=head3 rmkit

  Remove Kits from xCAT

=cut

#-------------------------------------------------------
sub rmkit
{
    my $request         = shift;
    my $callback        = shift;
    my $request_command = shift;
    my $kitdir;
    my $rc;

    my $xusage = sub {
        my %rsp;
        push @{ $rsp{data} }, "Usage: rmkit - Remove Kits from xCAT.";
        push @{ $rsp{data} }, "\trmkit [-h|--help]";
        push @{ $rsp{data} }, "\trmkit [-v|--version]";
        push @{ $rsp{data} }, "\trmkit [-V|--verbose] [-f|--force] [-t|--test] <kitlist>]";
        xCAT::MsgUtils->message("I", \%rsp, $callback);
    };

    if ($^O ne 'linux') {
        my $rsp = {};
        push @{ $rsp->{data} }, "The rmkit command is only supported on Linux.\n";
        xCAT::MsgUtils->message("E", $rsp, $::CALLBACK);
        return 1;
    }

    unless (defined($request->{arg})) { $xusage->(1); return; }
    @ARGV = @{ $request->{arg} };
    if ($#ARGV == -1) {
        $xusage->(1);
        return;
    }


    GetOptions(
        'h|help'    => \$help,
        'V|verbose' => \$::VERBOSE,
        'v|version' => \$vers,
        't|test'    => \$test,
        'f|force'   => \$force
    );

    # Option -v for version
    if (defined($vers)) {
        create_version_response('rmkit');
        return 1;    # no usage - just exit
    }

    if ($help) {
        $xusage->(0);
        return;
    }

    my %tabs   = ();
    my @tables = qw(kit kitrepo kitcomponent osimage);
    foreach my $t (@tables) {
        $tabs{$t} = xCAT::Table->new($t, -create => 1, -autocommit => 1);

        if (!exists($tabs{$t})) {
            my %rsp;
            push @{ $rsp{data} }, "Could not open xCAT table $t";
            xCAT::MsgUtils->message("E", \%rsp, $callback);
            return 1;
        }
    }

    # Convert to kitname if input is a basename
    my %kitnames;
    my $des    = shift @ARGV;
    my @kits   = split ',', $des;
    my $DBname = xCAT::Utils->get_DBName;    # support for DB2
    foreach my $kit (@kits) {

        # Check if it is a kitname or basename

        (my $ref1) = $tabs{kit}->getAttribs({ kitname => $kit }, 'basename', 'isinternal');
        if ($ref1 and $ref1->{'basename'}) {
            if ($ref1->{'isinternal'} and !$force) {
                my %rsp;
                push @{ $rsp{data} }, "Kit $kit with isinterval attribute cannot be removed";
                xCAT::MsgUtils->message("E", \%rsp, $callback);
                return 1;
            }
            $kitnames{$kit} = 1;
        } else {
            my @entries;
            if ($DBname =~ /^DB2/) {
                @entries = $tabs{kit}->getAllAttribsWhere("\"basename\" = '$kit'", 'kitname', 'isinternal');
            } else {
                @entries = $tabs{kit}->getAllAttribsWhere("basename = '$kit'", 'kitname', 'isinternal');
            }
            unless (@entries) {
                my %rsp;
                push @{ $rsp{data} }, "Kit $kit could not be found in DB $t";
                xCAT::MsgUtils->message("E", \%rsp, $callback);
                return 1;
            }
            foreach my $entry (@entries) {
                if ($entry->{'isinternal'} and !$force) {
                    my %rsp;
                    push @{ $rsp{data} }, "Kit $entry->{kitname} with isinterval attribute cannot be remoed";
                    xCAT::MsgUtils->message("E", \%rsp, $callback);
                    return 1;
                }
                $kitnames{ $entry->{kitname} } = 1;
            }
        }
    }

    # Remove each kit
    my @entries = $tabs{'osimage'}->getAllAttribs('imagename', 'kitcomponents');
    my @kitlist;
    my $hasplugin;
    my @kpinuse;

    foreach my $kitname (keys %kitnames) {

        if (!$test) {
            my %rsp;
            push @{ $rsp{data} }, "Removing kit $kitname";
            xCAT::MsgUtils->message("I", \%rsp, $callback);
        }

        # Remove osimage.kitcomponents.

        # Find all the components in this kit.
        my $kitcompnames;
        my @kitcomphash;
        if ($DBname =~ /^DB2/) {
            @kitcomphash = $tabs{kitcomponent}->getAllAttribsWhere("\"kitname\" = '$kitname'", 'kitcompname', 'postbootscripts', 'genimage_postinstall');
        } else {
            @kitcomphash = $tabs{kitcomponent}->getAllAttribsWhere("kitname = '$kitname'", 'kitcompname', 'postbootscripts', 'genimage_postinstall');
        }
        if (@entries && (@entries > 0)) {

            if ($::VERBOSE and !$test) {
                my %rsp;
                push @{ $rsp{data} }, "Removing kit components from osimage.kitcomponents";
                xCAT::MsgUtils->message("I", \%rsp, $callback);
            }

            foreach my $entry (@entries) {

                # Check osimage.kitcomponents
                my @kitcomponents = split ',', $entry->{kitcomponents};
                foreach my $kitcomponent (@kitcomponents) {
                    chomp $kitcomponent;

                    # Compare with each component in osimage.kitcomponents list.
                    foreach my $kitcomp (@kitcomphash) {
                        my $kitcompname = $kitcomp->{kitcompname};

                        # Remove this component from osimage.kitcomponents if -f option.
                        if ("$kitcompname" =~ /^$kitcomponent$/) {
                            if ($test) {
                                push @kpinuse, $kitcomponent;
                                my %rsp;
                                push @{ $rsp{data} }, "$kitcomponent is being used by osimage $entry->{imagename}";
                                xCAT::MsgUtils->message("I", \%rsp, $callback);
                                next;
                            }
                            unless ($force) {
                                my %rsp;
                                push @{ $rsp{data} }, "Failed to remove kit component $kitcomponent because:$kitcomponent is being used by osimage $entry->{imagename}";
                                xCAT::MsgUtils->message("E", \%rsp, $callback);
                                return 1;
                            }

                            # Remove this component from osimage.kitcomponents. Mark here.
                            my $ret = xCAT::Utils->runxcmd({ command => ['rmkitcomp'], arg => [ '-f', '-i', $entry->{imagename}, $kitcompname ] }, $request_command, 0, 1);
                            if ($::RUNCMD_RC) {
                                my %rsp;
                                push @{ $rsp{data} }, "Failed to remove kit component $kitcomponent from $entry->{imagename}";
                                xCAT::MsgUtils->message("E", \%rsp, $callback);
                                return 1;
                            }
                        }
                    }
                }
            }
        }

        if ($test) {
            next;
        }

        my $kitdir;
        (my $ref1) = $tabs{kit}->getAttribs({ kitname => $kitname }, 'kitdir');
        if ($ref1 and $ref1->{'kitdir'}) {

            $kitdir = $ref1->{'kitdir'};
            chomp $kitdir;

            # remove kit plugins from /opt/xcat/lib/perl/xCAT_plugin
            if ($::VERBOSE) {
                my %rsp;
                push @{ $rsp{data} }, "Removing kit plugins from $::XCATROOT/lib/perl/xCAT_plugin/";
                xCAT::MsgUtils->message("I", \%rsp, $callback);
            }

            opendir($dir, $kitdir . "/plugins");
            my @files = readdir($dir);
            if (grep { ($_ ne '.') && ($_ ne '..') } @files) {
                $hasplugin = 1;
            }
            foreach my $file (@files) {
                if ($file eq '.' or $file eq '..') { next; }
                if (-e "$::XCATROOT/lib/perl/xCAT_plugin/$file") {
                    if ($::VERBOSE) {
                        system("rm -rfv $::XCATROOT/lib/perl/xCAT_plugin/$file");
                    } else {
                        system("rm -rf $::XCATROOT/lib/perl/xCAT_plugin/$file");
                    }
                }
            }


            if ($::VERBOSE) {
                my %rsp;
                push @{ $rsp{data} }, "Removing kit scripts from /install/postscripts/";
                xCAT::MsgUtils->message("I", \%rsp, $callback);
            }

            # remove kit scripts from /install/postscripts/
            my $installdir = xCAT::TableUtils->getInstallDir();
            unless ($installdir) {
                $installdir = '/install';
            }
            $installdir =~ s/\/$//;

            my $scripts;
            foreach my $kitcomp (@kitcomphash) {
                $scripts = $scripts . "," . $kitcomp->{postbootscripts} if ($kitcomp->{postbootscripts});
                $scripts = $scripts . "," . $kitcomp->{genimage_postinstall} if ($kitcomp->{genimage_postinstall});
            }
            $scripts =~ s/^,//;
            my @files = split /,/, $scripts;
            foreach my $file (@files) {
                if ($file eq '.' or $file eq '..') { next; }
                if (-e "$installdir/postscripts/$file") {
                    if ($::VERBOSE) {
                        system("rm -rfv $installdir/postscripts/$file");
                    } else {
                        system("rm -rf $installdir/postscripts/$file");
                    }
                }
            }

            # remove kitdir from /install/kits/
            if ($::VERBOSE) {
                my %rsp;
                push @{ $rsp{data} }, "Removing kitdir from installdir";
                xCAT::MsgUtils->message("I", \%rsp, $callback);
                system("rm -rfv $kitdir");
            } else {
                system("rm -rf $kitdir");
            }
        }


        if ($::VERBOSE) {
            my %rsp;
            push @{ $rsp{data} }, "Removing kit from xCAT DB";
            xCAT::MsgUtils->message("I", \%rsp, $callback);
        }

        # Remove kitcomponent
        foreach my $kitcomp (@kitcomphash) {
            my $kitcompname = $kitcomp->{kitcompname};
            $tabs{kitcomponent}->delEntries({ kitcompname => $kitcompname });
        }

        # Remove kitrepo
        my @kitrepohash;
        if ($DBname =~ /^DB2/) {
            @kitrepohash = $tabs{kitrepo}->getAllAttribsWhere("\"kitname\" = '$kitname'", 'kitreponame');
        } else {
            @kitrepohash = $tabs{kitrepo}->getAllAttribsWhere("kitname = '$kitname'", 'kitreponame');
        }
        foreach my $kitrepo (@kitrepohash) {
            my $kitreponame = $kitrepo->{kitreponame};
            $tabs{kitrepo}->delEntries({ kitreponame => $kitreponame });
        }

        # Remove kit
        $tabs{kit}->delEntries({ kitname => $kitname });

        push @kitlist, $kitname;

    }

    if ($test) {
        if (scalar(@kpinuse)) {
            my $kp = join ',', @kpinuse;
            my %rsp;
            push @{ $rsp{data} }, "Following kitcomponents are in use: $kp";
            xCAT::MsgUtils->message("I", \%rsp, $callback);
        } else {
            my %rsp;
            push @{ $rsp{data} }, "No kitcomponents are in use";
            xCAT::MsgUtils->message("I", \%rsp, $callback);
        }
        return 0;
    }

    my $kits = join ',', @kitlist;
    my %rsp;
    push @{ $rsp{data} }, "Kit $kits was successfully removed.";
    xCAT::MsgUtils->message("I", \%rsp, $callback);

    if ($hasplugin) {

        # Issue rescanplugins to have xcatd load the new plugins
        xCAT::Utils->runxcmd({ command => ['rescanplugins'], },
            $request_command, 0, 1);
    }

}

sub check_minorversion
{
    my $kitminor = shift;
    my $osminor = shift;

    if ($kitminor eq $osminor) {
        return 0;
    }

    if (($kitminor =~ />/) || ($kitminor =~ /</) || ($kitminor =~ /==/)) {
        if (eval ($osminor. $kitminor)) {
            return 0;
        }
    }
    return 1;
}


#-------------------------------------------------------

=head3 validate_os

  Validate if the input OS fit to the osimage or not.

=cut

#-----------------------------------------------------

sub validate_os {

    my $os      = shift;
    my $osimage = shift;
    my $kitcomp = shift;

    # Validate each attribute in kitcomp.
    my $catched = 0;
    my @osbasename = split ',', $kitcomp->{osbasename};
    foreach (@osbasename) {
        if ($osimage->{basename} eq $_) {
            $catched = 1;
        }
    }

    unless ($catched) {

        #        my %rsp;
        #        push@{ $rsp{data} }, "osimage $os is not compatible with kit component $kitcomp->{kitcompname} with attribute OS";
        #        xCAT::MsgUtils->message( "E", \%rsp, $::CALLBACK );
        return 1;
    }

    if ($osimage->{majorversion} ne $kitcomp->{osmajorversion}) {

        #        my %rsp;
        #        push@{ $rsp{data} }, "osimage $os is not compatible with kit component $kitcomp->{kitcompname} with attribute majorversion";
        #        xCAT::MsgUtils->message( "E", \%rsp, $::CALLBACK );
        return 1;
    }

    if ($kitcomp->{osminorversion}) {
        if (check_minorversion($kitcomp->{osminorversion}, $osimage->{minorversion}))
        {
            return 1;
        }
    }

    if ($osimage->{arch} ne $kitcomp->{osarch} && $kitcomp->{osarch} ne 'noarch') {

        #        my %rsp;
        #        push@{ $rsp{data} }, "osimage $os is not compatible with kit component $kitcomp->{kitcompname} with attribute arch";
        #        xCAT::MsgUtils->message( "E", \%rsp, $::CALLBACK );
        return 1;
    }

    if ($osimage->{type} ne $kitcomp->{ostype}) {

        #        my %rsp;
        #        push@{ $rsp{data} }, "osimage $os is not compatible with kit component $kitcomp->{kitcompname} with attribute type";
        #        xCAT::MsgUtils->message( "E", \%rsp, $::CALLBACK );
        return 1;
    }
    if ($osimage->{serverrole}) {
        my $match               = 0;
        my @os_serverroles      = split /,/, $osimage->{serverrole};
        my @kitcomp_serverroles = split /,/, $kitcomp->{serverroles};
        foreach my $os_serverrole (@os_serverroles) {
            foreach my $kitcomp_serverrole (@kitcomp_serverroles) {
                if ($os_serverrole eq $kitcomp_serverrole) {
                    $match = 1;
                    last;
                }
            }

            if ($match) {
                last;
            }
        }
        if (!$match) {

            #            my %rsp;
            #            push@{ $rsp{data} }, "osimage $os is not compatible with kit component $kitcomp->{kitcompname} with attribute serverrole";
            #            xCAT::MsgUtils->message( "E", \%rsp, $::CALLBACK );
            return 1;
        }
    }


    return 0;
}


#-------------------------------------------------------

=head3 addkitcomp

  Assign Kit component to osimage

=cut

#-------------------------------------------------------
sub addkitcomp
{
    my $request         = shift;
    my $callback        = shift;
    my $request_command = shift;
    my $kitdir;
    my $rc;

    my $xusage = sub {
        my $ret = shift;
        my %rsp;
        push @{ $rsp{data} }, "Usage: addkitcomp - Add a Kit component to an xCAT osimage.";
        push @{ $rsp{data} }, "\taddkitcomp [-h|--help]";
        push @{ $rsp{data} }, "\taddkitcomp [-v|--version]";
        push @{ $rsp{data} }, "\taddkitcomp [-V|--verbose] [-a|--adddeps] [-f|--force] \n\t\t[-n|--noupgrade] [--noscripts] -i <osimage> <kitcompname_list>";
        xCAT::MsgUtils->message("I", \%rsp, $callback, $ret);
    };

    if ($^O ne 'linux') {
        my $rsp = {};
        push @{ $rsp->{data} }, "The addkitcomp command is only supported on Linux.\n";
        xCAT::MsgUtils->message("E", $rsp, $::CALLBACK);
        return 1;
    }

    unless (defined($request->{arg})) { $xusage->(1); return; }
    @ARGV = @{ $request->{arg} };
    if ($#ARGV == -1) {
        $xusage->(1);
        return;
    }

    GetOptions(
        'h|help'      => \$help,
        'V|verbose'   => \$::VERBOSE,
        'v|version'   => \$vers,
        'a|adddeps'   => \$adddeps,
        'f|force'     => \$force,
        'n|noupgrade' => \$::noupgrade,
        'noscripts'   => \$::noscripts,
        'i=s'         => \$osimage
    );

    if ($help) {
        $xusage->(0);
        return;
    }

    # Option -v for version
    if (defined($vers)) {
        create_version_response('addkitcomp');
        return 1;    # no usage - just exit
    }

    unless (scalar @ARGV)
    {
        $xusage->(1);
        return 1;
    }

    my $DBname = xCAT::Utils->get_DBName;    # support for DB2
    my %tabs   = ();
    my @tables = qw(kit kitrepo kitcomponent osimage osdistro linuximage);
    foreach my $t (@tables) {
        $tabs{$t} = xCAT::Table->new($t, -create => 1, -autocommit => 1);

        if (!exists($tabs{$t})) {
            my %rsp;
            push @{ $rsp{data} }, "Could not open xCAT table $t";
            xCAT::MsgUtils->message("E", \%rsp, $callback);
            return 1;
        }
    }

    # Check if all the kitcomponents are existing before processing

    if ($::VERBOSE) {
        my %rsp;
        push @{ $rsp{data} }, "Checking if kitcomponents are valid";
        xCAT::MsgUtils->message("I", \%rsp, $callback);
    }

    my %kitcomps;
    my $des = shift @ARGV;
    my @kitcomponents = split ',', $des;
    foreach my $kitcomponent (@kitcomponents) {

        # Check if it is a kitcompname or basename
        (my $kitcomptable) = $tabs{kitcomponent}->getAttribs({ kitcompname => $kitcomponent }, 'kitname', 'basename');
        if ($kitcomptable and $kitcomptable->{'basename'}) {
            $kitcomps{$kitcomponent}{name}     = $kitcomponent;
            $kitcomps{$kitcomponent}{basename} = $kitcomptable->{'basename'};
        } else {
            my @entries;
            if ($DBname =~ /^DB2/) {
                @entries = $tabs{kitcomponent}->getAllAttribsWhere("\"basename\" = '$kitcomponent'", 'kitcompname', 'version', 'release');
            } else {
                @entries = $tabs{kitcomponent}->getAllAttribsWhere("basename = '$kitcomponent'", 'kitcompname', 'version', 'release');
            }
            unless (@entries) {
                my %rsp;
                push @{ $rsp{data} }, "$kitcomponent kitcomponent does not exist";
                xCAT::MsgUtils->message("E", \%rsp, $callback);
                return 1;
            }

            my $highest = get_highest_version('kitcompname', 'version', 'release', @entries);
            $kitcomps{$highest}{name}     = $highest;
            $kitcomps{$highest}{basename} = $kitcomponent;
        }
    }

    # Verify if the kitcomponents fitting to the osimage or not.

    if ($::VERBOSE) {
        my %rsp;
        push @{ $rsp{data} }, "Verifying if kitcomponents fit to osimage";
        xCAT::MsgUtils->message("I", \%rsp, $callback);
    }

    my %os;
    my $osdistrotable;
    (my $osimagetable) = $tabs{osimage}->getAttribs({ imagename => $osimage }, 'osdistroname', 'serverrole', 'kitcomponents', 'osname', 'osvers', 'osarch');
    if ($osimagetable and $osimagetable->{'osdistroname'}) {
        ($osdistrotable) = $tabs{osdistro}->getAttribs({ osdistroname => $osimagetable->{'osdistroname'} }, 'basename', 'majorversion', 'minorversion', 'arch', 'type');
        if (!$osdistrotable or !$osdistrotable->{basename}) {
            my %rsp;
            push @{ $rsp{data} }, "$osdistroname osdistro does not exist";
            xCAT::MsgUtils->message("E", \%rsp, $callback);
            return 1;
        }

        # Read basename,majorversion,minorversion,arch,type, from osdistro table
        $os{$osimage}{basename}     = lc($osdistrotable->{basename});
        $os{$osimage}{majorversion} = lc($osdistrotable->{majorversion});
        $os{$osimage}{minorversion} = lc($osdistrotable->{minorversion});
        $os{$osimage}{arch}         = lc($osdistrotable->{arch});
        $os{$osimage}{type}         = lc($osdistrotable->{type});

        # Read serverrole from osimage.
        $os{$osimage}{serverrole} = lc($osimagetable->{'serverrole'});

    } elsif (!$osimagetable) {
        my %rsp;
        push @{ $rsp{data} }, "osimage $osimage does not exist";
        xCAT::MsgUtils->message("E", \%rsp, $callback);
        return 1;
    } elsif (!$osimagetable->{'osname'}) {
        my %rsp;
        push @{ $rsp{data} }, "osimage $osimage does not contain a valid 'osname' attribute";
        xCAT::MsgUtils->message("E", \%rsp, $callback);
        return 1;
    } elsif (!$osimagetable->{'osvers'}) {
        my %rsp;
        push @{ $rsp{data} }, "osimage $osimage does not contain a valid 'osvers' attribute";
        xCAT::MsgUtils->message("E", \%rsp, $callback);
        return 1;
    } elsif (!$osimagetable->{'osarch'}) {
        my %rsp;
        push @{ $rsp{data} }, "osimage $osimage does not contain a valid 'osarch' attribute";
        xCAT::MsgUtils->message("E", \%rsp, $callback);
        return 1;
    } else {
        $os{$osimage}{type}       = lc($osimagetable->{'osname'});
        $os{$osimage}{arch}       = lc($osimagetable->{'osarch'});
        $os{$osimage}{serverrole} = lc($osimagetable->{'serverrole'});

        my ($basename, $majorversion, $minorversion) = $osimagetable->{'osvers'} =~ /^(\D+)(\d+)\W+(\d+)/;
        $os{$osimage}{basename}     = lc($basename);
        $os{$osimage}{majorversion} = lc($majorversion);
        $os{$osimage}{minorversion} = lc($minorversion);
    }


    foreach my $kitcomp (keys %kitcomps) {
        (my $kitcomptable) = $tabs{kitcomponent}->getAttribs({ kitcompname => $kitcomp }, 'kitname', 'kitreponame', 'serverroles', 'kitcompdeps');
        if ($kitcomptable and $kitcomptable->{'kitname'} and $kitcomptable->{'kitreponame'}) {

            # Read serverroles from kitcomponent table
            $kitcomps{$kitcomp}{serverroles} = lc($kitcomptable->{'serverroles'});

            # Read ostype from kit table
            (my $kittable) = $tabs{kit}->getAttribs({ kitname => $kitcomptable->{'kitname'} }, 'ostype');
            if ($kittable and $kittable->{ostype}) {
                $kitcomps{$kitcomp}{ostype} = lc($kittable->{ostype});
            } else {
                my %rsp;
                push @{ $rsp{data} }, "$kitcomptable->{'kitname'} ostype does not exist";
                xCAT::MsgUtils->message("E", \%rsp, $callback);
                return 1;
            }

            # Read osbasename, osmajorversion,osminorversion,osarch,compat_osbasenames from kitrepo table
            (my $kitrepotable) = $tabs{kitrepo}->getAttribs({ kitreponame => $kitcomptable->{'kitreponame'} }, 'osbasename', 'osmajorversion', 'osminorversion', 'osarch', 'compat_osbasenames');
            if ($kitrepotable and $kitrepotable->{osbasename} and $kitrepotable->{osmajorversion} and $kitrepotable->{osarch}) {
                if ($kitrepotable->{compat_osbasenames}) {
                    $kitcomps{$kitcomp}{osbasename} = lc($kitrepotable->{osbasename}) . ',' . lc($kitrepotable->{compat_osbasenames});
                } else {
                    $kitcomps{$kitcomp}{osbasename} = lc($kitrepotable->{osbasename});
                }

                $kitcomps{$kitcomp}{osmajorversion} = lc($kitrepotable->{osmajorversion});
                $kitcomps{$kitcomp}{osminorversion} = lc($kitrepotable->{osminorversion});
                $kitcomps{$kitcomp}{osarch} = lc($kitrepotable->{osarch});
            } else {
                my %rsp;
                push @{ $rsp{data} }, "$kitcomp osbasename,osmajorversion,osminorversion or osarch does not exist";
                xCAT::MsgUtils->message("E", \%rsp, $callback);
                return 1;
            }

        } else {
            my %rsp;
            push @{ $rsp{data} }, "$kitcomp kitname or kitrepo name does not exist";
            xCAT::MsgUtils->message("E", \%rsp, $callback);
            return 1;
        }

        if (!$force) {

            # Validate each attribute in kitcomp.
            my $catched = 0;
            my @osbasename = split ',', $kitcomps{$kitcomp}{osbasename};
            foreach (@osbasename) {
                if ($os{$osimage}{basename} eq $_) {
                    $catched = 1;
                }
            }

            unless ($catched) {
                my %rsp;
                push @{ $rsp{data} }, "osimage $osimage is not compatible with kit component $kitcomp with attribute OS";
                xCAT::MsgUtils->message("E", \%rsp, $callback);
                return 1;
            }

            if ($os{$osimage}{majorversion} ne $kitcomps{$kitcomp}{osmajorversion}) {
                my %rsp;
                push @{ $rsp{data} }, "osimage $osimage is not compatible with kit component $kitcomp with attribute majorversion";
                xCAT::MsgUtils->message("E", \%rsp, $callback);
                return 1;
            }

            if ($kitcomps{$kitcomp}{osminorversion}) {
                if (check_minorversion($kitcomps{$kitcomp}{osminorversion}, $os{$osimage}{minorversion}))
                {
                    my %rsp;
                    push @{ $rsp{data} }, "osimage $osimage is not compatible with kit component $kitcomp with attribute minorversion";
                    xCAT::MsgUtils->message("E", \%rsp, $callback);
                    return 1;
                }
            }

            if ($os{$osimage}{arch} ne $kitcomps{$kitcomp}{osarch} && $kitcomps{$kitcomp}{osarch} ne 'noarch') {
                my %rsp;
                push @{ $rsp{data} }, "osimage $osimage is not compatible with kit component $kitcomp with attribute arch";
                xCAT::MsgUtils->message("E", \%rsp, $callback);
                return 1;
            }

            if ($os{$osimage}{type} ne $kitcomps{$kitcomp}{ostype}) {
                my %rsp;
                push @{ $rsp{data} }, "osimage $osimage is not compatible with kit component $kitcomp with attribute type";
                xCAT::MsgUtils->message("E", \%rsp, $callback);
                return 1;
            }

            if ($os{$osimage}{serverrole}) {
                my $match = 0;
                my @os_serverroles = split /,/, $os{$osimage}{serverrole};
                my @kitcomp_serverroles = split /,/, $kitcomps{$kitcomp}{serverroles};
                foreach my $os_serverrole (@os_serverroles) {
                    foreach my $kitcomp_serverrole (@kitcomp_serverroles) {
                        if ($os_serverrole eq $kitcomp_serverrole) {
                            $match = 1;
                            last;
                        }
                    }

                    if ($match) {
                        last;
                    }
                }
                if (!$match) {
                    my %rsp;
                    push @{ $rsp{data} }, "osimage $osimage is not compatible with kit component $kitcomp with attribute serverrole";
                    xCAT::MsgUtils->message("E", \%rsp, $callback);
                    return 1;
                }
            }

            if ($kitcomptable and $kitcomptable->{'kitcompdeps'}) {
                my @kitcompdeps = split ',', $kitcomptable->{'kitcompdeps'};
                foreach my $kitcompdependency (@kitcompdeps) {
                    my ($kitcompdep, $vers) = split /<=|>=|=|<|>/, $kitcompdependency;
                    my @entries;
                    if ($DBname =~ /^DB2/) {
                        @entries = $tabs{kitcomponent}->getAllAttribsWhere("\"basename\" = '$kitcompdep'", 'kitreponame', 'kitname', 'kitcompname', 'serverroles', 'kitcompdeps', 'version', 'prerequisite', 'release');
                    } else {
                        @entries = $tabs{kitcomponent}->getAllAttribsWhere("basename = '$kitcompdep'", 'kitreponame', 'kitname', 'kitcompname', 'serverroles', 'kitcompdeps', 'version', 'prerequisite', 'release');
                    }
                    unless (@entries) {
                        my %rsp;
                        push @{ $rsp{data} }, "Cannot find any matched kit component for kit component $kitcomp dependency $kitcompdep";
                        xCAT::MsgUtils->message("E", \%rsp, $callback);
                        return 1;
                    }

                    my $highest;
                    if ($vers) {
                        my @valid_kitcomp;
                        my $tmpkitcomp;
                        my ($vers, $rel) = split /-/, $vers;
                        $tmpkitcomp->{version} = $vers;
                        $tmpkitcomp->{release} = $rel;
                        foreach my $entry (@entries) {
                            my $rc = &compare_version($entry, $tmpkitcomp, 'kitcompname', 'version', 'release');
                            if (($rc == -1 and $kitcompdependency =~ />/)
                                || ($rc == 1 and $kitcompdependency =~ /</)
                                || ($rc == 0 and $kitcompdependency =~ /=/)) {


                                # Check if this kitcomp fits to the osimage
                                (my $krtable) = $tabs{kitrepo}->getAttribs({ kitreponame => $entry->{'kitreponame'} }, 'osbasename', 'osmajorversion', 'osminorversion', 'osarch', 'compat_osbasenames');
                                if ($krtable->{compat_osbasenames}) {
                                    $entry->{osbasename} = lc($krtable->{osbasename}) . ',' . lc($krtable->{compat_osbasenames});
                                } else {
                                    $entry->{osbasename} = lc($krtable->{osbasename});
                                }

                                $entry->{osmajorversion} = lc($krtable->{osmajorversion});
                                $entry->{osminorversion} = lc($krtable->{osminorversion});
                                $entry->{osarch} = lc($krtable->{osarch});

                                (my $ktable) = $tabs{kit}->getAttribs({ kitname => $entry->{'kitname'} }, 'ostype');
                                $entry->{ostype} = lc($ktable->{ostype});

                                unless (&validate_os($osimage, $os{$osimage}, $entry)) {
                                    push @valid_kitcomp, $entry;
                                }

                            }
                        }

                        if (scalar(@valid_kitcomp)) {
                            $highest = get_highest_version('kitcompname', 'version', 'release', @valid_kitcomp);
                        } else {
                            my %rsp;
                            push @{ $rsp{data} }, "Cannot find any kit component could match for kit component $kitcomp dependency $kitcompdep";
                            xCAT::MsgUtils->message("E", \%rsp, $callback);
                        }

                    } else {
                        my @valid_kitcomp;

                        foreach my $entry (@entries) {

                            # Check if this kitcomp fits to the osimage
                            (my $krtable) = $tabs{kitrepo}->getAttribs({ kitreponame => $entry->{'kitreponame'} }, 'osbasename', 'osmajorversion', 'osminorversion', 'osarch', 'compat_osbasenames');
                            if ($krtable->{compat_osbasenames}) {
                                $entry->{osbasename} = lc($krtable->{osbasename}) . ',' . lc($krtable->{compat_osbasenames});
                            } else {
                                $entry->{osbasename} = lc($krtable->{osbasename});
                            }

                            $entry->{osmajorversion} = lc($krtable->{osmajorversion});
                            $entry->{osminorversion} = lc($krtable->{osminorversion});
                            $entry->{osarch} = lc($krtable->{osarch});

                            (my $ktable) = $tabs{kit}->getAttribs({ kitname => $entry->{'kitname'} }, 'ostype');
                            $entry->{ostype} = lc($ktable->{ostype});


                            unless (&validate_os($osimage, $os{$osimage}, $entry)) {
                                push @valid_kitcomp, $entry;
                            }
                        }
                        $highest = get_highest_version('kitcompname', 'version', 'release', @valid_kitcomp);

                    }

                    if ($adddeps and $highest) {
                        my $catched = 0;
                        if ($osimagetable and $osimagetable->{'kitcomponents'}) {
                            my @oskitcomps = split ',', $osimagetable->{'kitcomponents'};
                            foreach my $oskitcomp (@oskitcomps) {
                                if ($highest eq $oskitcomp) {
                                    $catched = 1;
                                    last;
                                }
                            }
                        }


                        if (!$catched) {

                            # Add dependencies recursive
                            my $ret = xCAT::Utils->runxcmd({ command => ['addkitcomp'], arg => [ '-a', '-i', $osimage, $highest ] }, $request_command, 0, 1);
                            if ($::RUNCMD_RC) {
                                my %rsp;
                                push @{ $rsp{data} }, "Failed to add dependency $highest to osimage $osimage";
                                xCAT::MsgUtils->message("E", \%rsp, $callback);
                                return 1;
                            }


                        }
                    } else {

                        my $catched = 0;
                        if ($osimagetable and $osimagetable->{'kitcomponents'}) {
                            my @oskitcomps = split ',', $osimagetable->{'kitcomponents'};
                            foreach my $oskitcomp (@oskitcomps) {
                                if ($highest eq $oskitcomp) {
                                    $catched = 1;
                                    last;
                                }
                            }
                        }

                        foreach my $k (keys %kitcomps) {
                            if ($kitcomps{$k}{basename} and $kitcompdep eq $kitcomps{$k}{basename}) {
                                $catched = 1;
                                last;
                            }
                        }

                        if (!$catched) {
                            my %rsp;
                            push @{ $rsp{data} }, "kit component dependency $highest for kit component $kitcomp is not existing in osimage or specified in command line";
                            xCAT::MsgUtils->message("E", \%rsp, $callback);
                            return 1;
                        }
                    }
                }
            }
        }

        if ($::VERBOSE) {
            my %rsp;
            push @{ $rsp{data} }, "kitcomponent $kitcomp fits to osimage $osimage";
            xCAT::MsgUtils->message("I", \%rsp, $callback);
        }
    }

    # Now assign each component to the osimage

    if ($::VERBOSE) {
        my %rsp;
        push @{ $rsp{data} }, "Assigning kitcomponent to osimage";
        xCAT::MsgUtils->message("I", \%rsp, $callback);
    }

    my @kitcomps;
    my @oskitcomps;
    my $catched = 0;
    my @kitlist;

    if ($osimagetable and $osimagetable->{'kitcomponents'}) {
        @oskitcomps = split ',', $osimagetable->{'kitcomponents'};
    }

    my @newkitcomps = keys %kitcomps;
    foreach (keys %kitcomps) {
        my $kitcomp = shift @newkitcomps;

        my %rsp;
        push @{ $rsp{data} }, "Assigning kit component $kitcomp to osimage $osimage";
        xCAT::MsgUtils->message("I", \%rsp, $callback);

        # Check if this component is existing in osimage.kitcomponents
        foreach my $oskitcomp (@oskitcomps) {
            if ($kitcomp eq $oskitcomp) {
                my %rsp;
                push @{ $rsp{data} }, "$kitcomp kit component is already in osimage $osimage";
                xCAT::MsgUtils->message("I", \%rsp, $callback);
                $catched = 1;
            }
        }

        # No matching kitcomponent name in osimage.kitcomponents, now checking their basenames.
        if (!$catched) {

            my $add = 0;
            foreach my $oskitcomp (@oskitcomps) {

                # Compare this kit component's basename with basenames in osimage.kitcomponents
                (my $kitcomptable) = $tabs{kitcomponent}->getAttribs({ kitcompname => $kitcomp }, 'basename', 'version', 'release');
                if (!$kitcomptable or !$kitcomptable->{'basename'}) {
                    my %rsp;
                    push @{ $rsp{data} }, "$kitcomp kit component does not have basename";
                    xCAT::MsgUtils->message("E", \%rsp, $callback);
                    return 1;
                }
                (my $oskitcomptable) = $tabs{kitcomponent}->getAttribs({ kitcompname => $oskitcomp }, 'basename', 'version', 'release');
                if (!$oskitcomptable or !$oskitcomptable->{'basename'}) {
                    my %rsp;
                    push @{ $rsp{data} }, "$oskitcomp kit component does not have basename";
                    xCAT::MsgUtils->message("I", \%rsp, $callback);
                    next;
                }

                if ($kitcomptable->{'basename'} eq $oskitcomptable->{'basename'}) {
                    my $rc = compare_version($oskitcomptable, $kitcomptable, 'kitcompname', 'version', 'release');
                    if ($rc == 1 and !$::noupgrade) {
                        my %rsp;
                        push @{ $rsp{data} }, "Upgrading kit component $oskitcomp to $kitcomp";
                        xCAT::MsgUtils->message("I", \%rsp, $callback);
                        my $ret = xCAT::Utils->runxcmd({ command => ['rmkitcomp'], arg => [ '-f', '-i', $osimage, $oskitcomp ] }, $request_command, -2, 1);
                        if (!$ret) {
                            my %rsp;
                            push @{ $rsp{data} }, "Failed to remove kit component $kitcomp from $osimage";
                            xCAT::MsgUtils->message("E", \%rsp, $callback);
                            return 1;
                        }
                        $add = 1;
                    } elsif ($rc == 0) {
                        my %rsp;
                        push @{ $rsp{data} }, "Do nothing since kit component $oskitcomp in osimage $osimage has the same basename/version and release with kit component $kitcomp";
                        xCAT::MsgUtils->message("I", \%rsp, $callback);
                        next;
                    } elsif (!$::noupgrade) {
                        my %rsp;
                        push @{ $rsp{data} }, "kit component $oskitcomp is already in osimage $osimage, and has a newer release/version than $kitcomp.  Downgrading kit component is not supported";
                        xCAT::MsgUtils->message("E", \%rsp, $callback);
                        return 1;
                    }
                }
            }

            # Now assign this component to osimage
            my $rc = assign_to_osimage($osimage, $kitcomp, $callback, \%tabs);
        }

        push @kitlist, $kitcomp;
    }

    my $kitnames = join ',', @kitlist;
    my %rsp;
    push @{ $rsp{data} }, "Kit components $kitnames were added to osimage $osimage successfully";
    xCAT::MsgUtils->message("I", \%rsp, $callback);

    return;
}

#-------------------------------------------------------

=head3 rmkitcomp

  Remove Kit component from osimage

=cut

#-------------------------------------------------------
sub rmkitcomp
{

    my $request         = shift;
    my $callback        = shift;
    my $request_command = shift;
    my $kitdir;
    my $rc;

    my $xusage = sub {
        my $ret = shift;
        my %rsp;
        push @{ $rsp{data} }, "Usage: rmkitcomp - Remove Kit components from an xCAT osimage.";
        push @{ $rsp{data} }, "\trmkitcomp [-h|--help]";
        push @{ $rsp{data} }, "\trmkitcomp [-v|--version]";
        push @{ $rsp{data} }, "\trmkitcomp [-V|--verbose] [-u|--uninstall] [-f|--force] [--noscripts] \n\t\t-i <osimage> <kitcompname_list>";
        xCAT::MsgUtils->message("I", \%rsp, $callback, $ret);
    };

    if ($^O ne 'linux') {
        my $rsp = {};
        push @{ $rsp->{data} }, "The rmkitcomp command is only supported on Linux.\n";
        xCAT::MsgUtils->message("E", $rsp, $::CALLBACK);
        return 1;
    }

    unless (defined($request->{arg})) { $xusage->(1); return; }
    @ARGV = @{ $request->{arg} };
    if ($#ARGV == -1) {
        $xusage->(1);
        return;
    }


    GetOptions(
        'h|help'      => \$help,
        'V|verbose'   => \$::VERBOSE,
        'v|version'   => \$vers,
        'u|uninstall' => \$uninstall,
        'f|force'     => \$force,
        'noscripts'   => \$noscripts,
        'i=s'         => \$osimage
    );

    if ($help) {
        $xusage->(0);
        return;
    }

    # Option -v for version
    if (defined($vers)) {
        create_version_response('rmkitcomp');
        return 1;    # no usage - just exit
    }

    unless (scalar @ARGV)
    {
        $xusage->(1);
        return 1;
    }

    my $DBname = xCAT::Utils->get_DBName;    # support for DB2
    my %tabs   = ();
    my @tables = qw(kit kitrepo kitcomponent osimage osdistro linuximage);
    foreach my $t (@tables) {
        $tabs{$t} = xCAT::Table->new($t, -create => 1, -autocommit => 1);

        if (!exists($tabs{$t})) {
            my %rsp;
            push @{ $rsp{data} }, "Could not open xCAT table $t";
            xCAT::MsgUtils->message("E", \%rsp, $callback);
            return 1;
            return 1;
        }
    }


    # Check if all the kitcomponents are existing before processing

    if ($::VERBOSE) {
        my %rsp;
        push @{ $rsp{data} }, "Checking if kitcomponents are valid";
        xCAT::MsgUtils->message("I", \%rsp, $callback);
    }

    my %kitcomps;
    my @remove_kitcomps_by_basename;
    my $des = shift @ARGV;
    my @kitcomponents = split ',', $des;
    foreach my $kitcomponent (@kitcomponents) {

        # Check if it is a kitcompname or basename
        (my $kitcomptable) = $tabs{kitcomponent}->getAttribs({ kitcompname => $kitcomponent }, 'kitname', 'kitpkgdeps', 'prerequisite', 'postbootscripts', 'genimage_postinstall', 'kitreponame', 'exlist', 'basename', 'driverpacks');
        if ($kitcomptable and $kitcomptable->{'kitname'}) {
            $kitcomps{$kitcomponent}{name}       = $kitcomponent;
            $kitcomps{$kitcomponent}{kitname}    = $kitcomptable->{kitname};
            $kitcomps{$kitcomponent}{kitpkgdeps} = $kitcomptable->{kitpkgdeps};
            $kitcomps{$kitcomponent}{prerequisite} = $kitcomptable->{prerequisite};
            $kitcomps{$kitcomponent}{basename} = $kitcomptable->{basename};
            $kitcomps{$kitcomponent}{exlist}   = $kitcomptable->{exlist};
            $kitcomps{$kitcomponent}{postbootscripts} = $kitcomptable->{postbootscripts};
            $kitcomps{$kitcomponent}{kitreponame} = $kitcomptable->{kitreponame};
            $kitcomps{$kitcomponent}{driverpacks} = $kitcomptable->{driverpacks};
            $kitcomps{$kitcomponent}{genimage_postinstall} = $kitcomptable->{genimage_postinstall};
        } else {
            my @entries;
            if ($DBname =~ /^DB2/) {
                @entries = $tabs{kitcomponent}->getAllAttribsWhere("\"basename\" = '$kitcomponent'", 'kitcompname', 'version', 'release');
            } else {
                @entries = $tabs{kitcomponent}->getAllAttribsWhere("basename = '$kitcomponent'", 'kitcompname', 'version', 'release');
            }
            unless (@entries) {
                my %rsp;
                push @{ $rsp{data} }, "$kitcomponent kitcomponent does not exist";
                xCAT::MsgUtils->message("E", \%rsp, $callback);
                return 1;
            }
            push(@remove_kitcomps_by_basename, $kitcomponent);
            foreach $basename_entry (@entries) {
                my $this_entry = $basename_entry->{kitcompname};
                $kitcomps{$this_entry}{name} = $this_entry;
                (my $kitcomptable) = $tabs{kitcomponent}->getAttribs({ kitcompname => $this_entry }, 'kitname', 'kitpkgdeps', 'prerequisite', 'postbootscripts', 'genimage_postinstall', 'kitreponame', 'exlist', 'basename', 'driverpacks');
                $kitcomps{$this_entry}{kitname} = $kitcomptable->{kitname};
                $kitcomps{$this_entry}{kitpkgdeps} = $kitcomptable->{kitpkgdeps};
                $kitcomps{$this_entry}{prerequisite} = $kitcomptable->{prerequisite};
                $kitcomps{$this_entry}{basename} = $kitcomptable->{basename};
                $kitcomps{$this_entry}{exlist}   = $kitcomptable->{exlist};
                $kitcomps{$this_entry}{postbootscripts} = $kitcomptable->{postbootscripts};
                $kitcomps{$this_entry}{kitreponame} = $kitcomptable->{kitreponame};
                $kitcomps{$this_entry}{driverpacks} = $kitcomptable->{driverpacks};
                $kitcomps{$this_entry}{genimage_postinstall} = $kitcomptable->{genimage_postinstall};
                $kitcomps{$this_entry}{remove_by_basename} = '1';
            }
        }
    }

    # Check if the kitcomponents are existing in osimage.kitcomponents attribute.

    (my $osimagetable) = $tabs{osimage}->getAttribs({ imagename => $osimage }, 'kitcomponents', 'postbootscripts', 'provmethod');
    if (!$osimagetable or !$osimagetable->{'kitcomponents'}) {
        my %rsp;
        push @{ $rsp{data} }, "$osimage osimage does not exist or not includes any kit components";
        xCAT::MsgUtils->message("E", \%rsp, $callback);
        return 1;
    }

    if (!$osimagetable->{'provmethod'}) {
        my %rsp;
        push @{ $rsp{data} }, "$osimage osimage is missing provmethod";
        xCAT::MsgUtils->message("E", \%rsp, $callback);
        return 1;
    }
    my @osikitcomps = split ',', $osimagetable->{'kitcomponents'};
    foreach my $osikitcomp (@osikitcomps) {
        if (exists($kitcomps{$osikitcomp})) {
            $kitcomps{$osikitcomp}{matched} = 1;
        }
    }
    my $invalidkitcomp = '';
    foreach my $kitcomp (keys %kitcomps) {
        if (!$kitcomps{$kitcomp}{matched}) {
            if ($kitcomps{$kitcomp}{remove_by_basename}) {
                delete($kitcomps{$kitcomp});
            } else {
                if (!$invalidkitcomp) {
                    $invalidkitcomp = $kitcomp;
                } else {
                    $invalidkitcomp = join(',', $invalidkitcomp, $kitcomp);
                }
            }
        }
    }

    foreach $basename_to_remove (@remove_kitcomps_by_basename) {
        my $removing_at_least_one = 0;
        foreach my $kitcomponent (keys %kitcomps) {
            if ($basename_to_remove eq $kitcomps{$kitcomponent}{basename}) {
                $removing_at_least_one = 1;
                last;
            }
        }
        if (!$removing_at_least_one) {
            if (!$invalidkitcomp) {
                $invalidkitcomp = $basename_to_remove;
            } else {
                $invalidkitcomp = join(',', $invalidkitcomp, $basename_to_remove);
            }
        }
    }


    if ($invalidkitcomp) {
        my %rsp;
        push @{ $rsp{data} }, "$invalidkitcomp kit components are not assigned to osimage $osimage";
        xCAT::MsgUtils->message("E", \%rsp, $callback);
        return 1;
    }


    # Now check if there is any other kitcomponent depending on this one.

    foreach my $kitcomponent (keys %kitcomps) {
        foreach my $osikitcomp (@osikitcomps) {
            (my $kitcomptable) = $tabs{kitcomponent}->getAttribs({ kitcompname => $osikitcomp }, 'kitcompdeps');
            if ($kitcomptable and $kitcomptable->{'kitcompdeps'}) {

                my @kitcompdeps = split(',', $kitcomptable->{'kitcompdeps'});
                foreach my $kitcompdependency (@kitcompdeps) {

                    my ($kitcompdep, $vers) = split /<=|>=|=|<|>/, $kitcompdependency;

                    # Get the kit component full name from basename.
                    my @entries;
                    if ($DBname =~ /^DB2/) {
                        @entries = $tabs{kitcomponent}->getAllAttribsWhere("\"basename\" = '$kitcompdep'", 'kitcompname', 'version', 'release');
                    } else {
                        @entries = $tabs{kitcomponent}->getAllAttribsWhere("basename = '$kitcompdep'", 'kitcompname', 'version', 'release');
                    }
                    unless (@entries) {
                        my %rsp;
                        push @{ $rsp{data} }, "kitcomponent $kitcompdep basename does not exist";
                        xCAT::MsgUtils->message("W", \%rsp, $callback);
                    }

                    my $kitcompdepname = get_highest_version('kitcompname', 'version', 'release', @entries);

                    if (($kitcomponent eq $kitcompdepname) and !$force and !exists($kitcomps{$osikitcomp})) {

                        # There is other kitcomponent depending on this one and there is no --force option
                        my %rsp;
                        push @{ $rsp{data} }, "Failed to remove kitcomponent $kitcomponent because $osikitcomp is still depending on it.  Use -f option to remove it anyway";
                        xCAT::MsgUtils->message("E", \%rsp, $callback);
                        return 1;
                    }
                }
            }
        }
    }


    # Reading installdir
    my $installdir = xCAT::TableUtils->getInstallDir();
    unless ($installdir) {
        $installdir = '/install';
    }
    $installdir =~ s/\/$//;


    # Remove each kitcomponent from osimage.

    my @newosikitcomps;
    foreach my $osikitcomp (@osikitcomps) {
        my $match = 0;
        foreach my $kitcomponent (keys %kitcomps) {
            if ($kitcomponent eq $osikitcomp) {
                $match = 1;
                last;
            }
        }
        if (!$match) {
            push @newosikitcomps, $osikitcomp;
        }
    }

    my $newosikitcomp = join ',', @newosikitcomps;
    $osimagetable->{'kitcomponents'} = $newosikitcomp;


    # Remove kitcomponent.postbootscripts and kitcomponent.genimage_postinstall from osimage.postbootscripts.

    my @osimagescripts;
    my @newosimagescripts;
    if ($osimagetable and $osimagetable->{'postbootscripts'}) {
        @osimagescripts = split(',', $osimagetable->{'postbootscripts'});
    }

    foreach my $osimagescript (@osimagescripts) {
        my $match = 0;
        foreach my $kitcomponent (keys %kitcomps) {
            my @kitcompscripts = split(',', $kitcomps{$kitcomponent}{postbootscripts});
            foreach my $kitcompscript (@kitcompscripts) {
                if ($osimagescript =~ /^$kitcompscript/) {
                    $match = 1;
                    last;
                }
            }

            last if ($match);
        }

        if (!$match or $noscripts) {
            push @newosimagescripts, $osimagescript;
        }

        #Remove genimage_postinstall from osimage.postbootscripts
        if ($osimagescript =~ /KIT_$osimage.postbootscripts/ and -e "$installdir/postscripts/KIT_$osimage.postbootscripts") {
            foreach my $kitcomponent (keys %kitcomps) {

                my @postbootlines;
                my @newlines;
                if (open(POSTBOOTSCRIPTS, "<", "$installdir/postscripts/KIT_$osimage.postbootscripts")) {
                    @postbootlines = <POSTBOOTSCRIPTS>;
                    close(POSTBOOTSCRIPTS);
                }

                my $match = 0;
                my @kitcompscripts = split(',', $kitcomps{$kitcomponent}{genimage_postinstall});
                foreach my $line (@postbootlines) {
                    chomp $line;
                    foreach my $kitcompscript (@kitcompscripts) {
                        if (grep(/$kitcompscript/, $line)) {
                            $match = 1;
                        }
                    }

                    if (!$match) {
                        push @newlines, $line . "\n";
                    }
                }

                # Now write the new postbootscripts file.
                if (open(NEWEXLIST, ">", "$installdir/postscripts/KIT_$osimage.postbootscripts")) {
                    print NEWEXLIST @newlines;
                    close(NEWEXLIST);
                }
            }
        }
    }

    my $newosimagescript = join ',', @newosimagescripts;
    $osimagetable->{'postbootscripts'} = $newosimagescript;

    # Remove symlink from osimage.otherpkgdir.

    # Read all the kitcomponents assigned to all the osimage to make sure the repo used by other osimage
    # will not be deleted.

    my @allosikitcomps = $tabs{osimage}->getAllAttribs('imagename', 'kitcomponents');

    (my $linuximagetable) = $tabs{linuximage}->getAttribs({ imagename => $osimage }, 'postinstall', 'exlist', 'otherpkglist', 'otherpkgdir', 'driverupdatesrc');
    if ($linuximagetable and $linuximagetable->{otherpkgdir}) {

        my $otherpkgdir = $linuximagetable->{otherpkgdir};

        #support mixed otherpkgdir http and local dir
        $otherpkgdir = get_local_otherpkgdir($otherpkgdir);
        foreach my $kitcomponent (keys %kitcomps) {

            my %newosikitcomponents;
            foreach my $allosikitcomp (@allosikitcomps) {
                if ($allosikitcomp->{kitcomponents} and $allosikitcomp->{imagename}) {
                    (my $allosiotherpkgdir) = $tabs{linuximage}->getAttribs({ imagename => $allosikitcomp->{imagename} }, 'otherpkgdir');

                    my @allkitcomps = split /,/, $allosikitcomp->{kitcomponents};
                    foreach my $allkitcomp (@allkitcomps) {
                        if ((($allosikitcomp->{imagename} ne $osimage) and
                                ($allosiotherpkgdir->{otherpkgdir} eq $otherpkgdir))
                            or ($allkitcomp ne $kitcomponent)) {
                            $newosikitcomponents{$allkitcomp} = 1;
                        }
                    }
                }
            }

            if ($kitcomps{$kitcomponent}{kitreponame}) {
                if (-d "$otherpkgdir/$kitcomps{$kitcomponent}{kitreponame}") {

                    # Check if this repo is used by other kitcomponent before removing the link
                    my $match = 0;
                    foreach my $osikitcomp (keys %newosikitcomponents) {

                        my $depkitrepodir;
                        (my $kitcomptable) = $tabs{kitcomponent}->getAttribs({ kitcompname => $osikitcomp }, 'kitreponame');
                        if ($kitcomptable and $kitcomptable->{kitreponame}) {
                            $depkitrepodir = "$otherpkgdir/$kitcomptable->{kitreponame}";
                        }
                        if ($depkitrepodir =~ /^$otherpkgdir\/$kitcomps{$kitcomponent}{kitreponame}$/) {
                            $match = 1;
                        }
                    }
                    if (!$match) {
                        if ($debianflag)
                        {
                            #Now we do not use moun --bind for kitrepo dir to otherpkgdir
                            #leave this line is support old way when using mount
                            system("umount -f $otherpkgdir/$kitcomps{$kitcomponent}{kitreponame} > /dev/null");
                        }
                        system("rm -rf $otherpkgdir/$kitcomps{$kitcomponent}{kitreponame}");
                    }
                }
            }
        }
    }

    # Remove genimage_postinstall from linuximage table
    if ($linuximagetable->{postinstall}) {
        my @scripts = split ',', $linuximagetable->{postinstall};
        foreach my $script (@scripts) {
            if ($script =~ /KIT_COMPONENTS.postinstall/ and -e "$installdir/osimages/$osimage/kits/KIT_COMPONENTS.postinstall") {

                foreach my $kitcomponent (keys %kitcomps) {

                    my @postinstalllines;
                    my @newlines;
                    if (open(POSTINSTALLSCRIPTS, "<", "$installdir/osimages/$osimage/kits/KIT_COMPONENTS.postinstall")) {
                        @postinstalllines = <POSTINSTALLSCRIPTS>;
                        close(POSTINSTALLSCRIPTS);
                    }

                    my @kitcompscripts = split(',', $kitcomps{$kitcomponent}{genimage_postinstall});
                    foreach my $line (@postinstalllines) {
                        chomp $line;
                        my $match = 0;
                        foreach my $kitcompscript (@kitcompscripts) {

                            if (grep(/$kitcompscript/, $line)) {
                                $match = 1;
                                last;
                            }
                        }
                        if (!$match) {
                            push @newlines, $line . "\n";
                        }
                    }

                    # Now write the new postbootscripts file.
                    if (open(NEWEXLIST, ">", "$installdir/osimages/$osimage/kits/KIT_COMPONENTS.postinstall")) {
                        print NEWEXLIST @newlines;
                        close(NEWEXLIST);
                    }
                }
            }
        }
    }

    # Remove kitcomponent exlist,otherpkglist and deploy_params from osimage

    my @kitlist;
    foreach my $kitcomponent (keys %kitcomps) {

        my %rsp;
        push @{ $rsp{data} }, "Removing kitcomponent $kitcomponent from osimage $osimage";
        xCAT::MsgUtils->message("I", \%rsp, $callback);

        if (!exists($kitcomps{$kitcomponent}{kitname})) {
            my %rsp;
            push @{ $rsp{data} }, "Could not find kit object for kitcomponent $kitcomponent";
            xCAT::MsgUtils->message("E", \%rsp, $callback);
            return 1;
        }

        # Reading kitdir
        my $kitdir     = '';
        my $exlistfile = '';
        my $kitname    = $kitcomps{$kitcomponent}{kitname};
        (my $kittable) = $tabs{kit}->getAttribs({ kitname => $kitname }, 'kitdir', 'kitdeployparams');


        # Removing exlist

        if ($linuximagetable and $linuximagetable->{exlist}) {
            my $match = 0;
            my @exlists = split ',', $linuximagetable->{exlist};
            foreach my $exlist (@exlists) {
                if ($exlist =~ /^$installdir\/osimages\/$osimage\/kits\/KIT_COMPONENTS.exlist$/) {
                    $match = 1;
                    last;
                }
            }

            my @lines = ();
            if ($match and -e "$installdir/osimages/$osimage/kits/KIT_COMPONENTS.exlist") {
                if (open(EXLIST, "<", "$installdir/osimages/$osimage/kits/KIT_COMPONENTS.exlist")) {
                    @lines = <EXLIST>;
                    if ($::VERBOSE) {
                        my %rsp;
                        push @{ $rsp{data} }, "Reading kit component exlist file $installdir/osimages/$osimage/kits/KIT_COMPONENTS.exlist";
                        xCAT::MsgUtils->message("I", \%rsp, $callback);
                    }
                } else {
                    my %rsp;
                    push @{ $rsp{data} }, "Could not open kit component exlist file $installdir/osimages/$osimage/kits/KIT_COMPONENTS.exlist";
                    xCAT::MsgUtils->message("E", \%rsp, $callback);
                    return 1;
                }

                if ($kittable and $kittable->{kitdir}) {
                    $kitdir = $kittable->{kitdir};
                }

                if (exists($kitcomps{$kitcomponent}{exlist})) {
                    $exlistfile = $kitcomps{$kitcomponent}{exlist};
                }

                my @newlines = ();
                foreach my $line (@lines) {
                    chomp $line;
                    if ($line =~ /^#INCLUDE:$kitdir\/other_files\/$exlistfile#$/) {
                        next;
                    }
                    push @newlines, $line . "\n";
                }
                if (open(NEWEXLIST, ">", "$installdir/osimages/$osimage/kits/KIT_COMPONENTS.exlist")) {
                    print NEWEXLIST @newlines;
                    close(NEWEXLIST);
                }

            }
        }

        # Removing otherpkglist

        if ($linuximagetable and $linuximagetable->{otherpkglist}) {
            my $match = 0;
            my @lines = ();

            my @otherpkglists = split ',', $linuximagetable->{otherpkglist};
            foreach my $otherpkglist (@otherpkglists) {
                if ($otherpkglist =~ /^$installdir\/osimages\/$osimage\/kits\/KIT_COMPONENTS.otherpkgs.pkglist$/) {
                    $match = 1;
                    last;
                }
            }

            if ($match and -e "$installdir/osimages/$osimage/kits/KIT_COMPONENTS.otherpkgs.pkglist") {
                if (open(OTHERPKGLIST, "<", "$installdir/osimages/$osimage/kits/KIT_COMPONENTS.otherpkgs.pkglist")) {
                    @lines = <OTHERPKGLIST>;
                    if ($::VERBOSE) {
                        my %rsp;
                        push @{ $rsp{data} }, "Reading kit component otherpkg pkglist $installdir/osimages/$osimage/kits/KIT_COMPONENTS.otherpkgs.pkglist";
                        xCAT::MsgUtils->message("I", \%rsp, $callback);
                    }
                } else {
                    my %rsp;
                    push @{ $rsp{data} }, "Could not open kit component exlist file $installdir/osimages/$osimage/kits/KIT_COMPONENTS.exlist";
                    xCAT::MsgUtils->message("E", \%rsp, $callback);
                    return 1;
                }

                my $basename = '';
                if (exists($kitcomps{$kitcomponent}{basename}) and exists($kitcomps{$kitcomponent}{kitreponame})) {
                    $basename = $kitcomps{$kitcomponent}{basename};
                    my $kitreponame = $kitcomps{$kitcomponent}{kitreponame};

                    my @newlines = ();
                    my $num      = 0;
                    my $inlist   = 0;
                    foreach my $line (@lines) {
                        chomp $line;

                        if ($line =~ /^#NEW_INSTALL_LIST#/) {
                            $num    = 1;
                            $inlist = 1;
                        }
                        if ($kitcomps{$kitcomponent}{prerequisite}) {
                            if (($line =~ /^$kitreponame\/prep_$basename$/) ||
                                ($line =~ /^$kitreponame\/prep-$basename$/)) {
                                if ($inlist) {
                                    $num--;
                                    foreach (1 .. $num) {
                                        pop @newlines;
                                    }
                                }
                                next;
                            }
                        }
                        if ($line =~ /^$kitreponame\/$basename$/) {
                            if ($inlist) {
                                $num--;
                                foreach (1 .. $num) {
                                    pop @newlines;
                                }
                            }
                            next;
                        }
                        push @newlines, $line . "\n";
                        $num++;
                    }

                    if (open(NEWOTHERPKGLIST, ">", "$installdir/osimages/$osimage/kits/KIT_COMPONENTS.otherpkgs.pkglist")) {
                        print NEWOTHERPKGLIST @newlines;
                        close(NEWOTHERPKGLIST);
                    }
                }
            }

            # Add this component basename and pkgnames to KIT_RMPKGS.otherpkg.pkglist
            if ($uninstall) {
                my @lines = ();

                mkpath("$installdir/osimages/$osimage/kits/");

                if (-e "$installdir/osimages/$osimage/kits/KIT_RMPKGS.otherpkgs.pkglist") {
                    if (open(RMOTHERPKGLIST, "<", "$installdir/osimages/$osimage/kits/KIT_RMPKGS.otherpkgs.pkglist")) {
                        @lines = <RMOTHERPKGLIST>;
                        close(RMOTHERPKGLIST);
                        if ($::VERBOSE) {
                            my %rsp;
                            push @{ $rsp{data} }, "Reading kit component rmpkgs file $installdir/osimages/$osimage/kits/KIT_RMPKGS.otherpkgs.pkglist";
                            xCAT::MsgUtils->message("I", \%rsp, $callback);
                        }
                    } else {
                        my %rsp;
                        push @{ $rsp{data} }, "Could not open kit component rmpkgs file $installdir/osimages/$osimage/kits/KIT_RMPKGS.otherpkgs.pkglist";
                        xCAT::MsgUtils->message("E", \%rsp, $callback);
                        return 1;
                    }
                }

                my @l           = @lines;
                my $basename    = '';
                my $kitreponame = '';
                my @kitpkgdeps  = ();

                if (exists($kitcomps{$kitcomponent}{basename}) and exists($kitcomps{$kitcomponent}{kitreponame})) {
                    $basename    = $kitcomps{$kitcomponent}{basename};
                    $kitreponame = $kitcomps{$kitcomponent}{kitreponame};
                } else {
                    my %rsp;
                    push @{ $rsp{data} }, "Could not open kit component table and read basename for kit component $kitcomp";
                    xCAT::MsgUtils->message("E", \%rsp, $callback);
                    return 1;
                }

                if (exists($kitcomps{$kitcomponent}{kitpkgdeps})) {
                    @kitpkgdeps = split ',', $kitcomps{$kitcomponent}{kitpkgdeps};
                }

                #  Add prerequisite to  KIT_RMPKGS.otherpkg.pkglist
                if ($kitcomps{$kitcomponent}{prerequisite}) {
                    my @kitpreps = split /,/, $kitcomps{$kitcomponent}{prerequisite};
                    foreach my $kitprep (@kitpreps) {
                        push @kitpkgdeps, $kitprep;
                    }
                }

                push @kitpkgdeps, $basename;

                my $update = 0;

                foreach my $kitpkgdep (@kitpkgdeps) {
                    next if ($kitpkgdep =~ /^$/);
                    my $matched = 0;
                    foreach my $line (@lines) {
                        chomp $line;
                        if ($line =~ /^-$kitpkgdep$/) {
                            $matched = 1;
                            last;
                        }
                    }

                    unless ($matched) {
                        unshift @l, "-$kitpkgdep\n";
                        $update = 1;
                    }

                }


                if ($update and open(RMPKGLIST, ">", "$installdir/osimages/$osimage/kits/KIT_RMPKGS.otherpkgs.pkglist")) {
                    print RMPKGLIST @l;
                    close(RMPKGLIST);
                }

                if ($linuximagetable and $linuximagetable->{otherpkglist}) {
                    my $match = 0;
                    my @otherpkglists = split ',', $linuximagetable->{otherpkglist};
                    foreach my $otherpkglist (@otherpkglists) {
                        if ($otherpkglist =~ /^$installdir\/osimages\/$osimage\/kits\/KIT_RMPKGS.otherpkgs.pkglist$/) {
                            $match = 1;
                            last;
                        }
                    }

                    if (!$match and -e "$installdir/osimages/$osimage/kits/KIT_RMPKGS.otherpkgs.pkglist") {
                        $linuximagetable->{otherpkglist} = $linuximagetable->{otherpkglist} . ",$installdir/osimages/$osimage/kits/KIT_RMPKGS.otherpkgs.pkglist"
                    }
                }
            }

        }

        # Removing deploy parameters

        if ($kittable and $kittable->{kitdeployparams} and $kittable->{kitdir}) {

            my $kitdir        = $kittable->{kitdir};
            my $kitdeployfile = $kittable->{kitdeployparams};

            # Check if there is other kitcomponent in the same kit.
            my $match = 0;
            if (exists($kitcomps{$kitcomponent}{kitname})) {
                my $kitname = $kitcomps{$kitcomponent}{kitname};

                foreach my $osikitcomp (@osikitcomps) {
                    (my $kitcomptable) = $tabs{kitcomponent}->getAttribs({ kitcompname => $osikitcomp }, 'kitname');

                    if ($kitcomptable and $kitcomptable->{kitname} and $kitcomptable->{kitname} eq $kitname and !exists($kitcomps{$osikitcomp}{name})) {
                        $match = 1;
                        last;
                    }
                }

                unless ($match) {
                    my @contents = ();
                    if (-e "$kitdir/other_files/$kitdeployfile") {
                        if (open(KITDEPLOY, "<", "$kitdir/other_files/$kitdeployfile")) {
                            @contents = <KITDEPLOY>;
                            close(KITDEPLOY);
                            if ($::VERBOSE) {
                                my %rsp;
                                push @{ $rsp{data} }, "Reading kit deployparams from $kitdir/other_files/$kitdeployfile";
                                xCAT::MsgUtils->message("I", \%rsp, $callback);
                            }
                        } else {
                            my %rsp;
                            push @{ $rsp{data} }, "Could not open kit deployparams file $kitdir/other_files/$kitdeployfile";
                            xCAT::MsgUtils->message("E", \%rsp, $callback);
                            return 1;
                        }

                        push @contents, "#INCLUDE:$kitdir/other_files/$kitdeployfile#";
                    }

                    my @lines = ();
                    if (-e "$installdir/osimages/$osimage/kits/KIT_DEPLOY_PARAMS.otherpkgs.pkglist") {

                        system("cp $installdir/osimages/$osimage/kits/KIT_DEPLOY_PARAMS.otherpkgs.pkglist $installdir/osimages/$osimage/kits/KIT_DEPLOY_PARAMS.otherpkgs.pkglist.orig");

                        if (open(DEPLOYPARAM, "<", "$installdir/osimages/$osimage/kits/KIT_DEPLOY_PARAMS.otherpkgs.pkglist")) {
                            @lines = <DEPLOYPARAM>;
                            close(DEPLOYPARAM);
                            if ($::VERBOSE) {
                                my %rsp;
                                push @{ $rsp{data} }, "Reading kit deployparams file $installdir/osimages/$osimage/kits/KIT_DEPLOY_PARAMS.otherpkgs.pkglist";
                                xCAT::MsgUtils->message("I", \%rsp, $callback);
                            }
                        } else {
                            my %rsp;
                            push @{ $rsp{data} }, "Could not open kit deployparams file $installdir/osimages/$osimage/kits/KIT_DEPLOY_PARAMS.otherpkgs.pkglist";
                            xCAT::MsgUtils->message("E", \%rsp, $callback);
                            return 1;
                        }
                    }

                    # Check if each deploy parameter is used by other kitcomponent.
                    my @otherlines = ();
                    foreach my $osikitcomp (@osikitcomps) {
                        next if (exists($kitcomps{$osikitcomp}{name}));

                        (my $kitcomptable) = $tabs{kitcomponent}->getAttribs({ kitcompname => $osikitcomp }, 'kitname');
                        if ($kitcomptable and $kitcomptable->{kitname}) {
                            (my $kittable) = $tabs{kit}->getAttribs({ kitname => $kitcomptable->{kitname} }, 'kitdir', 'kitdeployparams');
                            if ($kittable and $kittable->{kitdeployparams} and $kittable->{kitdir}) {
                                my @otherdeployparams;
                                my $deployparam_file = $kittable->{kitdir} . "/other_files/" . $kittable->{kitdeployparams};
                                if (-e "$deployparam_file") {

                                    if (open(OTHERDEPLOYPARAM, "<", "$deployparam_file")) {
                                        @otherdeployparams = <OTHERDEPLOYPARAM>;
                                        close(OTHERDEPLOYPARAM);
                                    }

                                    foreach (@otherdeployparams) {
                                        push @otherlines, $_;
                                    }

                                    push @otherlines, "#INCLUDE:$deployparam_file#";
                                }
                            }
                        }
                    }


                    my @newcontents = ();
                    foreach my $line (@lines) {
                        chomp $line;
                        my $found = 0;

                        #check if the parameter is used by other kitcomponent
                        foreach my $otherline (@otherlines) {
                            chomp $otherline;
                            if ($otherline && $line =~ m!$otherline!) {
                                $found = 1;
                                last;
                            }
                        }

                        if ($found) {
                            push @newcontents, $line . "\n";
                        } else {
                            foreach my $content (@contents) {
                                chomp $content;
                                if ($content && $line =~ m!$content!) {
                                    $found = 1;
                                    last;
                                }
                            }

                            unless ($found) {
                                push @newcontents, $line . "\n";
                            }
                        }
                    }

                    # Write the updated lines to kit deployparams file
                    if (open(NEWDEPLOYPARAM, ">", "$installdir/osimages/$osimage/kits/KIT_DEPLOY_PARAMS.otherpkgs.pkglist")) {
                        print NEWDEPLOYPARAM @newcontents;
                        close(NEWDEPLOYPARAM);
                    }
                }
            }
        }


        # Remove driverpacks from linuximage

        if ($linuximagetable and $linuximagetable->{driverupdatesrc}) {
            if (exists($kitcomps{$kitcomponent}{driverpacks})) {
                my @driverpacks = split ',', $kitcomps{$kitcomponent}{driverpacks};
                my @driverupdatesrcs = split ',', $linuximagetable->{driverupdatesrc};

                my @newdriverupdatesrcs = ();

                foreach my $driverupdatesrc (@driverupdatesrcs) {
                    my $matched = 0;
                    foreach my $driverpack (@driverpacks) {
                        if ($driverpack eq $driverupdatesrc) {
                            $matched = 1;
                            last;
                        }
                    }

                    unless ($matched) {
                        push @newdriverupdatesrcs, $driverupdatesrc;
                    }
                }

                my $newdriverupdatesrc = join ',', @newdriverupdatesrcs;
                $linuximagetable->{driverupdatesrc} = $newdriverupdatesrc;
            }
        }

        push @kitlist, $kitcomponent;

    }

    my $kitcompnames = join ',', @kitlist;
    my %rsp;
    push @{ $rsp{data} }, "kitcomponents $kitcompnames were removed from osimage $osimage successfully";
    xCAT::MsgUtils->message("I", \%rsp, $callback);

    # Write linuximage table with all the above udpates.
    $tabs{linuximage}->setAttribs({ imagename => $osimage }, \%{$linuximagetable});

    # Write osimage table with all the above udpates.
    $tabs{osimage}->setAttribs({ imagename => $osimage }, \%{$osimagetable});

    #After all the data updated in osimage and linuximage table
    #check if these kitcomponents are assigned to other osimage
    #if these kitcomponents are not used by other osimage, find their kitrepo and kitrepo directory under otherpkg dir
    #delete these kitrepo
    my @allosikitcomps = $tabs{osimage}->getAllAttribs('imagename', 'kitcomponents');

    (my $linuximagetable) = $tabs{linuximage}->getAttribs({ imagename => $osimage }, 'postinstall', 'exlist', 'otherpkglist', 'otherpkgdir', 'driverupdatesrc');
    if ($linuximagetable and $linuximagetable->{otherpkgdir}) {

        my $otherpkgdir = $linuximagetable->{otherpkgdir};
        $otherpkgdir = get_local_otherpkgdir($otherpkgdir);

        foreach my $kitcomponent (keys %kitcomps) {

            my %newosikitcomponents;
            foreach my $allosikitcomp (@allosikitcomps) {
                if ($allosikitcomp->{kitcomponents} and $allosikitcomp->{imagename}) {
                    my @allkitcomps = split /,/, $allosikitcomp->{kitcomponents};
                    foreach my $allkitcomp (@allkitcomps) {
                        if ($allosikitcomp->{imagename} ne $osimage or $allkitcomp ne $kitcomponent) {
                            $newosikitcomponents{$allkitcomp} = 1;
                        }
                    }
                }
            }

            if ($kitcomps{$kitcomponent}{kitreponame}) {
                if (-d "$otherpkgdir/$kitcomps{$kitcomponent}{kitreponame}") {

                    # Check if this repo is used by other kitcomponent before removing the link
                    my $match = 0;
                    foreach my $osikitcomp (keys %newosikitcomponents) {

                        my $depkitrepodir;
                        (my $kitcomptable) = $tabs{kitcomponent}->getAttribs({ kitcompname => $osikitcomp }, 'kitreponame');
                        if ($kitcomptable and $kitcomptable->{kitreponame}) {
                            $depkitrepodir = "$otherpkgdir/$kitcomptable->{kitreponame}";
                        }
                        if ($depkitrepodir =~ /^$otherpkgdir\/$kitcomps{$kitcomponent}{kitreponame}$/) {
                            $match = 1;
                        }
                    }
                    if (!$match) {
                        if ($debianflag)
                        {
                            system("umount -f $otherpkgdir/$kitcomps{$kitcomponent}{kitreponame} > /dev/null");
                        }
                        system("rm -rf $otherpkgdir/$kitcomps{$kitcomponent}{kitreponame}");
                    }
                }
            }
        }
    }



    return;
}

#-------------------------------------------------------

=head3  chkkitcomp

    Check if the kit components fits to osimage

=cut

#-------------------------------------------------------
sub chkkitcomp
{
    my $request         = shift;
    my $callback        = shift;
    my $request_command = shift;

    my $xusage = sub {
        my %rsp;
        push @{ $rsp{data} }, "Usage: chkkitcomp - Check if a Kit component is compatible with an xCAT osimage.";
        push @{ $rsp{data} }, "\tchkkitcomp [-h|--help]";
        push @{ $rsp{data} }, "\tchkkitcomp [-v|--version]";
        push @{ $rsp{data} }, "\tchkkitcomp [-V|--verbose] -i <osimage> <kitcompname_list>";
        xCAT::MsgUtils->message("I", \%rsp, $callback);
    };

    if ($^O ne 'linux') {
        my $rsp = {};
        push @{ $rsp->{data} }, "The chkkitcomp command is only supported on Linux.\n";
        xCAT::MsgUtils->message("E", $rsp, $::CALLBACK);
        return 1;
    }

    unless (defined($request->{arg})) { $xusage->(1); return; }
    @ARGV = @{ $request->{arg} };
    if ($#ARGV == -1) {
        $xusage->(1);
        return;
    }

    GetOptions(
        'h|help'    => \$help,
        'V|verbose' => \$::VERBOSE,
        'v|version' => \$vers,
        'i=s'       => \$osimage
    );

    if ($help) {
        $xusage->(0);
        return;
    }

    # Option -v for version
    if (defined($vers)) {
        create_version_response('chkkitcomp');
        return 1;    # no usage - just exit
    }
    my $DBname = xCAT::Utils->get_DBName;    # support for DB2
    my %tabs   = ();
    my @tables = qw(kit kitrepo kitcomponent osimage osdistro linuximage);
    foreach my $t (@tables) {
        $tabs{$t} = xCAT::Table->new($t, -create => 1, -autocommit => 1);

        if (!exists($tabs{$t})) {
            my %rsp;
            push @{ $rsp{data} }, "Could not open xCAT table $t";
            xCAT::MsgUtils->message("E", \%rsp, $callback);
            return 1;
        }
    }

    # Check if all the kitcomponents are existing before processing

    my %kitcomps;
    my %kitcompbasename;
    my $des = shift @ARGV;
    my @kitcomponents = split ',', $des;
    foreach my $kitcomponent (@kitcomponents) {

        # Check if it is a kitcompname or basename
        (my $kitcomptable) = $tabs{kitcomponent}->getAttribs({ kitcompname => $kitcomponent }, 'kitname', 'kitcompdeps', 'kitpkgdeps', 'kitreponame', 'basename', 'serverroles');
        if ($kitcomptable and $kitcomptable->{'kitname'}) {
            $kitcomps{$kitcomponent}{name}       = $kitcomponent;
            $kitcomps{$kitcomponent}{kitname}    = $kitcomptable->{kitname};
            $kitcomps{$kitcomponent}{kitpkgdeps} = $kitcomptable->{kitpkgdeps};
            $kitcomps{$kitcomponent}{kitcompdeps} = $kitcomptable->{kitcompdeps};
            $kitcomps{$kitcomponent}{basename} = $kitcomptable->{basename};
            $kitcomps{$kitcomponent}{kitreponame} = $kitcomptable->{kitreponame};
            $kitcomps{$kitcomponent}{serverroles} = $kitcomptable->{serverroles};
            $kitcompbasename{ $kitcomptable->{basename} } = 1;
        } else {
            my @entries;
            if ($DBname =~ /^DB2/) {
                @entries = $tabs{kitcomponent}->getAllAttribsWhere("\"basename\" = '$kitcomponent'", 'kitcompname', 'version', 'release');
            } else {
                @entries = $tabs{kitcomponent}->getAllAttribsWhere("basename = '$kitcomponent'", 'kitcompname', 'version', 'release');
            }
            unless (@entries) {
                my %rsp;
                push @{ $rsp{data} }, "$kitcomponent kitcomponent does not exist";
                xCAT::MsgUtils->message("E", \%rsp, $callback);
                return 1;
            }

            my $highest = get_highest_version('kitcompname', 'version', 'release', @entries);
            $kitcomps{$highest}{name} = $highest;
            (my $kitcomptable) = $tabs{kitcomponent}->getAttribs({ kitcompname => $highest }, 'kitname', 'kitpkgdeps', 'kitcompdeps', 'kitreponame', 'basename', 'serverroles');
            $kitcomps{$highest}{kitname}     = $kitcomptable->{kitname};
            $kitcomps{$highest}{kitpkgdeps}  = $kitcomptable->{kitpkgdeps};
            $kitcomps{$highest}{kitcompdeps} = $kitcomptable->{kitcompdeps};
            $kitcomps{$highest}{basename}    = $kitcomptable->{basename};
            $kitcomps{$highest}{kitreponame} = $kitcomptable->{kitreponame};
            $kitcomps{$highest}{serverroles} = $kitcomptable->{serverroles};
            $kitcompbasename{$kitcomponent}  = 1;
        }
    }

    # Verify if the kitcomponents fitting to the osimage or not.
    my %os;
    my $osdistrotable;
    (my $osimagetable) = $tabs{osimage}->getAttribs({ imagename => $osimage }, 'osdistroname', 'serverrole', 'kitcomponents', 'osname', 'osvers', 'osarch');
    if ($osimagetable and $osimagetable->{'osdistroname'}) {
        ($osdistrotable) = $tabs{osdistro}->getAttribs({ osdistroname => $osimagetable->{'osdistroname'} }, 'basename', 'majorversion', 'minorversion', 'arch', 'type');
        if (!$osdistrotable or !$osdistrotable->{basename}) {
            my %rsp;
            push @{ $rsp{data} }, "$osdistroname osdistro does not exist";
            xCAT::MsgUtils->message("E", \%rsp, $callback);
            return 1;
        }

        # Read basename,majorversion,minorversion,arch,type, from osdistro table
        $os{$osimage}{basename}     = lc($osdistrotable->{basename});
        $os{$osimage}{majorversion} = lc($osdistrotable->{majorversion});
        $os{$osimage}{minorversion} = lc($osdistrotable->{minorversion});
        $os{$osimage}{arch}         = lc($osdistrotable->{arch});
        $os{$osimage}{type}         = lc($osdistrotable->{type});

        # Read serverrole from osimage.
        $os{$osimage}{serverrole} = lc($osimagetable->{'serverrole'});

    } elsif (!$osimagetable) {
        my %rsp;
        push @{ $rsp{data} }, "osimage $osimage does not exist";
        xCAT::MsgUtils->message("E", \%rsp, $callback);
        return 1;
    } elsif (!$osimagetable->{'osname'}) {
        my %rsp;
        push @{ $rsp{data} }, "osimage $osimage does not contain a valid 'osname' attribute";
        xCAT::MsgUtils->message("E", \%rsp, $callback);
        return 1;
    } elsif (!$osimagetable->{'osvers'}) {
        my %rsp;
        push @{ $rsp{data} }, "osimage $osimage does not contain a valid 'osvers' attribute";
        xCAT::MsgUtils->message("E", \%rsp, $callback);
        return 1;
    } elsif (!$osimagetable->{'osarch'}) {
        my %rsp;
        push @{ $rsp{data} }, "osimage $osimage does not contain a valid 'osarch' attribute";
        xCAT::MsgUtils->message("E", \%rsp, $callback);
        return 1;
    } else {
        $os{$osimage}{type}       = lc($osimagetable->{'osname'});
        $os{$osimage}{arch}       = lc($osimagetable->{'osarch'});
        $os{$osimage}{serverrole} = lc($osimagetable->{'serverrole'});

        my ($basename, $majorversion, $minorversion) = $osimagetable->{'osvers'} =~ /^(\D+)(\d+)\W+(\d+)/;
        $os{$osimage}{basename}     = lc($basename);
        $os{$osimage}{majorversion} = lc($majorversion);
        $os{$osimage}{minorversion} = lc($minorversion);
    }

    my @kitcompnames;
    foreach my $kitcomp (keys %kitcomps) {
        if ($kitcomps{$kitcomp}{kitname} and $kitcomps{$kitcomp}{kitreponame}) {

            # Read ostype from kit table
            (my $kittable) = $tabs{kit}->getAttribs({ kitname => $kitcomps{$kitcomp}{kitname} }, 'ostype');
            if ($kittable and $kittable->{ostype}) {
                $kitcomps{$kitcomp}{ostype} = lc($kittable->{ostype});
            } else {
                my %rsp;
                push @{ $rsp{data} }, "$kitcomp ostype does not exist";
                xCAT::MsgUtils->message("E", \%rsp, $callback);
                return 1;
            }

            # Read osbasename, osmajorversion,osminorversion,osarch,compat_osbasenames from kitrepo table
            (my $kitrepotable) = $tabs{kitrepo}->getAttribs({ kitreponame => $kitcomps{$kitcomp}{kitreponame} }, 'osbasename', 'osmajorversion', 'osminorversion', 'osarch', 'compat_osbasenames');
            if ($kitrepotable and $kitrepotable->{osbasename} and $kitrepotable->{osmajorversion} and $kitrepotable->{osarch}) {
                if ($kitrepotable->{compat_osbasenames}) {
                    $kitcomps{$kitcomp}{osbasename} = lc($kitrepotable->{osbasename}) . ',' . lc($kitrepotable->{compat_osbasenames});
                } else {
                    $kitcomps{$kitcomp}{osbasename} = lc($kitrepotable->{osbasename});
                }

                $kitcomps{$kitcomp}{osmajorversion} = lc($kitrepotable->{osmajorversion});
                $kitcomps{$kitcomp}{osminorversion} = lc($kitrepotable->{osminorversion});
                $kitcomps{$kitcomp}{osarch} = lc($kitrepotable->{osarch});
            } else {
                my %rsp;
                push @{ $rsp{data} }, "$kitcomp osbasename,osmajorversion,osminorversion or osarch does not exist";
                xCAT::MsgUtils->message("E", \%rsp, $callback);
                return 1;
            }

        } else {
            my %rsp;
            push @{ $rsp{data} }, "$kitcomp kitname $kitcomptable->{'kitname'} or kitrepo name $kitcomptable->{'kitreponame'} or serverroles $kitcomps{$kitcomp}{serverroles} does not exist";
            xCAT::MsgUtils->message("E", \%rsp, $callback);
            return 1;
        }

        # Validate each attribute in kitcomp.
        my $catched = 0;
        my @osbasename = split ',', $kitcomps{$kitcomp}{osbasename};
        foreach (@osbasename) {
            if ($os{$osimage}{basename} eq $_) {
                $catched = 1;
            }
        }
        unless ($catched) {
            my %rsp;
            push @{ $rsp{data} }, "kit component $kitcomp is not compatible with osimage $osimage with attribute OS";
            xCAT::MsgUtils->message("E", \%rsp, $callback);
            return 1;
        }

        if ($os{$osimage}{majorversion} ne $kitcomps{$kitcomp}{osmajorversion}) {
            my %rsp;
            push @{ $rsp{data} }, "kit component $kitcomp is not compatible with osimage $osimage with attribute majorversion";
            xCAT::MsgUtils->message("E", \%rsp, $callback);
            return 1;
        }

        if ($kitcomps{$kitcomp}{osminorversion} ) {
            if (check_minorversion($kitcomps{$kitcomp}{osminorversion}, $os{$osimage}{minorversion}))
            {
                my %rsp;
                push @{ $rsp{data} }, "kit component $kitcomp is not compatible with osimage $osimage with attribute minorversion";
                xCAT::MsgUtils->message("E", \%rsp, $callback);
                return 1;
            }
        }

        if ($os{$osimage}{arch} ne $kitcomps{$kitcomp}{osarch} && $kitcomps{$kitcomp}{osarch} ne 'noarch') {
            my %rsp;
            push @{ $rsp{data} }, "kit component $kitcomp is not compatible with osimage $osimage with attribute arch";
            xCAT::MsgUtils->message("E", \%rsp, $callback);
            return 1;
        }

        if ($os{$osimage}{type} ne $kitcomps{$kitcomp}{ostype}) {
            my %rsp;
            push @{ $rsp{data} }, "kit component $kitcomp is not compatible with osimage $osimage with attribute type";
            xCAT::MsgUtils->message("E", \%rsp, $callback);
            return 1;
        }

        if ($os{$osimage}{serverrole}) {
            my $match = 0;
            my @os_serverroles = split /,/, $os{$osimage}{serverrole};
            my @kitcomp_serverroles = split /,/, $kitcomps{$kitcomp}{serverroles};
            foreach my $os_serverrole (@os_serverroles) {
                foreach my $kitcomp_serverrole (@kitcomp_serverroles) {
                    if ($os_serverrole eq $kitcomp_serverrole) {
                        $match = 1;
                        last;
                    }
                }

                if ($match) {
                    last;
                }
            }
            if (!$match) {
                my %rsp;
                push @{ $rsp{data} }, "osimage $osimage is not compatible with kit component $kitcomp with attribute serverrole";
                xCAT::MsgUtils->message("E", \%rsp, $callback);
                return 1;
            }
        }

        # Check if this kit component's dependencies are in the kitcomponent list.
        if ($kitcomps{$kitcomp}{kitcompdeps}) {
            my ($kitcompdep, $vers) = split /<=|>=|=|<|>/, $kitcomps{$kitcomp}{kitcompdeps};
            if (!exists($kitcompbasename{$kitcompdep})) {
                my %rsp;
                push @{ $rsp{data} }, "kit component $kitcomp dependency $kitcomps{$kitcomp}{kitcompdeps} doesn't exist";
                xCAT::MsgUtils->message("E", \%rsp, $callback);
                return 1;
            }
        }

        push @kitcompnames, $kitcomp;
    }

    my $kitcompnamelist = join ',', @kitcompnames;

    my %rsp;
    push @{ $rsp{data} }, "Kit components $kitcompnamelist are compatible with osimage $osimage";
    xCAT::MsgUtils->message("I", \%rsp, $callback);

    return;
}

#----------------------------------------------------------------------------

=head3  lskit_usage

        Display the lskit usage
=cut

#-----------------------------------------------------------------------------

sub lskit_usage {
    my $rsp;
    push @{ $rsp->{data} },
      "\nUsage: lskit - List infomation for one or more kits.\n";
    push @{ $rsp->{data} },
"\tlskit [-V|--verbose] [-x|--xml|--XML] [-K|--kitattr kitattr_names]\n\t[-R|--repoattr repoattr_names] [-C|--compattr compattr_names]\n\t[kit_names]\n ";
    push @{ $rsp->{data} }, "\tlskit [-h|--help|-?]\n";
    push @{ $rsp->{data} }, "\tlskit [-v|--version]\n ";
    push @{ $rsp->{data} }, "\tlskit [-F|--framework] kit_path_name\n ";
    xCAT::MsgUtils->message("I", $rsp, $::CALLBACK);
    return 0;
}


#----------------------------------------------------------------------------

=head3  lskitcomp_usage

        Display the lskitcomp usage
=cut

#-----------------------------------------------------------------------------

sub lskitcomp_usage {
    my $rsp;
    push @{ $rsp->{data} },
      "\nUsage: lskitcomp - List information for one or more Kit components.";
    push @{ $rsp->{data} }, "\tlskitcomp [-h|--help|-?]";
    push @{ $rsp->{data} }, "\tlskitcomp [-v|--version]";
    push @{ $rsp->{data} }, "\tlskitcomp [-V|--verbose] [-x|--xml|--XML] \n\t\t[-C|--compattr compattr_names] [-O|--osdistro os_distro]\n\t\t[-S|--serverrole server_role] [kitcomp_names]\n ";
    xCAT::MsgUtils->message("I", $rsp, $::CALLBACK);
    return 0;
}


#----------------------------------------------------------------------------

=head3  lskitdeployparam_usage

        Display the lskitdeployparam usage
=cut

#-----------------------------------------------------------------------------

sub lskitdeployparam_usage {
    my $rsp;
    push @{ $rsp->{data} }, "\nUsage: lskitdeployparam - List the kit deployment parameters for full Kits or Kit components.";
    push @{ $rsp->{data} }, "\tlskitdeployparam [-h|--help|-?]";
    push @{ $rsp->{data} }, "\tlskitdeployparam [-v|--version]";
    push @{ $rsp->{data} }, "\tlskitdeployparam [-V|--verbose] [-x|--xml|--XML] \n\t\t[-k|--kitname kit_names] [-c|--compname comp_names]\n";
    xCAT::MsgUtils->message("I", $rsp, $::CALLBACK);
    return 0;
}

#----------------------------------------------------------------------------

=head3  create_version_response

        Create a response containing the command name and version
=cut

#-----------------------------------------------------------------------------
sub create_version_response {
    my $command = shift;
    my $rsp;
    my $version = xCAT::Utils->Version();
    push @{ $rsp->{data} }, "$command - xCAT $version";
    push @{ $rsp->{data} }, "\tkitframework = $::KITFRAMEWORK";
    push @{ $rsp->{data} }, "\tcompatible_frameworks = $::COMPATIBLE_KITFRAMEWORKS\n";
    xCAT::MsgUtils->message("I", $rsp, $::CALLBACK);
}


#----------------------------------------------------------------------------

=head3  create_error_response

        Create a response containing a single error message
        Arguments:  error message
=cut

#-----------------------------------------------------------------------------
sub create_error_response {
    my $error_msg = shift;
    my $rsp;
    push @{ $rsp->{data} }, $error_msg;
    xCAT::MsgUtils->message("E", $rsp, $::CALLBACK);
}


#----------------------------------------------------------------------------

=head3   lskit_processargs

        Process the lskit command line
        Returns:
                0 - OK
                1 - just print version
                2 - just print help
                3 - error
=cut

#-----------------------------------------------------------------------------
sub lskit_processargs {
    if (defined($::args) && @{$::args}) {
        @ARGV = @{$::args};
    }

    # parse the options
    # options can be bundled up like -vV, flag unsupported options
    Getopt::Long::Configure("bundling", "no_ignore_case", "no_pass_through");
    my $getopt_success = Getopt::Long::GetOptions(
        'help|h|?'      => \$::opt_h,
        'kitattr|K=s'   => \$::opt_K,
        'repoattr|R=s'  => \$::opt_R,
        'compattr|C=s'  => \$::opt_C,
        'framework|F=s' => \$::opt_F,
        'verbose|V'     => \$::opt_V,
        'version|v'     => \$::opt_v,
        'xml|XML|x'     => \$::opt_x,
    );

    if (!$getopt_success) {
        return 3;
    }

    # Option -h for Help
    if (defined($::opt_h)) {
        return 2;
    }

    # Option -v for version
    if (defined($::opt_v)) {
        create_version_response('lskit');
        return 1;    # no usage - just exit
    }

    # Option -V for verbose output
    if (defined($::opt_V)) {
        $::VERBOSE = 1;
    }

    # Option -K for kit attributes
    if (defined($::opt_K)) {
        $::kitattrs = split_comma_delim_str($::opt_K);
        ensure_kitname_attr_in_list($::kitattrs);
        if (check_attr_names_exist('kit', $::kitattrs) != 0) {
            return 3;
        }
    }

    # Option -R for kit repo attributes
    if (defined($::opt_R)) {
        $::kitrepoattrs = split_comma_delim_str($::opt_R);
        ensure_kitname_attr_in_list($::kitrepoattrs);
        if (check_attr_names_exist('kitrepo', $::kitrepoattrs) != 0) {
            return 3;
        }
    }

    # Option -C for kit component attributes
    if (defined($::opt_C)) {
        $::kitcompattrs = split_comma_delim_str($::opt_C);
        ensure_kitname_attr_in_list($::kitcompattrs);
        if (check_attr_names_exist('kitcomponent', $::kitcompattrs) != 0) {
            return 3;
        }
    }

    # Kit names
    my $kitnames_str = shift(@ARGV);
    if (defined($kitnames_str)) {
        my @tmp = split(/,/, $kitnames_str);
        $::kitnames = \@tmp;
        if (check_attr_values_exist('kit', 'kitname', 'kit names', $::kitnames) != 0) {
            return 3;
        }
    }

    # Other attributes are not allowed
    my $more_input = shift(@ARGV);
    if (defined($more_input)) {
        create_error_response("Invalid input: $more_input \n");
        return 3;
    }

    return 0;
}


#----------------------------------------------------------------------------

=head3   lskitcomp_processargs

        Process the lskitcomp command line
        Returns:
                0 - OK
                1 - just print version
                2 - just print help
                3 - error
=cut

#-----------------------------------------------------------------------------
sub lskitcomp_processargs {

    if (defined($::args) && @{$::args}) {
        @ARGV = @{$::args};
    }

    # parse the options
    # options can be bundled up like -vV, flag unsupported options
    Getopt::Long::Configure("bundling", "no_ignore_case", "no_pass_through");
    my $getopt_success = Getopt::Long::GetOptions(
        'help|h|?'       => \$::opt_h,
        'compattr|C=s'   => \$::opt_C,
        'osdistro|O=s'   => \$::opt_O,
        'serverrole|S=s' => \$::opt_S,
        'verbose|V'      => \$::opt_V,
        'version|v'      => \$::opt_v,
        'xml|XML|x'      => \$::opt_x,
    );

    if (!$getopt_success) {
        return 3;
    }

    # Option -h for Help
    if (defined($::opt_h)) {
        return 2;
    }

    # Option -v for version
    if (defined($::opt_v)) {
        create_version_response('lskitcomp');
        return 1;    # no usage - just exit
    }

    # Option -V for verbose output
    if (defined($::opt_V)) {
        $::VERBOSE = 1;
    }

    # Option -C for kit component attributes
    if (defined($::opt_C)) {
        $::kitcompattrs = split_comma_delim_str($::opt_C);
        push(@{$::kitcompattrs}, "kitcompname");
        ensure_kitname_attr_in_list($::kitcompattrs);
        if (check_attr_names_exist('kitcomponent', $::kitcompattrs) != 0) {
            return 3;
        }
    }

    # Option -O for osdistro name
    $::osdistroname = $::opt_O;
    if (defined($::osdistroname)) {
        if (check_attr_values_exist('osdistro', 'osdistroname', 'os distro', [$::osdistroname]) != 0) {
            return 3;
        }
    }

    # Option -S for server role
    $::serverrole = $::opt_S;

    # Kit component names
    my $kitcompnames_str = shift(@ARGV);
    if (defined($kitcompnames_str)) {
        my @tmp = split(/,/, $kitcompnames_str);
        $::kitcompnames = \@tmp;
        if (check_attr_values_exist('kitcomponent', 'kitcompname', 'kit component names', $::kitcompnames) != 0) {
            return 3;
        }
    }

    # Other attributes are not allowed
    my $more_input = shift(@ARGV);
    if (defined($more_input)) {
        create_error_response("Invalid input: $more_input \n");
        return 3;
    }

    return 0;
}


#----------------------------------------------------------------------------

=head3   lskitdeployparam_processargs

        Process the lskitdeployparam command line
        Returns:
                0 - OK
                1 - just print version
                2 - just print help
                3 - error
=cut

#-----------------------------------------------------------------------------
sub lskitdeployparam_processargs {

    if (defined($::args) && @{$::args}) {
        @ARGV = @{$::args};
    }

    # parse the options
    # options can be bundled up like -vV, flag unsupported options
    Getopt::Long::Configure("bundling", "no_ignore_case", "no_pass_through");
    my $getopt_success = Getopt::Long::GetOptions(
        'help|h|?'     => \$::opt_h,
        'kitname|k=s'  => \$::opt_k,
        'compname|c=s' => \$::opt_c,
        'verbose|V'    => \$::opt_V,
        'version|v'    => \$::opt_v,
        'xml|XML|x'    => \$::opt_x,
    );

    if (!$getopt_success) {
        return 3;
    }

    # Option -h for Help
    if (defined($::opt_h)) {
        return 2;
    }

    # Option -v for version
    if (defined($::opt_v)) {
        create_version_response('lskitdeployparam');
        return 1;    # no usage - just exit
    }

    # Option -V for verbose output
    if (defined($::opt_V)) {
        $::VERBOSE = 1;
    }

    # Ensure -k and -c option not used together
    if (defined($::opt_k) && defined($::opt_c)) {
        create_error_response("The -k and -c options cannot be used together.");
        return 3;
    }

    # Ensure -k or -c option are specified
    if (!defined($::opt_k) && !defined($::opt_c)) {
        create_error_response("The -k or -c option must be specified.");
        return 3;
    }

    # Option -k for kit names
    if (defined($::opt_k)) {
        $::kitnames = split_comma_delim_str($::opt_k);
        if (check_attr_values_exist('kit', 'kitname', 'kit names', $::kitnames) != 0) {
            return 3;
        }
    }

    # Option -c for kitocmponent names
    if (defined($::opt_c)) {
        $::kitcompnames = split_comma_delim_str($::opt_c);
        if (check_attr_values_exist('kitcomponent', 'kitcompname', 'kit component names', $::kitcompnames) != 0) {
            return 3;
        }
    }

    # Other attributes are not allowed
    my $more_input = shift(@ARGV);
    if (defined($more_input)) {
        create_error_response("Invalid input: $more_input \n");
        return 3;
    }

    return 0;
}


#----------------------------------------------------------------------------

=head3  split_comma_delim_str

        Split comma-delimited list of strings into an array.

        Arguments: comma-delimited string
        Returns:   Returns list of strings (ref)

=cut

#-----------------------------------------------------------------------------
sub split_comma_delim_str {
    my $input_str = shift;

    my @result = split(/,/, $input_str);
    return \@result;
}


#----------------------------------------------------------------------------

=head3  ensure_kitname_attr_in_list

        Checks if 'kitname' attribute is in specified attribute list.
        If not, then add it.

        Arguments: list of attribute names (ref)
=cut

#-----------------------------------------------------------------------------
sub ensure_kitname_attr_in_list {
    my $attrs = shift;

    if (defined($attrs)) {
        if (!grep(/^kitname$/, @$attrs)) {
            push(@$attrs, "kitname");
        }
    }
}


#----------------------------------------------------------------------------

=head3  check_attr_names_exist

        Check if list of DB attribute names exist in a table
        Arguments:  a table name
                    a list of attribute names to check (ref)
        Returns:
                0 - OK
                1 - error
=cut

#-----------------------------------------------------------------------------
sub check_attr_names_exist {

    my $tablename = shift;
    my $attrs     = shift;
    my @badattrs  = ();

    if (defined($attrs)) {
        my $schema = xCAT::Table->getTableSchema($tablename);
        my @cols   = @{ $schema->{cols} };
        foreach my $attr (@{$attrs}) {
            if (!grep { $_ eq $attr } @cols) {
                push(@badattrs, $attr);
            }
        }
    }

    if (scalar @badattrs > 0) {
        my $error = sprintf("The following %s attributes are not valid: %s.",
            $tablename, join(",", @badattrs));
        create_error_response($error);
        return 1;
    }
    return 0;
}

#----------------------------------------------------------------------------

=head3  check_attr_values_exist

        Check if a list of DB attribute values exist
        Arguments:
                table name
                table attribute
                table attribute desc (e.g. kit names), this string is added to error message
                list of values to check (ref)
        Returns:
                0 - OK
                1 - error
=cut

#-----------------------------------------------------------------------------
sub check_attr_values_exist {

    my $tablename       = shift;
    my $tableattr       = shift;
    my $tableattr_desc  = shift;
    my $values_to_check = shift;
    my @badvalues       = ();

    my $filter_stmt = db_build_filter_stmt({ $tableattr => $values_to_check });
    my $rows = db_get_table_rows($tablename, [$tableattr], $filter_stmt);

    my @values_in_DB = map { $_->{$tableattr} } @$rows;
    foreach my $value_to_check (@{$values_to_check}) {
        if (!grep { $_ eq $value_to_check } @values_in_DB) {
            push(@badvalues, $value_to_check);
        }
    }

    if (scalar @badvalues > 0) {
        my $error;
        if ($tableattr_desc =~ /s$/) {
            $error = sprintf("The following %s are not valid: %s.", $tableattr_desc, join(",", @badvalues));
        } else {
            $error = sprintf("The following %s is not valid: %s.", $tableattr_desc, join(",", @badvalues));
        }
        create_error_response($error);
        return 1;
    }

    return 0;
}



#----------------------------------------------------------------------------

=head3  lskit

        Support for listing kits
        Returns:
                0 - OK
                1 - help
                2 - error
=cut

#-----------------------------------------------------------------------------
sub lskit {

    my $request         = shift;
    my $callback        = shift;
    my $request_command = shift;

    my $rc = 0;

    # process the command line
    # 0=success, 1=version, 2=help, 3=error

    $rc = lskit_processargs(@_);
    if ($rc != 0) {
        if ($rc != 1) {
            lskit_usage(@_);
        }
        return ($rc - 1);
    }

    if ($^O ne 'linux') {
        my $rsp = {};
        push @{ $rsp->{data} }, "The lskit command is only supported on Linux.\n";
        xCAT::MsgUtils->message("E", $rsp, $::CALLBACK);
        return 1;
    }

    if (defined($::opt_F)) {
        if (defined($::opt_x) || defined($::opt_K) || defined($::opt_R) || defined($::opt_C)) {
            my $rsp = {};
            push @{ $rsp->{data} }, "\nThe \'-F\' option cannot be used with the x, R, K, or C options.\n";
            xCAT::MsgUtils->message("E", $rsp, $::CALLBACK);
            lskit_usage(1);
            return 1;
        }
        my $tarpathname = $::opt_F;

        # check if full path
        my $dir = $request->{cwd};    #getcwd;
        my $cwd = $dir->[0];

        if ($tarpathname !~ /^\//)
        {
            my $fullpath = xCAT::Utils->full_path($tarpathname, $cwd);
            if ($fullpath)
            {
                $tarpathname = $fullpath;
            }
        }

        unless (-e $tarpathname) {
            my $rsp = {};
            push @{ $rsp->{data} }, "Could not find $tarpathname.\n";
            xCAT::MsgUtils->message("E", $rsp, $::CALLBACK);
            return 1;
        }

        #  create a unique  /tmp dir name name
        my $thisdate     = `/bin/date +%s`;
        my $tmp_dir_name = qq~/tmp/tmp_kit_$thisdate~;
        $tmp_dir_name =~ s/\s*//g;    #remove blanks

        # extract the kit.conf file
        my $rsp = {};
        push @{ $rsp->{data} }, "\nExtracting the kit.conf file from $tarpathname. Please wait.\n";
        xCAT::MsgUtils->message("I", $rsp, $::CALLBACK);

        my $tcmd = qq~/bin/mkdir -p $tmp_dir_name;/bin/tar -C $tmp_dir_name -jxvf $tarpathname --wildcards */kit.conf~;
        my $kitconffile = xCAT::Utils->runcmd("$tcmd", -1);
        if ($::RUNCMD_RC != 0) {
            my $rsp = {};
            push @{ $rsp->{data} }, "No kit.conf file was found.\n";
            xCAT::MsgUtils->message("E", $rsp, $::CALLBACK);
            return 1;
        }

        # get the framework from the file
        my $fcmd = "/bin/cat $tmp_dir_name/$kitconffile | grep 'kitframework '";
        my $fline = xCAT::Utils->runcmd("$fcmd", -1);
        if ($::RUNCMD_RC != 0) {
            my $rsp = {};
            push @{ $rsp->{data} }, "No kitframework value was found.\n";
            xCAT::MsgUtils->message("E", $rsp, $::CALLBACK);
            return 1;
        }
        my ($junk, $kitframework) = split(/=/, $fline);
        $kitframework =~ s/\s*//g;    #remove blanks

        my $cfcmd = "/bin/cat $tmp_dir_name/$kitconffile | grep 'compatible_kitframeworks'";
        my $cfline = xCAT::Utils->runcmd("$cfcmd", -1);
        if ($::RUNCMD_RC != 0) {
            my $rsp = {};
            push @{ $rsp->{data} }, "No compatible_kitframeworks value was found.\n";
            xCAT::MsgUtils->message("E", $rsp, $::CALLBACK);
            return 1;
        }
        my ($junk, $compatframework) = split(/=/, $cfline);
        $compatframework =~ s/\s*//g;    #remove blanks

        my $rcmd = qq~/bin/rm -Rf $tmp_dir_name 2>/dev/null~;
        my $out = xCAT::Utils->runcmd("$rcmd", -1);
        if ($::RUNCMD_RC != 0) {
            my $rsp = {};
            push @{ $rsp->{data} }, "Could not remove $tmp_dir_name.\n";
            xCAT::MsgUtils->message("E", $rsp, $::CALLBACK);
        }

        if ($kitframework && $compatframework) {
            my $rsp = {};
            push @{ $rsp->{data} }, "\tkitframework=$kitframework\n\tcompatible_kitframeworks=$compatframework\n";
            xCAT::MsgUtils->message("I", $rsp, $::CALLBACK);
            return 0;
        } else {
            my $rsp = {};
            push @{ $rsp->{data} }, "Could not determine framework values for Kit $tarpathname.\n";
            xCAT::MsgUtils->message("E", $rsp, $::CALLBACK);
            return 1;
        }
        return 0;
    }

    # Prepare the hash tables to pass to the output routines
    my $kit_hash = get_kit_hash($::kitnames, $::kitattrs);
    my $kitrepo_hash = get_kitrepo_hash($::kitnames, $::kitrepoattrs);
    my $kitcomp_hash = get_kitcomp_hash($::kitnames, $::kitcompattrs);



    # Now display the output
    my @kitnames = keys(%$kit_hash);
    if (scalar @kitnames == 0) {
        my $rsp = {};
        push @{ $rsp->{data} }, "No kits were found.";
        xCAT::MsgUtils->message("I", $rsp, $::CALLBACK);
        return 0;
    }
    if (defined($::opt_K) || defined($::opt_R) || defined($::opt_C)) {

        if (!defined($::opt_x)) {
            if (defined($::opt_K)) {
                lskit_K($kit_hash);
            }

            # Option -R for kit repo attributes
            if (defined($::opt_R)) {
                my @kitrepos = keys(%$kitrepo_hash);
                if (scalar @kitrepos == 0) {
                    my $rsp = {};
                    push @{ $rsp->{data} }, "No kit repos were found.";
                    xCAT::MsgUtils->message("I", $rsp, $::CALLBACK);
                    return 0;
                }
                lskit_R($kit_hash, $kitrepo_hash);
            }

            if (defined($::opt_C)) {
                my @kitcomplist = keys(%$kitcomp_hash);
                if (scalar @kitcomplist == 0) {
                    my $rsp = {};
                    push @{ $rsp->{data} }, "No kit components were found.";
                    xCAT::MsgUtils->message("I", $rsp, $::CALLBACK);
                    return 0;
                }
                lskit_C($kit_hash, $kitcomp_hash);
            }
        } else
        {
            if (defined($::opt_K)) {
                create_lskit_K_xml_response($kit_hash);
            }
            if (defined($::opt_R)) {
                create_lskit_R_xml_response($kit_hash, $kitrepo_hash);
            }
            if (defined($::opt_C)) {
                create_lskit_C_xml_response($kit_hash, $kitcomp_hash);
            }
        }
    }
    else
    {
        if (defined($::opt_x)) {
            create_lskit_xml_response($kit_hash, $kitrepo_hash, $kitcomp_hash);
        } else {
            create_lskit_stanza_response($kit_hash, $kitrepo_hash, $kitcomp_hash);
        }
    }
    return 0;
}


#----------------------------------------------------------------------------

=head3  lskit_R

        Support for listing kit repo

        Arguments:
        Returns:
                0 - OK
                1 - help
                2 - error
=cut

#-----------------------------------------------------------------------------

sub lskit_R {

    my $kit_hash     = shift;
    my $kitrepo_hash = shift;
    my $rsp          = {};
    my $count        = 0;


    for my $kitname (sort(keys(%$kit_hash))) {

        my $output .= "\nkit : $kitname\n----------------------------------------------------\n";


        # Kit repository info
        if (defined($kitrepo_hash->{$kitname})) {
            for my $kitrepo (@{ $kitrepo_hash->{$kitname} }) {
                $output .= "kitrepo:\n";
                for my $kitrepo_attr (sort(keys(%$kitrepo))) {
                    $output .= sprintf("    %s=%s\n", $kitrepo_attr, $kitrepo->{$kitrepo_attr});
                }
                $output .= "\n";
            }
        }

        push @{ $rsp->{data} }, $output;
    }

    xCAT::MsgUtils->message("D", $rsp, $::CALLBACK);


}


#----------------------------------------------------------------------------

=head3  lskit_K

        Support for listing kit

        Arguments:
        Returns:
                0 - OK
                1 - help
                2 - error
=cut

#-----------------------------------------------------------------------------

sub lskit_K {

    my $kit_hash = shift;
    my $rsp      = {};
    my $count    = 0;


    for my $kitname (sort(keys(%$kit_hash))) {

        my $output .= "\nkit : $kitname\n----------------------------------------------------\n";

        # Kit info
        if (defined($kit_hash->{$kitname})) {
            my $kit = $kit_hash->{$kitname}->[0];
            $output .= "kit:\n";
            for my $kit_attr (sort(keys(%$kit))) {
                $output .= sprintf("    %s=%s\n", $kit_attr, $kit->{$kit_attr});
            }
            $output .= "\n";
        }


        push @{ $rsp->{data} }, $output;
    }

    xCAT::MsgUtils->message("D", $rsp, $::CALLBACK);


}

#----------------------------------------------------------------------------

=head3  lskit_C

        Support for listing kitcomponent

        Arguments:
        Returns:
                0 - OK
                1 - help
                2 - error
=cut

#-----------------------------------------------------------------------------

sub lskit_C {

    my $kit_hash     = shift;
    my $kitcomp_hash = shift;
    my $rsp          = {};
    my $count        = 0;


    for my $kitname (sort(keys(%$kit_hash))) {

        my $output .= "\nkit : $kitname\n----------------------------------------------------\n";


        # Kit component info
        if (defined($kitcomp_hash->{$kitname})) {
            for my $kitcomp (@{ $kitcomp_hash->{$kitname} }) {
                $output .= "kitcomponent:\n";
                for my $kitcomp_attr (sort(keys(%$kitcomp))) {
                    $output .= sprintf("    %s=%s\n", $kitcomp_attr, $kitcomp->{$kitcomp_attr});
                }
                $output .= "\n";
            }
        }


        push @{ $rsp->{data} }, $output;
    }

    xCAT::MsgUtils->message("D", $rsp, $::CALLBACK);


}


#----------------------------------------------------------------------------

=head3  lskitcomp

        Support for listing kit components

        Arguments:
        Returns:
                0 - OK
                1 - help
                2 - error
=cut

#-----------------------------------------------------------------------------


sub lskitcomp {

    my $rc = 0;

    # process the command line
    # 0=success, 1=version, 2=help, 3=error

    if ($^O ne 'linux') {
        my $rsp = {};
        push @{ $rsp->{data} }, "The lskitcomp command is only supported on Linux.\n";
        xCAT::MsgUtils->message("E", $rsp, $::CALLBACK);
        return 1;
    }

    $rc = lskitcomp_processargs(@_);
    if ($rc != 0) {
        if ($rc != 1) {
            lskitcomp_usage(@_);
        }
        return ($rc - 1);
    }

    # Get the list of kitcomponents to display

    ## Build the initial kitcomponent list, filtering the kitcomponents whose:
    ##    - name matches one of the user-specified names
    ##    - AND, reponame refers to a repo that is compatible with the user-specified osdistro

    my $compat_kitreponames = undef;
    if (defined($::osdistroname)) {
        $compat_kitreponames = get_compat_kitreponames($::osdistroname);
    }
    my $kitcomps = get_kitcomp_list($::kitcompnames, $compat_kitreponames, $::kitcompattrs);


    ## Filter the kitcomponent list by user-specified server role
    if (defined($::serverrole)) {
        my @tmplist = ();
        foreach my $kitcomp (@$kitcomps) {
            if (defined($kitcomp->{serverroles})) {
                my @serverroles = split(/,/, $kitcomp->{serverroles});
                if (grep { $_ eq $::serverrole } @serverroles) {
                    push(@tmplist, $kitcomp);
                }
            } else {

                # If kit component doesn't have server roles, it means
                # it supports any server role.
                push(@tmplist, $kitcomp);
            }
        }
        @$kitcomps = @tmplist;
    }


    # Check if kit component list is empty

    if (scalar(@$kitcomps) == 0) {
        my $rsp = {};
        push @{ $rsp->{data} }, "No kit components were found.";
        xCAT::MsgUtils->message("I", $rsp, $::CALLBACK);
        return 0;
    }


    # Prepare the hash tables to pass to the output routines

    ## Kit hash table
    my @kitnames = map { $_->{kitname} } @$kitcomps;
    my $kit_hash = get_kit_hash(\@kitnames, ['kitname']);
    ## Kit component hash table
    my $kitcomp_hash = create_hash_from_table_rows($kitcomps, 'kitname');


    ## Now display the output
    if (defined($::opt_C)) {
        if (defined($::opt_x)) {
            create_lskitcomp_C_xml_response($kit_hash, $kitcomp_hash);
        }
        else
        {
            if (defined($::opt_C)) {
                my @kitcomplist = keys(%$kitcomp_hash);
                if (scalar @kitcomplist == 0) {
                    my $rsp = {};
                    push @{ $rsp->{data} }, "No kit components were found.";
                    xCAT::MsgUtils->message("I", $rsp, $::CALLBACK);
                    return 0;
                }
                lskitcomp_C($kit_hash, $kitcomp_hash);
            }
        }
    }
    else
    {

        if (defined($::opt_x)) {
            create_lskit_xml_response($kit_hash, {}, $kitcomp_hash);
        }
        else
        {
            create_lskit_stanza_response($kit_hash, {}, $kitcomp_hash);
        }
    }
    return 0;
}

#----------------------------------------------------------------------------

=head3  lskitcomp_C

        Support for listing kitcomponent

        Arguments:
        Returns:
                0 - OK
                1 - help
                2 - error
=cut

#-----------------------------------------------------------------------------

sub lskitcomp_C {

    my $kit_hash     = shift;
    my $kitcomp_hash = shift;
    my $rsp          = {};
    my $count        = 0;


    for my $kitname (sort(keys(%$kit_hash))) {

        my $output .= "\n----------------------------------------------------\n";
        $output .= "\nkit : $kitname:\n\n";


        # Kit component info
        if (defined($kitcomp_hash->{$kitname})) {

            for my $kitcomp (@{ $kitcomp_hash->{$kitname} }) {
                $output .= "kitcomponent:\n";
                for my $kitcomp_attr (sort(keys(%$kitcomp))) {
                    $output .= sprintf("    %s=%s\n", $kitcomp_attr, $kitcomp->{$kitcomp_attr});
                }
                $output .= "\n";
            }
        }


        push @{ $rsp->{data} }, $output;
    }

    xCAT::MsgUtils->message("D", $rsp, $::CALLBACK);


}


#----------------------------------------------------------------------------

=head3  lskitdeployparam

        Support for listing kit deployment parameters

        Arguments:
        Returns:
                0 - OK
                1 - help
                2 - error
=cut

#-----------------------------------------------------------------------------


sub lskitdeployparam {

    my $rc = 0;

    # process the command line
    # 0=success, 1=version, 2=help, 3=error

    if ($^O ne 'linux') {
        my $rsp = {};
        push @{ $rsp->{data} }, "The lskitdeployparam command is only supported on Linux.\n";
        xCAT::MsgUtils->message("E", $rsp, $::CALLBACK);
        return 1;
    }

    $rc = lskitdeployparam_processargs(@_);
    if ($rc != 0) {
        if ($rc != 1) {
            lskitdeployparam_usage(@_);
        }
        return ($rc - 1);
    }

    # Get the kit deployment parameter files to read

    ## First get the list of kits
    my $kitnames = undef;
    if (defined($::kitnames)) {
        $kitnames = $::kitnames;
    } elsif (defined $::kitcompnames) {
        my $kitcomps = get_kitcomp_list($::kitcompnames, undef, [ 'kitname', 'kitcompname' ]);
        my @tmp = map { $_->{kitname} } @$kitcomps;
        $kitnames = \@tmp;
    } else {

        # unreachable code
    }

    ## Then get the kit deployment parameter file for each kit
    my $kits = get_kit_list($kitnames, [ 'kitname', 'kitdir', 'kitdeployparams' ]);

    # Read the kit deployment parameter files. The format is:
    #        #ENV:KIT_KIT1_PARAM1=value11#
    #        #ENV:KIT_KIT1_PARAM2=value12#
    #        #ENV:KIT_KIT2_PARAM1=value21#
    my $deployparam_hash = {};
    foreach my $kit (@$kits) {
        my $deployparam_file = $kit->{kitdir} . "/other_files/" . $kit->{kitdeployparams};

        my $tmpkitdeployparames = $kit->{kitdeployparams};

        if (defined($deployparam_file)) {

            # Check if there is kitdeployparam file or not
            if (defined($tmpkitdeployparames))
            {
                open(my $fh, "<", $deployparam_file) || die sprintf("Failed to open file %s because: %s", $deployparam_file, $!);

                while (<$fh>) {
                    chomp $_;
                    if ($_ =~ /^#ENV:.+=.+#$/) {
                        my $tmp = $_;
                        $tmp =~ s/^#ENV://;
                        $tmp =~ s/#$//;
                        (my $name, my $value) = split(/=/, $tmp);
                        $deployparam_hash->{$name} = $value;
                    }
                }
                close($fh);
            }
            else {
                my $rsp = {};
                push @{ $rsp->{data} }, "There is no kitdeployparams file in $deployparam_file.\n";
                xCAT::MsgUtils->message("W", $rsp, $::CALLBACK);

                return 1;
            }

        }
    }

    # Now display the output

    my $rsp = {};

    if (defined($::opt_x)) {

        # Output XML format
        foreach my $deployparam_key (sort(keys(%$deployparam_hash))) {
            my $output_hash = { "kitdeployparam" => { "name" => "", "value" => "" } };
            $output_hash->{kitdeployparam}->{name} = $deployparam_key;
            $output_hash->{kitdeployparam}->{value} = $deployparam_hash->{$deployparam_key};
            push @{ $rsp->{data} }, $output_hash;
        }
    } else {

        # Output Stanza format
        foreach my $deployparam_key (sort(keys(%$deployparam_hash))) {
            my $output = "kitdeployparam:\n";
            $output .= sprintf("    name=%s\n", $deployparam_key);
            $output .= sprintf("    value=%s\n", $deployparam_hash->{$deployparam_key});
            push @{ $rsp->{data} }, $output;
        }
    }

    xCAT::MsgUtils->message("D", $rsp, $::CALLBACK);

    return 0
}


1;

#----------------------------------------------------------------------------

=head3  get_kit_hash

        Returns a hash table containing kit entries indexed by kit name.
        Arguments:
            list of kit names (ref)
            list of kit attribute names (ref)
        Returns: hash table (ref)
           { kitname1 => [{kitname1,basename1,...}],
             kitname2 => [{kitname2,basename2,...}],
             ...
           }
=cut

#-----------------------------------------------------------------------------

sub get_kit_hash {

    my $tablename = 'kit';
    my $kitnames  = shift;
    my $kitattrs  = shift;

    my $filter_hash = {};
    if (defined($kitnames)) {
        $filter_hash->{kitname} = $kitnames;
    }

    my $filter_stmt = undef;
    if (scalar(keys(%$filter_hash)) > 0) {
        $filter_stmt = db_build_filter_stmt($filter_hash);
    }

    my $rows = db_get_table_rows($tablename, $kitattrs, $filter_stmt);

    return create_hash_from_table_rows($rows, 'kitname');
}

#----------------------------------------------------------------------------

=head3  get_kitrepo_hash

        Returns a hash table containing lists of kit repository entries
        indexed by kit name.
        Arguments:
            list of kit names (ref)
            list of kit repo attribute names (ref)
        Returns: hash table (ref)
           { kitname1 => [{kitrepo11,...},{kitrepo12,...}],
             kitname2 => [{kitrepo21,...},{kitrepo22,...}],
             ...
           }
=cut

#-----------------------------------------------------------------------------

sub get_kitrepo_hash {

    my $tablename    = 'kitrepo';
    my $kitnames     = shift;
    my $kitrepoattrs = shift;

    my $filter_hash = {};
    if (defined($kitnames)) {
        $filter_hash->{kitname} = $kitnames;
    }

    my $filter_stmt = undef;
    if (scalar(keys(%$filter_hash)) > 0) {
        $filter_stmt = db_build_filter_stmt($filter_hash);
    }
    my $rows = db_get_table_rows($tablename, $kitrepoattrs, $filter_stmt);

    return create_hash_from_table_rows($rows, 'kitname');
}


#----------------------------------------------------------------------------

=head3  get_kitcomp_hash

        Returns a hash table containing lists of kit component entries
        indexed by kit name.
        Arguments:
            list of kit names (ref)
            list of kit component attribute names (ref)
        Returns: hash table (ref)
           { kitname1 => [{kitcomp11,...},{kitcomp12,...}],
             kitname2 => [{kitcomp21,...},{kitcomp22,...}],
             ...
           }
=cut

#-----------------------------------------------------------------------------

sub get_kitcomp_hash {

    my $tablename    = 'kitcomponent';
    my $kitnames     = shift;
    my $kitcompattrs = shift;

    my $filter_hash = {};
    if (defined($kitnames)) {
        $filter_hash->{kitname} = $kitnames;
    }

    my $filter_stmt = undef;
    if (scalar(keys(%$filter_hash)) > 0) {
        $filter_stmt = db_build_filter_stmt($filter_hash);
    }
    my $rows = db_get_table_rows($tablename, $kitcompattrs, $filter_stmt);

    return create_hash_from_table_rows($rows, 'kitname');

}


#----------------------------------------------------------------------------

=head3  get_kit_list

        Returns a list of kit, filtering the kits by:
          - kit name

        Arguments:
               list of kit attributes to query (ref)
               list of kit names for filtering  (ref)

        Returns a list of kit(ref)
=cut

#-----------------------------------------------------------------------------

sub get_kit_list {

    my $kitnames = shift;
    my $kitattrs = shift;

    my $filter_hash = {};

    if (defined($kitnames)) {
        $filter_hash->{kitname} = $kitnames;
    }

    my $filter_stmt = undef;
    if (scalar(keys(%$filter_hash)) > 0) {
        $filter_stmt = db_build_filter_stmt($filter_hash);
    }
    return db_get_table_rows('kit', $kitattrs, $filter_stmt);
}


#----------------------------------------------------------------------------

=head3  get_kitcomp_list

        Returns a list of kit components, filtering the kit components by:
          - kit component name
          - kit repository name

        Arguments:
               list of kit component names for filtering  (ref)
               list of kit repo names for filtering (ref)
               list of kit component attributes to query (ref)

        Returns a list of kit components (ref)
=cut

#-----------------------------------------------------------------------------

sub get_kitcomp_list {

    my $kitcompnames = shift;
    my $kitreponames = shift;
    my $kitcompattrs = shift;

    my $filter_hash = {};

    if (defined($kitcompnames)) {
        $filter_hash->{kitcompname} = $kitcompnames;
    }

    if (defined($kitreponames)) {
        $filter_hash->{kitreponame} = $kitreponames;
    }

    my $filter_stmt = undef;
    if (scalar(keys(%$filter_hash)) > 0) {
        $filter_stmt = db_build_filter_stmt($filter_hash);
    }
    return db_get_table_rows('kitcomponent', $kitcompattrs, $filter_stmt);
}


#----------------------------------------------------------------------------

=head3  get_compat_kitreponames

        Returns a list of kitreponames which are compatible with the specified
        osdistroname.

        Arguments:
             osdistroname

        Returns:
             List of kitreponames (ref)

=cut

#-----------------------------------------------------------------------------

sub get_compat_kitreponames {

    my $osdistroname    = shift;
    my @compat_kitrepos = ();

    ## Get the osdistro info
    my $filter_stmt = db_build_filter_stmt({ 'osdistroname' => [$osdistroname] });
    my $osdistros = db_get_table_rows('osdistro', undef, $filter_stmt);
    my $osdistro = $osdistros->[0];

    #print Dumper($osdistro);

    ## Get the kitrepos, which are compatible with the osdistro info
    my $kitrepos = db_get_table_rows('kitrepo', undef, undef);

    foreach my $kitrepo (@$kitrepos) {
        ## To check if kitrepo is compatible with an osdistro, the following 4 things
        ## must be true:
        ##     1) The kitrepo basename must be same as or compatible with osdistro basename.
        ##     2) The kitrepo major verison must be same as osdistro major version.
        ##     3) The kitrepo minor version must either:
        ##           - Be same as osdistro minor version
        ##           - OR, be empty which matches any osdistro minor version
        ##     4) The kitrepo arch must be same as osdistro arch.
        if (defined($kitrepo->{osbasename})) {
            my @kitrepo_compat_basenames = ();
            if (defined($kitrepo->{compat_osbasenames})) {
                @kitrepo_compat_basenames = split(/,/, $kitrepo->{compat_osbasenames});
            }
            if ($kitrepo->{osbasename} ne $osdistro->{basename} &&
                !grep { $_ eq $osdistro->{basename} } @kitrepo_compat_basenames) {
                next;
            }
        }
        if (defined($kitrepo->{osmajorversion}) && $kitrepo->{osmajorversion} ne $osdistro->{majorversion}) {
            next;
        }
        if (defined($kitrepo->{osminorversion})) {
            if (check_minorversion($kitrepo->{osminorversion},$osdistro->{minorversion}))
            {
                next;
            }
        }
        if (defined($kitrepo->{osarch}) && $kitrepo->{osarch} ne $osdistro->{arch} && $kitrepo->{osarch} ne 'noarch') {
            next;
        }

        push(@compat_kitrepos, $kitrepo);
    }

    #print Dumper(@compat_kitrepos);

    my @compat_kitreponames = map { $_->{kitreponame} } @compat_kitrepos;
    return \@compat_kitreponames;

}

#----------------------------------------------------------------------------

=head3  db_build_filter_stmt

        Returns a SQL 'where' statement which is used to filter the
        result of a kit, kit repository or kit component query.

        Arguments:
             hash table
                 - each entry represents a filter
                 - each entry has a key <keyN>, and a list of values <valuesN>.
                 - each entry is added to 'where' stmt as follows:
                      <key1> in (comma-separated list of <values1>)
                      <key2> in (comma-separated list of <values2>)
                      ...
        Returns: string containing SQL 'where' statement
=cut

#-----------------------------------------------------------------------------

sub db_build_filter_stmt {

    my $filter_hash = shift;
    my $filter_stmt = "";

    for my $filter_key (keys(%$filter_hash)) {
        my $filter_values = $filter_hash->{$filter_key};
        my $values_str = join ",", map { '\'' . $_ . '\'' } @$filter_values;
        if ($filter_stmt eq "") {
            $filter_stmt = sprintf("%s in (%s)", $filter_key, $values_str);
        } else {
            $filter_stmt .= sprintf(" AND %s in (%s)", $filter_key, $values_str);
        }
    }

    return $filter_stmt;
}


#----------------------------------------------------------------------------

=head3  db_get_table_rows

        Returns a list of table rows.  Each table row is a hash table.

        Arguments:
               table name
               attribute list
               where statement

        Returns: list of table rows (ref)
=cut

#-----------------------------------------------------------------------------

sub db_get_table_rows {

    my $tablename   = shift;
    my $attrs       = shift;
    my $filter_stmt = shift;

    if (!defined($attrs)) {
        @{$attrs} = ();
        my $schema = xCAT::Table->getTableSchema($tablename);
        foreach my $c (@{ $schema->{cols} }) {
            push @{$attrs}, $c;
        }
    }

    my $table      = xCAT::Table->new($tablename);
    my @table_rows = ();

    # todo fix for DB2 support
    if (defined($filter_stmt)) {
        if (length($filter_stmt) > 0) {
            @table_rows = $table->getAllAttribsWhere($filter_stmt, @{$attrs});
        }
    } else {
        @table_rows = $table->getAllAttribs(@{$attrs});
    }

    return \@table_rows;
}

#----------------------------------------------------------------------------

=head3  create_hash_from_table_rows

        Returns a hash table containing table rows indexed by specified
        table attribute.
        Arguments:
               list of table rows (each row is a hash table)
               table attribute
        Returns: hash table (ref)
            { kitname1 => [row11,row12,...}],
              kitname2 => [row21,row22,...}],
              ...
            }
=cut

#-----------------------------------------------------------------------------

sub create_hash_from_table_rows {

    my $table_rows = shift;
    my $table_attr = shift;

    my $result = {};
    foreach my $row (@$table_rows) {
        my $hash_key = $row->{$table_attr};
        if (!defined($result->{$hash_key})) {
            $result->{$hash_key} = [];
        }
        push(@{ $result->{$hash_key} }, $row);
    }

    return $result;

}

#----------------------------------------------------------------------------

=head3  create_lskit_xml_response

        Prepare a response that returns the kit, kit repository, and
        kit component info in XML format.

        Arguments:
               kit hash table
               kit repo hash table
               kit component hash table

               Note: Hash tables are created by create_hash_from_table_rows()
=cut

#-----------------------------------------------------------------------------

sub create_lskit_xml_response {

    my $kit_hash     = shift;
    my $kitrepo_hash = shift;
    my $kitcomp_hash = shift;

    my $rsp = {};

    for my $kitname (sort(keys(%$kit_hash))) {
        my $output_hash = { "kitinfo" => { "kit" => [], "kitrepo" => [], "kitcomponent" => [] } };

        # Kit info
        if (defined($kit_hash->{$kitname})) {
            my $kit = $kit_hash->{$kitname}->[0];
            push(@{ $output_hash->{kitinfo}->{kit} }, $kit);
        }

        # Kit repository info
        if (defined($kitrepo_hash->{$kitname})) {
            for my $kitrepo (@{ $kitrepo_hash->{$kitname} }) {
                push(@{ $output_hash->{kitinfo}->{kitrepo} }, $kitrepo);
            }
        }

        # Kit component info
        if (defined($kitcomp_hash->{$kitname})) {
            for my $kitcomp (@{ $kitcomp_hash->{$kitname} }) {
                push(@{ $output_hash->{kitinfo}->{kitcomp} }, $kitcomp);
            }
        }

        push @{ $rsp->{data} }, $output_hash;
    }

    xCAT::MsgUtils->message("D", $rsp, $::CALLBACK);
}


#----------------------------------------------------------------------------

=head3  create_lskit_K_xml_response

        Prepare a response that returns the kit info in XML format.

        Arguments:
               kit hash table

               Note: Hash tables are created by create_hash_from_table_rows()
=cut

#-----------------------------------------------------------------------------

sub create_lskit_K_xml_response {

    my $kit_hash = shift;

    my $rsp = {};

    for my $kitname (sort(keys(%$kit_hash))) {
        my $output_hash = { "kitinfo" => { "kit" => [], "kitrepo" => [], "kitcomponent" => [] } };

        # Kit info
        if (defined($kit_hash->{$kitname})) {
            my $kit = $kit_hash->{$kitname}->[0];
            push(@{ $output_hash->{kitinfo}->{kit} }, $kit);
        }

        push @{ $rsp->{data} }, $output_hash;
    }

    xCAT::MsgUtils->message("D", $rsp, $::CALLBACK);
}

#----------------------------------------------------------------------------

=head3  create_lskit_R_xml_response

        Prepare a response that returns the kit repository
        info in XML format.

        Arguments:
               kit repo hash table

               Note: Hash tables are created by create_hash_from_table_rows()
=cut

#-----------------------------------------------------------------------------

sub create_lskit_R_xml_response {

    my $kit_hash     = shift;
    my $kitrepo_hash = shift;

    my $rsp = {};

    for my $kitname (sort(keys(%$kit_hash))) {
        my $output_hash = { "kitinfo" => { "kit" => [], "kitrepo" => [], "kitcomponent" => [] } };

        # Kit repository info
        if (defined($kitrepo_hash->{$kitname})) {
            for my $kitrepo (@{ $kitrepo_hash->{$kitname} }) {
                push(@{ $output_hash->{kitinfo}->{kitrepo} }, $kitrepo);
            }
        }

        push @{ $rsp->{data} }, $output_hash;
    }

    xCAT::MsgUtils->message("D", $rsp, $::CALLBACK);
}


#----------------------------------------------------------------------------

=head3  create_lskit_C_xml_response

        Prepare a response that returns the
        kit component info in XML format.

        Arguments:
               kit hash table
               kit component hash table

               Note: Hash tables are created by create_hash_from_table_rows()
=cut

#-----------------------------------------------------------------------------

sub create_lskit_C_xml_response {

    my $kit_hash     = shift;
    my $kitcomp_hash = shift;

    my $rsp = {};

    for my $kitname (sort(keys(%$kit_hash))) {
        my $output_hash = { "kitinfo" => { "kit" => [], "kitrepo" => [], "kitcomponent" => [] } };


        # Kit component info
        if (defined($kitcomp_hash->{$kitname})) {
            for my $kitcomp (@{ $kitcomp_hash->{$kitname} }) {
                push(@{ $output_hash->{kitinfo}->{kitcomp} }, $kitcomp);
            }
        }

        push @{ $rsp->{data} }, $output_hash;
    }

    xCAT::MsgUtils->message("D", $rsp, $::CALLBACK);
}

#----------------------------------------------------------------------------

=head3  create_lskitcomp_C_xml_response

        Prepare a response that returns the
        kit component info in XML format.

        Arguments:
               kit hash table
               kit component hash table

               Note: Hash tables are created by create_hash_from_table_rows()
=cut

#-----------------------------------------------------------------------------

sub create_lskitcomp_C_xml_response {

    my $kit_hash     = shift;
    my $kitcomp_hash = shift;

    my $rsp = {};

    for my $kitname (sort(keys(%$kit_hash))) {
        my $output_hash = { "kitinfo" => { "kit" => [], "kitrepo" => [], "kitcomponent" => [] } };


        # Kit component info
        if (defined($kitcomp_hash->{$kitname})) {
            for my $kitcomp (@{ $kitcomp_hash->{$kitname} }) {
                push(@{ $output_hash->{kitinfo}->{kitcomp} }, $kitcomp);
            }
        }

        push @{ $rsp->{data} }, $output_hash;
    }

    xCAT::MsgUtils->message("D", $rsp, $::CALLBACK);
}

#----------------------------------------------------------------------------

=head3  create_lskit_stanza_response

        Prepare a response that returns the kit, kit repository, and
        kit component info in stanza-like format.

        Arguments:
               kit hash table
               kit repo hash table
               kit component hash table
               Note: Hash tables are created by create_hash_from_table_rows()
=cut

#-----------------------------------------------------------------------------
sub create_lskit_stanza_response {

    my $kit_hash     = shift;
    my $kitrepo_hash = shift;
    my $kitcomp_hash = shift;

    my $rsp   = {};
    my $count = 0;

    for my $kitname (sort(keys(%$kit_hash))) {
        my $output .= "\n----------------------------------------------------\n";

        # Kit info
        if (defined($kit_hash->{$kitname})) {
            my $kit = $kit_hash->{$kitname}->[0];
            $output .= "kit:\n";
            for my $kit_attr (sort(keys(%$kit))) {
                $output .= sprintf("    %s=%s\n", $kit_attr, $kit->{$kit_attr});
            }
            $output .= "\n";
        }

        # Kit repository info
        if (defined($kitrepo_hash->{$kitname})) {
            for my $kitrepo (@{ $kitrepo_hash->{$kitname} }) {
                $output .= "kitrepo:\n";
                for my $kitrepo_attr (sort(keys(%$kitrepo))) {
                    $output .= sprintf("    %s=%s\n", $kitrepo_attr, $kitrepo->{$kitrepo_attr});
                }
                $output .= "\n";
            }
        }

        # Kit component info
        if (defined($kitcomp_hash->{$kitname})) {
            for my $kitcomp (@{ $kitcomp_hash->{$kitname} }) {
                $output .= "kitcomponent:\n";
                for my $kitcomp_attr (sort(keys(%$kitcomp))) {
                    $output .= sprintf("    %s=%s\n", $kitcomp_attr, $kitcomp->{$kitcomp_attr});
                }
                $output .= "\n";
            }
        }


        push @{ $rsp->{data} }, $output;
    }

    xCAT::MsgUtils->message("D", $rsp, $::CALLBACK);

}

1;



#-----------------------------------------------------------------------------

=head3    check_framework

    Check the compatible frameworks of the kit to see if it is
        compatible with the running code.

    If one of the compatible frameworks of the kit matches one of the
        compatible frameworks of the running code then we're good.

    NOTE:  compatible_kitframeworks are the kitframeworks that I can add
        and kit frameworks that I can be added to.

    Returns:
        0 - OK
        1 - error

    Example:
        my $rc = &check_framework(\@lines);

=cut

#-----------------------------------------------------------------------------
sub check_framework
{
    my $lines = shift;

    my @kitconflines = @$lines;

    my $kitbasename;
    my $kitcompat;
    my $section = '';
    foreach my $l (@kitconflines) {

        # skip blank and comment lines
        if ($l =~ /^\s*$/ || $l =~ /^\s*#/) {
            next;
        }

        if ($l =~ /^\s*(\w+)\s*:/) {
            $section = $1;
            next;
        }

        if ($l =~ /^\s*(\w+)\s*=\s*(.*)\s*/) {
            my $attr = $1;
            my $val  = $2;
            $attr =~ s/^\s*//;       # Remove any leading whitespace
            $attr =~ s/\s*$//;       # Remove any trailing whitespace
            $attr =~ tr/A-Z/a-z/;    # Convert to lowercase
            $val =~ s/^\s*//;
            $val =~ s/\s*$//;

            if ($section eq 'kitbuildinfo') {
                if ($attr eq 'compatible_kitframeworks') {
                    $kitcompat = $val;
                }
            }
            if ($section eq 'kit') {
                if ($attr eq 'basename') { $kitbasename = $val; }
            }
        }
    }

    if (!$kitcompat) {
        my %rsp;
        push @{ $rsp{data} }, "Could not determine the kit compatible framework values for \'$kitbasename\' from the kit.conf file. Continuing for now.";
        xCAT::MsgUtils->message("I", \%rsp, $::CALLBACK);
        return 0;
    }

    my @kit_compat_list = split(',', $kitcompat);
    my @my_compat_list  = split(',', $::COMPATIBLE_KITFRAMEWORKS);

    foreach my $myfw (@my_compat_list) {
        chomp $myfw;
        foreach my $kitfw (@kit_compat_list) {
            chomp $kitfw;

            if ($myfw eq $kitfw) {
                return 0;
            }
        }
    }

    my %rsp;
    push @{ $rsp{data} }, "The kit named \'$kitbasename\' is not compatible with this version of the buildkit command.  \'$kitbasename\' is compatible with \'$kitcompat\' and the buildkit command is compatible with \'$::COMPATIBLE_KITFRAMEWORKS\'";
    xCAT::MsgUtils->message("E", \%rsp, $::CALLBACK);
    return 1;
}
