#coding:utf-8

import os
import sys
import sunhpc
import shutil
import textwrap
import subprocess
class Command(sunhpc.commands.pxelinux.build.command):
    """
    Build the iso pxe (dhcpd, tftpd, httpd service for the sunhpc cluster.

    1) config interface(e,g.. eth0/eth1) ip address
    2) source /opt/sunhpc/etc/env.sunhpc
    3) git clone https://gitee.com/qcsun/sunhpc.git
    4) git clone https://gitee.com/qcsun/tools.git
    5) rpm -ivh tools/sunhpc-python-3.12.0-1.el8.x86_64.rpm
    6) sunhpc pxelinux build Rocky-8.7-x86_64-dvd1.iso
    7) sunhpc pxelinux build cdpxe dev=eth1 repo=/export/sunhpc/install/repos/Rocky/8/x86_64

    <param type='Bool' name='Quiet'>
    Whether to output detailed information, default: no
    </param>

    <param type='name' name='dev'>
    supply an network device name,e.g,, eth0,eth1...
    </param>

    <param type='path' name='repo'>
    supply an pxe repo path,e.g,, /export/sunhpc/install/repos/Rocky/8/x86_64/
    </param>

    <param type='path' name='pxedir'>
    supply an pxelinux dirs. Default: /tftpboot/pxelinux
    </param>

    <param type='string' name='boot'>
    supply an boot mode, mbr or uefi. Default: MBR
    </param>
    
    <example cmd='pxelinux build cdpxe'>
    In local build the iso pxe dhcpd, tftpd, httpd services.
    </example>

    <example cmd='pxelinux build cdpxe dev=eth1 repo=/export/sunhpc/install/repos/Rocky/8/x86_64'>
    Build the iso pxe dhcpd, tftpd, httpd service.
    </example>
    """
    def writeRepos(self, repo_dirs):

        base_repo = os.path.join(repo_dirs, 'BaseOS')
        apps_repo = os.path.join(repo_dirs, 'AppStream')

        local_repos = '/etc/yum.repos.d/sunhpc-local.repo'
        with open(local_repos, 'w') as fn:
            fn.write('#\n# sunhpc-local.repo\n# Generate by sunhpc\n#\n\n')
            fn.write('[sunhpc-baseos]\n')
            fn.write('name=Sunhpc Linux $releasever - Local - BaseOS\n')
            fn.write('baseurl=file://%s\n' % base_repo)
            fn.write('gpgcheck=1\n')
            fn.write('enabled=1\n')
            fn.write('gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-rockyofficial\n\n')

            fn.write('[sunhpc-appstream]\n')
            fn.write('name=Sunhpc Linux $releasever - Local - AppStream\n')
            fn.write('baseurl=file://%s\n' % apps_repo)
            fn.write('gpgcheck=1\n')
            fn.write('enabled=1\n')
            fn.write('gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-rockyofficial\n\n')

    def copyit(self, src, dst, perms=0o644, owner=None):
        if not os.path.exists(src):
            self.msg('The %s not exists.' % src, 'a')

        if os.path.exists(dst): os.remove(dst)

        shutil.copyfile(src, dst)
        os.chmod(dst, perms)
        if owner:
            os.system('chown %s %s' % (owner, dst))

    def run(self, params, args):

        (quiet, pxedir, dev, rpath, boot) = self.fillParams([
            ('quiet', 'no'),
            ('pxedir', '/tftpboot/pxelinux'),
            ('dev', None),
            ('repo', ''),
            ('boot', 'mbr')
            ])
        q = self.str2bool(quiet)

        if dev is None:
            self.msg('Must supply an network dev name,e.g,, eth0/eth1...', 'a')

        address = os.popen("ip -4 addr show %s |grep inet|awk '{print $2}'" % dev).read().strip().split('/')[0]

        if not os.path.exists(rpath):
            self.msg('supply an pxe repo path,e.g,, repo=/export/sunhpc/install/repos/Rocky/8/x86_64', 'a')

        # write new repos
        self.writeRepos(rpath)

        # install dhcp server
        self.installDhcpd(dev, boot, address, q)

        # install tftp server
        self.installTftpd(pxedir, rpath, boot, address, q)

        # install httpd server
        self.installHttpd(dev, rpath, address, q)

        # off selinux
        os.system('sed -i "s/^SELINUX=.*/SELINUX=disabled/g" /etc/selinux/config')

    def installHttpd(self, devices, rpath, address, q):
        self.msg('Starting build the httpd service...', q=q)

        webdir = '/var/www/html'
        ksdirs = os.path.join(webdir, 'ksfile')
        redhat = os.path.join(webdir, 'redhat')
        repos  = os.path.join(redhat, 'r8')

        if not os.path.exists(ksdirs):
            os.makedirs(ksdirs)

        if not os.path.exists(redhat):
            os.makedirs(redhat)

        if not os.path.islink(repos):
            os.symlink(rpath, repos)

        shared = '/opt/sunhpc/share/pxeboot/kickstart'
        ksfile = os.listdir(shared)
        for i in ksfile:
            if i.startswith('r8_min.conf'):
                self.msg('Copying to %s - %s' % (ksdirs, i), q=q)
                self.copyit(os.path.join(shared, i), os.path.join(ksdirs, i), 0o755, '0.0')
            if i.startswith('r8_gui.conf'):
                self.msg('Copying to %s - %s' % (ksdirs, i), q=q)
                self.copyit(os.path.join(shared, i), os.path.join(ksdirs, i), 0o755, '0.0')

            if not os.path.exists(os.path.join(ksdirs, i)):
                continue

            # replace ip address for kickstart config file.
            os.system('sed -i "s/ipaddress/%s/g" %s' % (address, os.path.join(ksdirs, i)))

        sunhpc_httpd_config = '/etc/httpd/conf.d/ks-sunhpc.conf'
        if not os.path.exists(sunhpc_httpd_config):
            cmds  = 'yum install -y httpd'
            cmds += '--disablerepo=* --enablerepo=sunhpc-appstream,sunhpc-baseos'
            self.shcmd(cmds)

        cmds  = 'sed -i "s/#ServerName www.example.com:80/ServerName localhost:80/" /etc/httpd/conf/httpd.conf'
        self.shcmd(cmds)

        with open(sunhpc_httpd_config, 'w') as f:
            f.write(textwrap.dedent("""\
            <VirtualHost %s:80>
                ServerAdmin root@cluster.hpc.com
                DocumentRoot %s
                ServerName cluster.hpc.com
                ErrorLog  logs/ks-sunhpc-error_log
                CustomLog logs/ks-sunhpc-access_log common
                <Directory %s>
                    AllowOverride All
                    Require all granted
                </Directory>
                <Directory %s>
                    Options Indexes MultiViews
                    AllowOverride All
                    Require all granted
                </Directory>
            </VirtualHost>
            """ % (address, webdir, ksdirs, repos)))

        self.fw.addports(['80', '443'], ['tcp'])
        self.fw.addservice('http')
        self.fw.addservice('https')

        os.system('systemctl daemon-reload')
        os.system('systemctl restart httpd.service')
        os.system('systemctl status httpd.service')


    def installTftpd(self, pxedir, rpath, boot, address, q):
        self.msg('Starting build the tftpd service...', q=q)

        sunhpc_tftpd_config = '/etc/xinetd.d/tftp'
        # The dhcpd is not installed.
        if not os.path.exists(sunhpc_tftpd_config):
            cmds  = 'yum install -y tftp tftp-server syslinux '
            cmds += '--disablerepo=* --enablerepo=sunhpc-appstream,sunhpc-baseos'
            self.shcmd(cmds)

        # pxedir: /tftpboot/pxelinux
        c7 = os.path.join(pxedir, 'c7')
        r8 = os.path.join(pxedir, 'r8')
        r9 = os.path.join(pxedir, 'r9')
        pd = os.path.join(pxedir, 'pxelinux.cfg')
        if not os.path.exists(pxedir):
            os.makedirs(c7)
            os.makedirs(r8)
            os.makedirs(r9)
            os.makedirs(pd)

        self.fw.addports(['69'], ['udp'])
        self.fw.addservice('tftp')

        sunhpc_tftpd_config = '/etc/xinetd.d/tftp'
        with open(sunhpc_tftpd_config, 'w') as f:
            f.write(textwrap.dedent("""\
                # default: off
                #   protocol.  The tftp protocol is often used to boot diskless
                #   workstations, download configuration files to network-aware printers,
                #   and to start the installation process for some operating systems.
                service tftp
                {
                    socket_type     = dgram
                    protocol        = udp
                    wait            = yes
                    user            = root
                    server          = /usr/sbin/in.tftpd
                    server_args     = -s %s -B 1468
                    disable         = no
                    per_source      = 11
                    cps             = 100 2
                    flags           = IPv4
                }
            """ % pxedir))

        tftpd_srvs_config = '/usr/lib/systemd/system/tftp.service'
        with open(tftpd_srvs_config, 'w') as f:
            f.write(textwrap.dedent(f"""\
                [Unit]
                Description=Tftp Server
                Requires=tftp.socket
                Documentation=man:in.tftpd

                [Service]
                ExecStart=/usr/sbin/in.tftpd -s %s
                StandardInput=socket

                [Install]
                Also=tftp.socket
            """ % pxedir))
        
        os.system('systemctl daemon-reload')
        os.system('systemctl restart tftp.service')
        #
        # copy uefi boot file
        # BaseOS/Packages/s/shim-x64-15.6-1.el8.x86_64.rpm
        # BaseOS/Packages/g/grub2-efi-x64-2.02-142.el8.rocky.0.2.x86_64.rpm
        # /usr/share/syslinux/{pxelinux.0, menu.c32}
        # isolinux/{ldlinux.c32, libcom32.c32, libutil.c32}
        # decompress: rpm2cpio xxx-xxx-xxx.rpm |cpio -dim
        #       boot/efi/EFI/BOOT/shimx64.efi
        #       boot/efi/EFI/rocky/grubx64.efi
        # 
        share = '/opt/sunhpc/share/pxeboot'
        repos = os.path.join(rpath, 'isolinux')

        pxefile = os.listdir(share) + os.listdir(repos)
        for i in pxefile:
            if i.startswith('initrd.img'):
                self.msg('Copying to %s - %s' % (r8, i), q=q)
                self.copyit(os.path.join(repos, i), os.path.join(r8, i), 0o755, '0.0')
            if i.startswith('vmlinuz'):
                self.msg('Copying to %s - %s' % (r8, i), q=q)
                self.copyit(os.path.join(repos, i), os.path.join(r8, i), 0o755, '0.0')

            if i.startswith('grubx64.efi'):
                self.msg('Copying to %s - %s' % (pxedir, i), q=q)
                self.copyit(os.path.join(share, i), os.path.join(pxedir, i), 0o644, '0.0')
            if i.startswith('ldlinux.c32'):
                self.msg('Copying to %s - %s' % (pxedir, i), q=q)
                self.copyit(os.path.join(share, i), os.path.join(pxedir, i), 0o644, '0.0')
            if i.startswith('libcom32.c32'):
                self.msg('Copying to %s - %s' % (pxedir, i), q=q)
                self.copyit(os.path.join(share, i), os.path.join(pxedir, i), 0o644, '0.0')
            if i.startswith('libutil.c32'):
                self.msg('Copying to %s - %s' % (pxedir, i), q=q)
                self.copyit(os.path.join(share, i), os.path.join(pxedir, i), 0o644, '0.0')
            if i.startswith('menu.c32'):
                self.msg('Copying to %s - %s' % (pxedir, i), q=q)
                self.copyit(os.path.join(share, i), os.path.join(pxedir, i), 0o644, '0.0')
            if i.startswith('pxelinux.0'):
                self.msg('Copying to %s - %s' % (pxedir, i), q=q)
                self.copyit(os.path.join(share, i), os.path.join(pxedir, i), 0o644, '0.0')
            if i.startswith('shimx64.efi'):
                self.msg('Copying to %s - %s' % (pxedir, i), q=q)
                self.copyit(os.path.join(share, i), os.path.join(pxedir, i), 0o644, '0.0')
            if i.startswith('vesamenu.c32'):
                self.msg('Copying to %s - %s' % (pxedir, i), q=q)
                self.copyit(os.path.join(share, i), os.path.join(pxedir, i), 0o644, '0.0')

            if i.startswith('default'):
                self.msg('Copying to %s - %s' % (pd, i), q=q)
                self.copyit(os.path.join(share, i), os.path.join(pd, i), 0o644, '0.0')

        # replace ip address for pxelinux.cfg/default
        os.system('sed -i "s/ipaddress/%s/g" %s' % (address, os.path.join(pd, 'default')))


    def installDhcpd(self, devices, boot, address, q):
        self.msg('Starting build the dhcpd service...', q=q)

        config = '/etc/dhcp/dhcpd.conf'
        # The dhcpd is not installed.
        if not os.path.exists(config):
            cmds  = 'yum install -y dhcp-server '
            cmds += '--disablerepo=* --enablerepo=sunhpc-appstream,sunhpc-baseos'
            self.shcmd(cmds)

        # 172.16.2.1 -> ['172','16','0'] -> 172.16.2
        addr_pre = '.'.join(address.split('.')[:3])

        with open(config, 'w') as f:
            f.write(textwrap.dedent("""\
            #
            # dhcpd.conf
            #

            ddns-update-style none;
            allow bootp;
            allow booting;
            option space pxelinux;
            option pxelinux.magic code 208 = string;
            option pxelinux.configfile code 209 = text;
            option pxelinux.pathprefix code 210 = text;
            option pxelinux.reboottime code 211 = unsigned integer 32;
            option arch-type code 93 = unsigned integer 16;

            subnet %s.0 netmask 255.255.255.0 {
                default-lease-time 600;
                max-lease-time 7200;

                range %s.10 %s.200;
                option routers %s;
                option domain-name "local";
                option domain-name-servers %s;
                option interface-mtu 1500;

                class "pxeclients" {
                    match if substring (option vendor-class-identifier, 0, 9) = "PXEClient";
                    next-server %s;
                    if option arch-type = 00:07 {
                        filename "shimx64.efi";
                    } else {
                        filename "pxelinux.0";
                    }
                }
            }
            """ % (addr_pre, addr_pre, addr_pre, address, address, address)))

        dhcpd_srv_conf = '/usr/lib/systemd/system/dhcpd.service'
        with open(dhcpd_srv_conf, 'w') as f:
            f.write(textwrap.dedent("""\
                [Unit]
                Description=DHCPv4 Server Daemon
                Documentation=man:dhcpd(8) man:dhcpd.conf(5)
                Wants=network-online.target
                After=network-online.target
                After=time-sync.target

                [Service]
                Type=notify
                ExecStart=/usr/sbin/dhcpd -f -cf /etc/dhcp/dhcpd.conf -user dhcpd -group dhcpd --no-pid %s

                [Install]
                WantedBy=multi-user.target
            """ % devices))

        self.fw.addports(['67'], ['udp'])
        self.fw.addports(['68'], ['udp'])
        self.fw.addservice('dhcp')
        os.system('systemctl daemon-reload')
        os.system('systemctl restart dhcpd')
        os.system('systemctl status dhcpd')

RollName = "base"
