<commit_before># ${license-info}
# ${developer-info
# ${author-info}
# ${build-info}

package NCM::Component::pxelinux;

use strict;
use warnings;
use NCM::Component;
use EDG::WP4::CCM::Property;
use NCM::Check;
use Sys::Hostname;
use CAF::FileWriter;
use NCM::Component::ks qw (ksuserhooks);
use LC::Fatal qw (symlink);
use File::stat;
use Time::localtime;
use Readonly;
use version;

use constant PXEROOT => "/system/aii/nbp/pxelinux";
use constant NBPDIR => 'nbpdir';
use constant LOCALBOOT => 'bootconfig';
use constant HOSTNAME => "/system/network/hostname";
use constant DOMAINNAME => "/system/network/domainname";
use constant ETH => "/system/network/interfaces";
use constant INSTALL => 'install';
use constant BOOT => 'boot';
use constant RESCUE => 'rescue';
use constant RESCUEBOOT => 'rescueconfig';
use constant FIRMWARE => 'firmware';
use constant LIVECD => 'livecd';
# Hooks for NBP plug-in
use constant RESCUE_HOOK_PATH => '/system/aii/hooks/rescue';
use constant INSTALL_HOOK_PATH => '/system/aii/hooks/install';
use constant REMOVE_HOOK_PATH => '/system/aii/hooks/remove';
use constant BOOT_HOOK_PATH => '/system/aii/hooks/boot';
use constant FIRMWARE_HOOK_PATH => '/system/aii/hooks/firmware';
use constant LIVECD_HOOK_PATH => '/system/aii/hooks/livecd';
# Kickstart constants (trying to use same name as in ks.pm from aii-ks)
use constant KS => "/system/aii/osinstall/ks";

# Lowest supported version is EL 5.0
use constant ANACONDA_VERSION_EL_5_0 => version->new("11.1");
use constant ANACONDA_VERSION_EL_7_0 => version->new("19.31"); 
use constant ANACONDA_VERSION_LOWEST => ANACONDA_VERSION_EL_5_0;

our @ISA = qw (NCM::Component);
our $EC = LC::Exception::Context->new->will_store_all;
our $this_app = $main::this_app;

# Return the fqdn of the node
sub get_fqdn 
{
    my $cfg = shift;
    my $h = $cfg->getElement (HOSTNAME)->getValue;
    my $d = $cfg->getElement (DOMAINNAME)->getValue;
    return "$h.$d";    
}

# return the anaconda version instance as specified in the kickstart (if at all)
sub get_anaconda_version
{
    my $kst = shift;
    my $version = ANACONDA_VERSION_LOWEST;
    if ($kst->{version}) {
        $version = version->new($kst->{version});
        if ($version < ANACONDA_VERSION_LOWEST) {
            # TODO is this ok, or should we stop?
            $this_app->error("Version $version < lowest supported ".ANACONDA_VERSION_LOWEST.", continuing with lowest");
            $version = ANACONDA_VERSION_LOWEST;
        }        
    };
    return $version;    
}

# Returns the absolute path where the PXE file must be written.
sub filepath
{
    my $cfg = shift;

    my $fqdn = get_fqdn($cfg);
    my $dir = $this_app->option (NBPDIR);
    $this_app->debug(3, "NBP directory = $dir");
    return "$dir/$fqdn.cfg";
}

# Returns the absolute path of the PXE file to link to
sub link_filepath
{
    my ($cfg, $cmd) = @_;

    my $dir = $this_app->option (NBPDIR);

    my $cfgpath = PXEROOT . "/" . $cmd;
    if ($cfg->elementExists ($cfgpath)) {
        my $linkname = $cfg->getElement ($cfgpath)->getValue;
        return "$dir/$linkname";
    } elsif ($cmd eq RESCUE) {
        # Backwards compatibility: use the option set on the command line
        # if the profile does not define a rescue image
        my $path = $this_app->option (RESCUEBOOT);
        unless ($path =~ m{^([-.\w]+)$}) {
            $this_app->error ("Unexpected RESCUE configuration file");
        }
        return "$dir/$1";
    } else {
        my $fqdn = get_fqdn($cfg);
        $this_app->debug(3, "No $cmd defined for $fqdn");
    }
    return undef;
}


# Configure the ksdevice with static IP 
# (EL7+ only)
sub pxe_ks_static_network
{
    my ($config, $t) = @_;

    my $fqdn = get_fqdn($config);
    my $dev = $t->{ksdevice};

    my $net = $config->getElement("/system/network/interfaces/$dev")->getTree;
    unless ($net->{ip}) {
            $this_app->error ("Static boot protocol specified ",
                              "but no IP given to the interface $dev");
            return;
    }
    
    # can't set MTU with static ip via PXE

    my $gw;
    if ($net->{gateway}) {
        $gw = $net->{gateway};
    } elsif ($config->elementExists ("/system/network/default_gateway")) {
        $gw = $config->getElement ("/system/network/default_gateway")->getValue;
    } else {
        # This is a recipe for disaster
        # No best guess here
        $this_app->error ("No gateway defined for dev $dev and ",
                          " using static network description.");
        return;                
    };

    return "$net->{ip}::$gw:$net->{netmask}:$fqdn:$dev:none";
}


# create a list with all append options for kickstart installations
sub pxe_ks_append 
{
    my $cfg = shift;

    my $t = $cfg->getElement (PXEROOT)->getTree;
    
    my $kst = {}; # empty hashref in case no kickstart is defined
    $kst = $cfg->getElement (KS)->getTree if $cfg->elementExists(KS);

    my $version = get_anaconda_version($kst);

    my $keyprefix = "";
    my $ksdevicename = "ksdevice";  
    if($version >= ANACONDA_VERSION_EL_7_0) {
        $keyprefix="inst.";

        if($t->{ksdevice} =~ m/^(bootif|link)$/ &&
            ! $cfg->hasElement("/system/network/interfaces/$t->{ksdevice}")) {
            $this_app->debug("Using depreacted legacy behaviour. Please look into the configuration.");
        } else {
            $ksdevicename = "bootdev";  
        }
    }  

    my $ksloc = $t->{kslocation};
    my $server = hostname();
    $ksloc =~ s{LOCALHOST}{$server};

    my @append;
    push(@append,
         "ramdisk=32768",
         "initrd=$t->{initrd}",
         "${keyprefix}ks=$ksloc",
         "$ksdevicename=$t->{ksdevice}"
         );         

    if ($t->{updates}) {
        push(@append,"${keyprefix}updates=$t->{updates}");
    };

    if ($kst->{logging}) {
        push(@append, "${keyprefix}syslog=$kst->{logging}->{host}:$kst->{logging}->{port}"); 
        push(@append, "${keyprefix}loglevel=$kst->{logging}->{level}") if $kst->{logging}->{level};
    }
    
    if ($version >= ANACONDA_VERSION_EL_7_0) {
        if ($kst->{enable_sshd}) {
            push(@append, "${keyprefix}sshd");
        };
        
        if ($kst->{cmdline}) {
            push(@append, "${keyprefix}cmdline");
        };<commit_msg>ks logging host is optional now, if it is missing, it means no anaconda syslog config<commit_after># ${license-info}
# ${developer-info
# ${author-info}
# ${build-info}

package NCM::Component::pxelinux;

use strict;
use warnings;
use NCM::Component;
use EDG::WP4::CCM::Property;
use NCM::Check;
use Sys::Hostname;
use CAF::FileWriter;
use NCM::Component::ks qw (ksuserhooks);
use LC::Fatal qw (symlink);
use File::stat;
use Time::localtime;
use Readonly;
use version;

use constant PXEROOT => "/system/aii/nbp/pxelinux";
use constant NBPDIR => 'nbpdir';
use constant LOCALBOOT => 'bootconfig';
use constant HOSTNAME => "/system/network/hostname";
use constant DOMAINNAME => "/system/network/domainname";
use constant ETH => "/system/network/interfaces";
use constant INSTALL => 'install';
use constant BOOT => 'boot';
use constant RESCUE => 'rescue';
use constant RESCUEBOOT => 'rescueconfig';
use constant FIRMWARE => 'firmware';
use constant LIVECD => 'livecd';
# Hooks for NBP plug-in
use constant RESCUE_HOOK_PATH => '/system/aii/hooks/rescue';
use constant INSTALL_HOOK_PATH => '/system/aii/hooks/install';
use constant REMOVE_HOOK_PATH => '/system/aii/hooks/remove';
use constant BOOT_HOOK_PATH => '/system/aii/hooks/boot';
use constant FIRMWARE_HOOK_PATH => '/system/aii/hooks/firmware';
use constant LIVECD_HOOK_PATH => '/system/aii/hooks/livecd';
# Kickstart constants (trying to use same name as in ks.pm from aii-ks)
use constant KS => "/system/aii/osinstall/ks";

# Lowest supported version is EL 5.0
use constant ANACONDA_VERSION_EL_5_0 => version->new("11.1");
use constant ANACONDA_VERSION_EL_7_0 => version->new("19.31"); 
use constant ANACONDA_VERSION_LOWEST => ANACONDA_VERSION_EL_5_0;

our @ISA = qw (NCM::Component);
our $EC = LC::Exception::Context->new->will_store_all;
our $this_app = $main::this_app;

# Return the fqdn of the node
sub get_fqdn 
{
    my $cfg = shift;
    my $h = $cfg->getElement (HOSTNAME)->getValue;
    my $d = $cfg->getElement (DOMAINNAME)->getValue;
    return "$h.$d";    
}

# return the anaconda version instance as specified in the kickstart (if at all)
sub get_anaconda_version
{
    my $kst = shift;
    my $version = ANACONDA_VERSION_LOWEST;
    if ($kst->{version}) {
        $version = version->new($kst->{version});
        if ($version < ANACONDA_VERSION_LOWEST) {
            # TODO is this ok, or should we stop?
            $this_app->error("Version $version < lowest supported ".ANACONDA_VERSION_LOWEST.", continuing with lowest");
            $version = ANACONDA_VERSION_LOWEST;
        }        
    };
    return $version;    
}

# Returns the absolute path where the PXE file must be written.
sub filepath
{
    my $cfg = shift;

    my $fqdn = get_fqdn($cfg);
    my $dir = $this_app->option (NBPDIR);
    $this_app->debug(3, "NBP directory = $dir");
    return "$dir/$fqdn.cfg";
}

# Returns the absolute path of the PXE file to link to
sub link_filepath
{
    my ($cfg, $cmd) = @_;

    my $dir = $this_app->option (NBPDIR);

    my $cfgpath = PXEROOT . "/" . $cmd;
    if ($cfg->elementExists ($cfgpath)) {
        my $linkname = $cfg->getElement ($cfgpath)->getValue;
        return "$dir/$linkname";
    } elsif ($cmd eq RESCUE) {
        # Backwards compatibility: use the option set on the command line
        # if the profile does not define a rescue image
        my $path = $this_app->option (RESCUEBOOT);
        unless ($path =~ m{^([-.\w]+)$}) {
            $this_app->error ("Unexpected RESCUE configuration file");
        }
        return "$dir/$1";
    } else {
        my $fqdn = get_fqdn($cfg);
        $this_app->debug(3, "No $cmd defined for $fqdn");
    }
    return undef;
}


# Configure the ksdevice with static IP 
# (EL7+ only)
sub pxe_ks_static_network
{
    my ($config, $t) = @_;

    my $fqdn = get_fqdn($config);
    my $dev = $t->{ksdevice};

    my $net = $config->getElement("/system/network/interfaces/$dev")->getTree;
    unless ($net->{ip}) {
            $this_app->error ("Static boot protocol specified ",
                              "but no IP given to the interface $dev");
            return;
    }
    
    # can't set MTU with static ip via PXE

    my $gw;
    if ($net->{gateway}) {
        $gw = $net->{gateway};
    } elsif ($config->elementExists ("/system/network/default_gateway")) {
        $gw = $config->getElement ("/system/network/default_gateway")->getValue;
    } else {
        # This is a recipe for disaster
        # No best guess here
        $this_app->error ("No gateway defined for dev $dev and ",
                          " using static network description.");
        return;                
    };

    return "$net->{ip}::$gw:$net->{netmask}:$fqdn:$dev:none";
}


# create a list with all append options for kickstart installations
sub pxe_ks_append 
{
    my $cfg = shift;

    my $t = $cfg->getElement (PXEROOT)->getTree;
    
    my $kst = {}; # empty hashref in case no kickstart is defined
    $kst = $cfg->getElement (KS)->getTree if $cfg->elementExists(KS);

    my $version = get_anaconda_version($kst);

    my $keyprefix = "";
    my $ksdevicename = "ksdevice";  
    if($version >= ANACONDA_VERSION_EL_7_0) {
        $keyprefix="inst.";

        if($t->{ksdevice} =~ m/^(bootif|link)$/ &&
            ! $cfg->hasElement("/system/network/interfaces/$t->{ksdevice}")) {
            $this_app->debug("Using depreacted legacy behaviour. Please look into the configuration.");
        } else {
            $ksdevicename = "bootdev";  
        }
    }  

    my $ksloc = $t->{kslocation};
    my $server = hostname();
    $ksloc =~ s{LOCALHOST}{$server};

    my @append;
    push(@append,
         "ramdisk=32768",
         "initrd=$t->{initrd}",
         "${keyprefix}ks=$ksloc",
         "$ksdevicename=$t->{ksdevice}"
         );         

    if ($t->{updates}) {
        push(@append,"${keyprefix}updates=$t->{updates}");
    };

    if ($kst->{logging} && $kst->{logging}->{host}) {
        push(@append, "${keyprefix}syslog=$kst->{logging}->{host}:$kst->{logging}->{port}"); 
        push(@append, "${keyprefix}loglevel=$kst->{logging}->{level}") if $kst->{logging}->{level};
    }
    
    if ($version >= ANACONDA_VERSION_EL_7_0) {
        if ($kst->{enable_sshd}) {
            push(@append, "${keyprefix}sshd");
        };
        
        if ($kst->{cmdline}) {
            push(@append, "${keyprefix}cmdline");
        };
{'fraction_non_alphanumeric': 0.15796952711770743, 'fraction_numerical': 0.004578826872977027, 'mean_word_length': 2.939054726368159, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 171, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5921547', 'n_tokens_mistral': 4369, 'n_tokens_neox': 4272, 'n_words': 1052}