<?php

/**
 * 
 * @author LIN 
 * @copyright pnetlab.com
 * @link https://www.pnetlab.com/
 * 
 */

class device_ceos extends device
{

    public function createEthernets($quantity)
    {
        $ethernets = [];
        for ($i = 0; $i < $quantity; $i++) {

            if (!isset($this->ethernets[$i])) {
                    if ($i==0){
                     $n = 'Mgmt/eth0' ;
                    }
                    else {
                     $n = 'et' . $i;
                    }
                            // Interface name
                try {
                    $ethernets[$i] = new Interfc($this, ['name' => $n,'type' => 'ethernet',], $i);
                } catch (Exception $e) {
                    error_log(date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][40020]);
                    error_log(date('M d H:i:s ') . (string) $e);
                    return 40020;
                }
            } else {
                $ethernets[$i] = $this->ethernets[$i];
            }
        }
        $this->ethernets = $ethernets;
        return $this->ethernets;
    }

    public function editParams($p)
    {
        
        if (isset($p['docker_options'])) {
            $this->docker_options = (string) $p['docker_options'];
        }

        if (isset($p['console'])) {
            $this->console = (string) $p['console'];
        }
        if (isset($p['ETBA'])) {
            $this->ETBA = (string) $p['ETBA'];
        }
        if (isset($p['EOS_PLATFORM'])) {
            $this->EOS_PLATFORM = (string) $p['EOS_PLATFORM'];
        }

        if (isset($p['eth0_dhcp'])) {
            $this->eth0_dhcp = (int) $p['eth0_dhcp'];
        }
        if (isset($p['eth0_ip'])) {
            $this->eth0_ip = (string) $p['eth0_ip'];
        }
        if (isset($p['console_2nd'])) {
            $this->console_2nd = (string) $p['console_2nd'];
        }
        if (isset($p['default_route'])) {
            $this->default_route = (string) $p['default_route'];
        }
        
        if (isset($p['DNS'])) {
            $this->DNS = (string) $p['DNS'];
        }



        parent::editParams($p);
    }

    public function getParams()
    {
        $params = parent::getParams();
        return array_replace($params, [
            'docker_options' => $this->docker_options,
            'console' => $this->console,
            'console_2nd' => $this->console_2nd,
            'ETBA' => $this->ETBA,
            'EOS_PLATFORM' => $this->EOS_PLATFORM,
            'eth0_dhcp' => $this->eth0_dhcp,
            'eth0_ip' => $this->eth0_ip,
            'default_route' => $this->default_route,
            'DNS' => $this->DNS,
        ]);
    }

    public function prepare()
    {
        $result = parent::prepare();
        if($result != 0) return $result;

        if($this->map_port == ''){
            if ($this->console == 'ssh') {
                $connPort = 22;
            } 
            else {
                $connPort = 23;
            }
        }
        else{
            $connPort = (int) $this->map_port;
        }
        if($this->map_port_2nd == ''){
            if ($this->console_2nd == 'ssh') {
                $connPort2nd = 22;
            }
            else {
                $connPort2nd = 23;
            }
        }
        else{
            $connPort2nd = (int) $this->map_port_2nd;
        }

        $cmd = 'docker -H=tcp://127.0.0.1:4243 inspect --format="{{ .State.Running }}" docker' . $this->getSession();
         error_log(date('M d H:i:s ') . 'INFO: starting ' . $cmd);
        exec($cmd, $o, $rc);
        error_log($rc);
        if ($rc != 0) {
            // Must create docker.io container
            //Check Docker Options
            if($connPort == 23 && $connPort2nd == 23 ){
                 $consoleCmd = '';
                 $consoleCmd2nd = '';
            }else if ($connPort !== 23 && $connPort2nd !== 23 ) {
                $consoleCmd = '--net=bridge -p ' . $this->getPort() . ':' . $connPort . ' -p ' . $this->getSecondPort() . ':' . $connPort2nd;
            }
           
            else if ($connPort == 23 && $connPort2nd !== 23 ) {
                $consoleCmd2nd = ' -p --net=bridge ' . $this->getSecondPort() . ':' . $connPort2nd;
            }
            else if ($connPort !== 23 && $connPort2nd == 23 ) {
                $consoleCmd2nd = '--net=bridge -p ' . $this->getPort() . ':' . $connPort;
            } 


            if(!isset($this->docker_options)) $this->docker_options = '';
       
            $setenv = ' /sbin/init systemd.setenv=INTFTYPE=et systemd.setenv=ETBA=' . $this->ETBA . ' systemd.setenv=SKIP_ZEROTOUCH_BARRIER_IN_SYSDBINIT=1 systemd.setenv=CEOS=1 systemd.setenv=EOS_PLATFORM=' . $this->EOS_PLATFORM. ' systemd.setenv=container=docker systemd.setenv=MAPETH0=1 systemd.setenv=MGMT_INTF=eth0 ';

            $env = ' -e INTFTYPE=et -e ETBA=' .$this->ETBA. ' -e SKIP_ZEROTOUCH_BARRIER_IN_SYSDBINIT=1 -e CEOS=1 -e EOS_PLATFORM='. $this->EOS_PLATFORM . ' -e container=docker --net=none --privileged ';
            $cmd = 'docker -H=tcp://127.0.0.1:4243 create ' ;
            $cmd .=  $env .' ' . $this->docker_options . '  ' . $consoleCmd . ' ' . $consoleCmd2nd . ' --name=docker' . $this->getSession() . ' ' . ' -h "' . $this->name . '" '.   $this->image . $setenv ;
            $cmd = preg_replace('/\s+/m', ' ', $cmd);
            error_log(date('M d H:i:s ') . 'INFO: starting ' . $cmd);
            exec($cmd, $o, $rc);
            
            if ($rc != 0) {
                error_log(date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][80083]);
                return 80083;
            }
        }


        if (!touch($this->getRunningPath() . '/.prepared')) {
            // Cannot write on directory
            error_log(date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][80044]);
            return 80044;
        }

        return 0;
    }


    public function start()
    {

        $result = parent::start();
        if($result != 0) return $result;
        $docker_name= 'docker' . $this->getSession();
        $cmd = 'docker -H=tcp://127.0.0.1:4243 start docker' . $this->getSession();
        error_log(date('M d H:i:s ') . 'INFO: starting ' . $cmd);
        exec($cmd, $o, $rc);
        sleep((int) $this->delay);
        if ($rc == 0) {

                $cmd = 'docker -H=tcp://127.0.0.1:4243 inspect --format "{{ .State.Pid }}" docker' . $this->getSession();
                //error_log(date('M d H:i:s ').'INFO: starting '.$cmd);
                exec($cmd, $o, $rc);
                $pid = $o[1];
                
                if ( file_exists ($this->getRunningPath() . '/startup-config' ) ) {
                $cmd = ' /usr/bin/docker -H tcp://127.0.0.1:4243 cp '.$this->getRunningPath() . '/startup-config '  .$docker_name . ':/mnt/flash/' ; 
                         error_log(date('M d H:i:s ') . 'INFO: starting ' . $cmd);

                 exec($cmd, $o, $rc);

                }
                else if ( !file_exists ($this->getRunningPath() . '/initial-config' ) )  {
                copy('/opt/unetlab/startup_configs/Docker/cEOS/initial-config', $this->getRunningPath() . '/initial-config'); 
                $startup_ceos = $this->getRunningPath() . '/initial-config' ;    
                $file_contents = file_get_contents($startup_ceos);
                $file_contents = str_replace("hostname ", "hostname  $this->name  ", $file_contents);
                file_put_contents($this->getRunningPath() . '/initial-config',  $file_contents);
                $cmd = ' /usr/bin/docker -H tcp://127.0.0.1:4243 cp '.$this->getRunningPath() . '/initial-config '  .$docker_name . ':/mnt/flash/startup-config' ; 
                         error_log(date('M d H:i:s ') . 'INFO: starting ' . $cmd);

                 exec($cmd, $o, $rc);
                }
 

                foreach ($this->getEthernets() as $interface_id => $interface) {

                    $vunl = 'vunl' . $this->getSession() . '_' . $interface_id ;
                    $sr_linux = 'ceos' . $this->getSession() . '_' . $interface_id ;

                    $cmd = 'ip link delete ' . $vunl;
                    exec($cmd, $o, $rc);

                    $cmd = 'ip link add ' . $sr_linux . ' type veth peer name '. $vunl;
                    error_log(date('M d H:i:s ') . 'INFO: starting ' . $cmd);
                    exec($cmd, $o, $rc);

                    $cmd = 'ip link set dev ' . $vunl . ' up';
                    error_log(date('M d H:i:s ') . 'INFO: starting ' . $cmd);
                    exec($cmd, $o, $rc);
                    $network = $this->getNetwork($interface->getNetworkId());

                        if (isset($network) && $network->isCloud()) {
                            // Network is a Cloud
                            $net_name = $network->getNType();
                        } 
                        else if ( $network && $network->listNetworkTypes( ) == 'internal') {

                             $net_name = 'internal_' . $this->getLabSession()  ;
                             $netName1 = $this->getNetwork($interface->getNetworkId())->addSysNetwork();
                        }
                        else if ($network && $network->listNetworkTypes( ) == 'internal2') {

                            $net_name = 'internal2_' . $this->getLabSession()  ;
                            $netName1 = $this->getNetwork($interface->getNetworkId())->addSysNetwork();
                        }
                        else if ($network && $network->listNetworkTypes( ) == 'internal3') {

                            $net_name = 'internal3_'  . $this->getLabSession() ;
                            $netName1 = $this->getNetwork($interface->getNetworkId())->addSysNetwork();
                        }

                        else if ($network && $network->listNetworkTypes( ) == 'private') {

                            $net_name = 'private_' . $this->getHost() ;
                            $netName1 = $this->getNetwork($interface->getNetworkId())->addSysNetwork();
                        }
                        else if ($network && $network->listNetworkTypes( ) == 'private2') {

                            $net_name = 'private2_' . $this->getHost() ;
                            $netName1 = $this->getNetwork($interface->getNetworkId())->addSysNetwork();

                        }
                        else if ($network && $network->listNetworkTypes( ) == 'private3') {

                            $net_name = 'private3_' . $this->getHost();
                            $netName1 = $this->getNetwork($interface->getNetworkId())->addSysNetwork();
                        }
                        else {
                        $net_name = 'vnet' . $this->getLabSession() . '_' . $interface->getNetworkId();
                        }

                        $cmd = 'brctl addif ' . $net_name . ' vunl' . $this->getSession() . '_' . $interface_id  ;
                        error_log(date('M d H:i:s ') . 'INFO: starting ' . $cmd);
                        exec($cmd, $o, $rc);
                        if ($interface_id == 0 ) {
                            $cmd = 'ip link set netns ' . $pid . ' ' . $sr_linux . ' name eth0 up';
                            error_log(date('M d H:i:s ') . 'INFO: starting ' . $cmd);
                            exec($cmd, $o, $rc);

                            }
                        else {
                            $cmd = 'ip link set netns ' . $pid . ' ' . $sr_linux . ' name et' .  $interface_id .  ' up';
                            error_log(date('M d H:i:s ') . 'INFO: starting ' . $cmd);
                            exec($cmd, $o, $rc);
                        }
            }            

                $cmd = ' docker -H=tcp://127.0.0.1:4243 exec -u root ' . $docker_name . ' umount /etc/resolv.conf ' ;
                error_log(date('M d H:i:s ') . 'umount ' . $cmd);
                exec($cmd, $o, $rc);

                $cmd = ' /usr/bin/docker -H tcp://127.0.0.1:4243 cp /opt/unetlab/wrappers/busybox ' . $docker_name . ':/' ;
                error_log(date('M d H:i:s ') . 'importing busybox ' . $cmd);
                exec($cmd, $o, $rc);

       
                $cmd = ' /usr/bin/docker -H tcp://127.0.0.1:4243 cp /opt/unetlab/wrappers/profile.sh ' . $docker_name . ':/' ;
                error_log(date('M d H:i:s ') . 'importing profile ' . $cmd);
                exec($cmd, $o, $rc);

        
                $cmd = ' /usr/bin/docker -H tcp://127.0.0.1:4243 cp /opt/unetlab/wrappers/udhcpc.script ' . $docker_name . ':/' ;
                error_log(date('M d H:i:s ') . 'importing udhcpc.script ' . $cmd);
                exec($cmd, $o, $rc);

                $cmd = ' /usr/bin/docker -H tcp://127.0.0.1:4243 cp /opt/unetlab/wrappers/bash-static ' . $docker_name . ':/' ;
                error_log(date('M d H:i:s ') . 'importing bash-static ' . $cmd);
                exec($cmd, $o, $rc);

                $cmd = '/opt/unetlab/wrappers/nsenter -t ' . $pid . ' -m -u -i -n -p -w /bash-static -c "/busybox ls /bin/bash 2>/dev/null || /busybox ln /bash-static /bin/bash"' ;
                error_log(date('M d H:i:s ') . ' linking bash if required  ' . $cmd);
                exec($cmd, $o, $rc);
                $cmd = '/opt/unetlab/wrappers/nsenter -t ' . $pid . ' -m -u -i -n -p -w chmod a+x /etc/profile' ;
                error_log(date('M d H:i:s ') . ' starting  ' . $cmd);
                exec($cmd, $o, $rc); 
                $cmd = '/opt/unetlab/wrappers/nsenter -t ' . $pid . ' -m -u -i -n -p -w bash /etc/profile' ;
                error_log(date('M d H:i:s ') . ' starting  ' . $cmd);
                exec($cmd, $o, $rc); 

        // Start configuration process  

            $cmd = exec ('sleep 5');

            //DHCP Settings
            if(preg_match('/([0-9]+.[0-9]+.[0-9]+.[0-9]+)\/[0-9]+/', $this->eth0_ip)){
                    $cmd = '/opt/unetlab/wrappers/nsenter  -r -m -t '.$pid.' -n /busybox ip a add ' . $this->eth0_ip . ' dev eth0';
                    exec($cmd, $o, $rc);
                    error_log(date('M d H:i:s ') . 'INFO: importing ' . $cmd);
            }
            else if($this->eth0_dhcp){
                     $cmd = '/opt/unetlab/wrappers/nsenter -r -m -t ' . $pid . ' -n  /busybox udhcpc NODE="'. $this->name .'" -b -s /udhcpc.script -i eth0' ;
                   // error_log(date('M d H:i:s ') . ' starting  ' . $cmd);
                    exec($cmd, $o, $rc);
            }
            if(preg_match('/([0-9]+.[0-9]+.[0-9]+.[0-9]+)/', $this->DNS)){

                $cmd = ' docker -H=tcp://127.0.0.1:4243 exec -u root ' . $docker_name . ' umount /etc/resolv.conf ' ;
                error_log(date('M d H:i:s ') . 'umount ' . $cmd);
                exec($cmd, $o, $rc);
                $cmd = 'sudo /opt/unetlab/wrappers/nsenter -r -m -t '.$pid.' -n /bash-static -c "echo nameserver "' . $this->DNS.'" > /etc/resolv.conf" ';
                exec($cmd, $o, $rc);
                error_log(date('M d H:i:s ') . 'INFO: Set Dns Server ' . $cmd);
            }
            
            if(preg_match('/([0-9]+.[0-9]+.[0-9]+.[0-9]+)/', $this->default_route)){
                $cmd = '/opt/unetlab/wrappers/nsenter -r -m -t '.$pid.' -n /busybox route add default gw ' . $this->default_route;
                exec($cmd, $o, $rc);
                error_log(date('M d H:i:s ') . 'INFO: importing ' . $cmd);
            }
            //error_log(date('M d H:i:s ') . 'INFO: run ' . $cmd);
            unlink ($this->getRunningPath() . '/startup-config' );
            $attachCmd = 'Cli';
            $attachCmd_bash = 'bash';
            if($this->console == 'telnet'){
                $cmd = 'sudo /opt/unetlab/wrappers/docker_wrapper -P ' . $this->getPort() . ' -t "'.$this->name.'" -p ' . $this->getSession() .' -c '. $attachCmd .' > ' . $this->getRunningPath() . '/wrapper.txt 2>&1 &';
                exec($cmd, $o, $rc);
                error_log(date('M d H:i:s ') . 'INFO: run ' . $cmd);
            } 
            else if($this->console_2nd == 'telnet'){
                $cmd = 'sudo /opt/unetlab/wrappers/docker_wrapper -P ' . $this->getSecondPort() . ' -t "'.$this->name.'" -p ' . $this->getSession() .' -c '. $attachCmd .' > ' . $this->getRunningPath() . '/wrapper1.txt 2>&1 &';
                exec($cmd, $o, $rc);
                error_log(date('M d H:i:s ') . 'INFO: run ' . $cmd);
            }
            if($this->console == 'bash'){
                $cmd = 'sudo /opt/unetlab/wrappers/docker_wrapper -P ' . $this->getPort() . ' -t "'.$this->name.'" -p ' . $this->getSession() .' -c '. $attachCmd_bash .' > ' . $this->getRunningPath() . '/wrapper.txt 2>&1 &';
                exec($cmd, $o, $rc);
                error_log(date('M d H:i:s ') . 'INFO: run ' . $cmd);
            } 
            else if($this->console_2nd == 'bash'){
                $cmd = 'sudo /opt/unetlab/wrappers/docker_wrapper -P ' . $this->getSecondPort() . ' -t "'.$this->name.'" -p ' . $this->getSession() .' -c '. $attachCmd_bash .' > ' . $this->getRunningPath() . '/wrapper1.txt 2>&1 &';
                exec($cmd, $o, $rc);
                error_log(date('M d H:i:s ') . 'INFO: run ' . $cmd);
            }
            $ethernets = $this->getEthernets();
            $index = 0;
            foreach($ethernets as $ethernet){
                $index ++;
                if($index == 1) continue; // Keep eth0 up for management
                if(count($ethernet->getQuality()) > 0) $ethernet->applyQuality();
                if($ethernet->getSuspendStatus() == 1) $ethernet->applySuspendStatus();
                if($ethernet->getNetworkId() == 0) $ethernet->setLinkState('down');
            }
            
        }

        return 0;
    }

    public function export()

    {
        $docker_name= 'docker' . $this->getSession();
        $cmd = exec ('docker -H=tcp://127.0.0.1:4243 cp ' . $docker_name . ':/mnt/flash/startup-config ' .$this->getRunningPath() . '/export-config'  );
        
        $tmp = $this->getRunningPath(). '/export-config';

        // Now save the config file within the lab
        clearstatcache();

        $fp = fopen($tmp, 'r');
        if (!isset($fp)) {
            // Cannot open file
            error_log(date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][80064]);
            return 80064;
        }

       $config_data = fread($fp, filesize($tmp));
        if ($config_data === False || $config_data === '') {
            // Cannot read file
            error_log(date('M d H:i:s ') . 'ERROR: ' . $GLOBALS['messages'][80065]);
            return 80065;
        }

        $activeConfig = $this->getActiveConfig();
        if($activeConfig == ''){
            $this->config_data = $config_data;
        }else{
            $this->multi_config[$activeConfig] = $config_data;
        }
        
        
        if (!unlink($tmp)) {
            // Failed to remove tmp file
            error_log(date('M d H:i:s ') . 'WARNING: ' . $GLOBALS['messages'][80070]);
        }
        return 0;
    }

    public function wipe()
    {
        
        $cmd = 'sudo /usr/bin/docker -H=tcp://127.0.0.1:4243 rm docker' . $this->getSession();
        exec($cmd, $o, $rc);

        return parent::wipe();
        
    }
}
