Routerconfigs plugin / how it works?

Discussions on developing plugins for the Cacti Plugin Architecture

Moderators: Moderators, Developers

Post Reply
Author
Message
grady
Cacti User
Posts: 60
Joined: Wed Feb 20, 2008 5:02 pm

#16 Post by grady » Mon Mar 22, 2010 2:22 pm

Nearly have it going. Need some lovin on the username and pw being sent, but it's at least at a point where it's sending the return.

dieselboy
Cacti User
Posts: 135
Joined: Wed May 27, 2009 5:10 pm

#17 Post by dieselboy » Tue Mar 23, 2010 10:54 am

Excellent work! Whats the issue with the username / password?
The PHP part I can only just get to grips with

grady
Cacti User
Posts: 60
Joined: Wed Feb 20, 2008 5:02 pm

#18 Post by grady » Tue Mar 23, 2010 10:55 am

The username / pw issue is resolved. I ended up replicating the phptelnet class to phptelnethp with some minor additions. I can now log in successfully, but I think I have a logic error somewhere. Working that out now...

dieselboy
Cacti User
Posts: 135
Joined: Wed May 27, 2009 5:10 pm

#19 Post by dieselboy » Tue Mar 23, 2010 10:58 am

Okay, what is happening?

I mentioned that what seems to happen is the tftp download takes place, and then the script investigates the file, if it doesnt fit the criteria it deletes the file and fails. To prevent this, I had to comment out the investigative lines from 187 to 192?

May you post a copy of your current functions.php?

grady
Cacti User
Posts: 60
Joined: Wed Feb 20, 2008 5:02 pm

#20 Post by grady » Tue Mar 23, 2010 11:08 am

Sure, so this is where I'm at: I've updated the function plugin_routerconfigs_download_config. The commented section is obviously what Ive added thus far and the last change is below the // if result =0. Changed that or so that result1 is evaluated the same way. (which doesnt appear to be happening)

Code: Select all

        $devicetype = db_fetch_row("SELECT * FROM plugin_routerconfigs_devicetypes WHERE id = " . $device['devicetype']);
//begin HP switch crap
        $devicetypeHP = db_fetch_row("SELECT name FROM plugin_routerconfigs_devicetypes WHERE id = " . $device['devicetype']);
        foreach ($devicetypeHP as $key => $value)
        {
        if ($value == "HP"){
                $devicetype = db_fetch_row("SELECT * FROM plugin_routerconfigs_devicetypes WHERE id = " . $device['devicetype']);
                $result1 = $telnetHP->Connect($device['ipaddress'],  $info['username'], $info['password'], $devicetype);
                $debug = $telnet->debug;
                 db_execute('UPDATE plugin_routerconfigs_devices SET lastattempt = ' . time() . ' WHERE id = ' . $device['id']);



                }
        }

      if (strpos($result1, 'confirm') !== FALSE || strpos($result1, 'to tftp:') !== FALSE || $devicetype['forceconfirm']) {
                        $telnet->DoCommand('y', $result1);
                        $debug .= $result1;
                        echo $result1;

        }
 //end HP switch crap */

                if (empty($devicetype)){               $devicetype = array('username' => 'sername:',
                                        'password' => 'assword:',
                                        'copytftp' => 'copy run tftp',
                                        'version' => 'show version',
                                        'forceconfirm' => 0,
                                );
        }
        $result = $telnet->Connect($device['ipaddress'], $info['username'], $info['password'], $info['enablepw'], $devicetype);
        $debug = $telnet->debug;
        db_execute('UPDATE plugin_routerconfigs_devices SET lastattempt = ' . time() . ' WHERE id = ' . $device['id']);

        //if ($result == 0) {
        if (($result == 0) || ($result1 == 0)) {

Then the changes I made to the telnet class are below. When I run router-download.php I can see that it gets to the login screen, sends a carriage return, then logs in. (My changes assume both username and pw are required on your HP. If theyre not change it to that using password manager username password) Then it just hangs. I can see that the db execute statement is being executed as the time stamp in that table for my switch is updated.

Code: Select all

class PHPTelnetHP {
        var $show_connect_error=1;

        var $use_usleep=0;      // change to 1 for faster execution
                // don't change to 1 on Windows servers unless you have PHP 5
        var $sleeptime=125000;
        var $loginsleeptime=1000000;
        var $fp=NULL;
        var $loginprompt;
        var $error = 0;

        var $conn1;
        var $conn2;

        var $debug;

        /*
        0 = success
        1 = couldn't open network connection
        2 = unknown host
        3 = login failed
        4 = PHP version too low
        */
        function Connect($server, $user, $pass, $enablepw, $devicetype) {
                $this->debug = '';
                $rv=0;
                $vers=explode('.',PHP_VERSION);
                $needvers=array(4,3,0);
                $j=count($vers);
                $k=count($needvers);
                if ($k<$j) $j=$k;
                for ($i=0;$i<$j;$i++) {
                        if (($vers[$i]+0)>$needvers[$i]) break;
                        if (($vers[$i]+0)<$needvers[$i]) {
                                echo $this->ConnectError(4);
                                return 4;
                        }
                }

                $this->Disconnect();

                if (strlen($server)) {
                        if (preg_match('/[^0-9.]/',$server)) {
                                $ip=gethostbyname($server);
                                if ($ip==$server) {
                                        $ip='';
                                        $rv=2;
                                }
                        } else $ip=$server;
                } else $ip='127.0.0.1';
                if (strlen($ip)) {
                        if (@$this->fp = fsockopen($ip, 23)) {
                                @fputs($this->fp, $this->conn1);
                                $this->Sleep();

                                @fputs($this->fp, $this->conn2);
                                $this->Sleep();

                                $this->GetResponse($r);
                                if (strpos($r, $devicetype['username']) === FALSE && strpos($r, 'Access not permitted.') !== FALSE || !$this->fp) {
                                        echo $r;
                                        return 6;
                                }
                                echo $r;




// Get Username Prompt
                                $res = '';
                                $x = 0;
                                while ($x < 10) {
                                        sleep(1);
                                        $this->GetResponse($r);
                                        echo $r;
                                        $res .= $r;
                                        if (strpos($r, $devicetype['username']) == FALSE) {
                                                fputs($this->fp, "\r$user\r");
                                                sleep(1);
                                                break;
                                        }
                                        $x++;
                                }
                                if ($x == 10) {
                                        return 8;
                                }
// Get Password Prompt
                                $res = '';
                                $x = 0;
                                while ($x < 10) {
                                        sleep(3);
                                        $this->GetResponse($r);
                                        echo $r;
                                        $res .= $r;
                                        if (strpos($res, $devicetype['password']) == FALSE) {
                                                $r=explode("\n", $r);
                                                $this->loginprompt = trim(substr($r[count($r) - 1], 0, strpos($r[count($r) - 1], ' ')));
                                                @fputs($this->fp, "$pass\r");
                                                break;
                                        }
                                        $x++;
                                }
                                if ($x == 10) {
                                        return 9;
                                }

                                if ($enablepw != '') {
                                        # Get > to show we are at the command prompt and ready to input the en command
                                        $res = '';
                                        $x = 0;
                                        while ($x < 10) {
                                                sleep(3);
                                                $this->GetResponse($r);
                                                echo $r;
                                                $res .= $r;
                                                if (strpos($r, '>') !== FALSE) {
                                                        break;
                                                }
                                                $x++;
                                        }
                                        @fputs($this->fp, "en\r");

                                        # Get the password prompt again to input the enable password
                                        $res = '';
                                        $x = 0;
                                        while ($x < 10) {
                                                sleep(3);
                                                $this->GetResponse($r);
                                                echo $r;
                                                $res .= $r;
                                                if (strpos($res, $devicetype['password']) !== FALSE) {
                                                        break;
                                                }
                                                $x++;
                                        }
                                        $r=explode("\n", $r);
                                        @fputs($this->fp, "$enablepw\r");
                                }


                                $res = '';
                                $x = 0;
                                while ($x < 10) {
                                        sleep(3);
                                        $this->GetResponse($r);
                                        echo $r;
                                        $res .= $r;
                                        if (strpos($r, '#') !== FALSE || strpos($r, '> (') !== FALSE) {
                                                break;
                                        }
                                        $x++;
                                }
                                $r=explode("\n", $r);
                                if (($r[count($r) - 1] == '') || ($this->loginprompt == trim($r[count($r) - 1]))) {
                                        $rv = 3;
                                        $this->Disconnect();
                                }
                        } else $rv = 1;
                }

                if ($rv) echo $this->ConnectError($rv);
                return $rv;
        }

        function Disconnect($exit = 1) {
                if ($this->fp) {
                        if ($exit)
                                $this->DoCommand('exit', $junk);
                        fclose($this->fp);
              }
        }

        function DoCommand($c, &$r) {
                if ($this->fp) {
                        fputs($this->fp, "$c\r");
                        $this->Sleep();
                        $this->GetResponse($r);
                        $r = preg_replace("/^.*?\n(.*)\n[^\n]*$/", "$1", $r);
                }
                return $this->fp ? 1 : 0;
        }

        function GetResponse(&$r) {
                $r = '';
                stream_set_timeout($this->fp, 1);
                do {
                        $r .= fread($this->fp, 2048);
                        $this->debug .= $r;
                        $s = socket_get_status($this->fp);
                } while ($s['unread_bytes']);
        }

        function Sleep() {
                if ($this->use_usleep) usleep($this->sleeptime);
                else sleep(1);
        }

        function PHPTelnet() {
                $this->conn1=chr(0xFF).chr(0xFB).chr(0x1F).chr(0xFF).chr(0xFB).
                        chr(0x20).chr(0xFF).chr(0xFB).chr(0x18).chr(0xFF).chr(0xFB).
                        chr(0x27).chr(0xFF).chr(0xFD).chr(0x01).chr(0xFF).chr(0xFB).
                        chr(0x03).chr(0xFF).chr(0xFD).chr(0x03).chr(0xFF).chr(0xFC).
                        chr(0x23).chr(0xFF).chr(0xFC).chr(0x24).chr(0xFF).chr(0xFA).
                        chr(0x1F).chr(0x00).chr(0x50).chr(0x00).chr(0x18).chr(0xFF).
                        chr(0xF0).chr(0xFF).chr(0xFA).chr(0x20).chr(0x00).chr(0x33).
                        chr(0x38).chr(0x34).chr(0x30).chr(0x30).chr(0x2C).chr(0x33).
                        chr(0x38).chr(0x34).chr(0x30).chr(0x30).chr(0xFF).chr(0xF0).
                        chr(0xFF).chr(0xFA).chr(0x27).chr(0x00).chr(0xFF).chr(0xF0).
                        chr(0xFF).chr(0xFA).chr(0x18).chr(0x00).chr(0x58).chr(0x54).
                        chr(0x45).chr(0x52).chr(0x4D).chr(0xFF).chr(0xF0);
                $this->conn2=chr(0xFF).chr(0xFC).chr(0x01).chr(0xFF).chr(0xFC).
                        chr(0x22).chr(0xFF).chr(0xFE).chr(0x05).chr(0xFF).chr(0xFC).chr(0x21);
        }

      function ConnectError($num) {
                if ($this->show_connect_error) {
                        $this->error = $num;
                        switch ($num) {
                                case 1:
                                        return 'Unable to open network connection';
                                        break;
                                case 2:
                                        return 'Unknown host';
                                        break;
                                case 3:
                                        return 'Login failed';
                                        break;
                                case 4:
                                        return 'Connect failed: Your servers PHP version is too low for PHP Telnet';
                                        break;
                                case 5:
                                        return 'Bad download of config';
                                        break;
                                case 6:
                                        return 'Access not Permitted';
                                        break;
                                case 7:
                                        return 'No Config uploaded from Router';
                                        break;
                                case 8:
                                        return 'No Username Prompt';
                                        break;
                                case 9:
                                        return 'No Password Prompt';
                                        break;
                        }
                }
                return '';
        }
}

dieselboy
Cacti User
Posts: 135
Joined: Wed May 27, 2009 5:10 pm

#21 Post by dieselboy » Tue Mar 23, 2010 11:38 am

So are those pieces of code you entered additions or changes to the functions.php?
(I was hoping you would upload the full file so I could examdiff the files and merge with the working file I posted in post 2)
If they are additions where are they added into the file?

Thanks

grady
Cacti User
Posts: 60
Joined: Wed Feb 20, 2008 5:02 pm

#22 Post by grady » Tue Mar 23, 2010 12:44 pm

Here it is.

Edit: If you wait long enough you'll see that something finally is sent to the switch. Though it errors with "Invalid input Array". The switch then logs the user out and on 2nd pass router configs sends the following:

Code: Select all

Username: opy running-config tftp 192.
Password: 
Invalid password
Username: 192.168.1.2
WTF
Attachments
functions.txt
Somewhat functional functions ;)
(30.76 KiB) Downloaded 178 times

dieselboy
Cacti User
Posts: 135
Joined: Wed May 27, 2009 5:10 pm

#23 Post by dieselboy » Tue Mar 23, 2010 12:54 pm

Thanks for that, I'll combine that with the file I posted in post 2 (I can understand what the code is doing this way) test, and post back!

grady
Cacti User
Posts: 60
Joined: Wed Feb 20, 2008 5:02 pm

#24 Post by grady » Tue Mar 23, 2010 1:30 pm

It's pretty hacked together right now. Ultimately we need a function to tell whether or not it's an HP, and then $result inherits from it so that it can be evaluated on down the line.

Code: Select all

E.g: if $value == HP then $result = $result1 else $result = $result. 

That way all of this works: 
$telnet->DoCommand($devicetype['copytftp'], $result);
                $debug .= $result;
                echo $result;
                $telnet->DoCommand($tftpserver, $result);
                $debug .= $result;
                echo $result;
                $telnet->DoCommand($filename, $result);
                $debug .= $result;
                echo $result;
[/code]

dieselboy
Cacti User
Posts: 135
Joined: Wed May 27, 2009 5:10 pm

#25 Post by dieselboy » Wed Mar 24, 2010 8:34 am

Interesting.
You seem to know a lot about PHP.

So what happens when you attempt to backup with what you have?
For testing purposes, I set the tftp server as my laptop so I could monitor if the config was actually beginning to back up.
I don't have a HP swtich to test with any longer.

grady
Cacti User
Posts: 60
Joined: Wed Feb 20, 2008 5:02 pm

#26 Post by grady » Wed Mar 24, 2010 10:24 am

When run it logs into the HP, then for some reason it logs out. After it's logged out it then grabs the relevant commands from the sql table and sends them. I'm wondering if it's not a problem with the regex prompt matches defined in phptelnethp class.

dieselboy
Cacti User
Posts: 135
Joined: Wed May 27, 2009 5:10 pm

#27 Post by dieselboy » Thu Mar 25, 2010 4:53 am

How about trying commenting out the disconnect statement?

Code: Select all

class PHPTelnetHP {
        var $show_connect_error=1;

        var $use_usleep=0;      // change to 1 for faster execution
                // don't change to 1 on Windows servers unless you have PHP 5
        var $sleeptime=125000;
        var $loginsleeptime=1000000;
        var $fp=NULL;
        var $loginprompt;
        var $error = 0;

        var $conn1;
        var $conn2;

        var $debug;

        /*
        0 = success
        1 = couldn't open network connection
        2 = unknown host
        3 = login failed
        4 = PHP version too low
        */
        function Connect($server, $user, $pass, $enablepw, $devicetype) {
                $this->debug = '';
                $rv=0;
                $vers=explode('.',PHP_VERSION);
                $needvers=array(4,3,0);
                $j=count($vers);
                $k=count($needvers);
                if ($k<$j) $j=$k;
                for ($i=0;$i<$j;$i++) {
                        if (($vers[$i]+0)>$needvers[$i]) break;
                        if (($vers[$i]+0)<$needvers[$i]) {
                                echo $this->ConnectError(4);
                                return 4;
                        }
                }

                //$this->Disconnect();

                if (strlen($server)) {
                        if (preg_match('/[^0-9.]/',$server)) {
                                $ip=gethostbyname($server);
                                if ($ip==$server) {
                                        $ip='';
                                        $rv=2;
                                }
                        } else $ip=$server;
                } else $ip='127.0.0.1';
                if (strlen($ip)) {
                        if (@$this->fp = fsockopen($ip, 23)) {
                                @fputs($this->fp, $this->conn1);
                                $this->Sleep();

                                @fputs($this->fp, $this->conn2);
                                $this->Sleep();

                                $this->GetResponse($r);
                                if (strpos($r, $devicetype['username']) === FALSE && strpos($r, 'Access not permitted.') !== FALSE || !$this->fp) {
                                        echo $r;
                                        return 6;
                                }
                                echo $r;

grady
Cacti User
Posts: 60
Joined: Wed Feb 20, 2008 5:02 pm

#28 Post by grady » Thu Mar 25, 2010 3:45 pm

I misunderstood what was actually happening here. It goes out and logs into the HP. I saw (from cli) the welcome screen being returned and thus thought it had logged out. Not the case. That's being echoed back to save to $debug.

I've made some further changes to functions and now have things to a point where I'm able to successfully tftp an HP config up to the server. There's some error checking down the line that cacti uses to ensure that the copy was successful, and that's broken. That has a domino effect and breaks some of the other tools in the routerconfigs UI. (E.g the backups link shows no backups for the device b/c that check fails and thus the proper tables aren't updated)

I'll work on getting that sorted out.

dieselboy
Cacti User
Posts: 135
Joined: Wed May 27, 2009 5:10 pm

#29 Post by dieselboy » Fri Mar 26, 2010 8:49 am

grady wrote:I misunderstood what was actually happening here. It goes out and logs into the HP. I saw (from cli) the welcome screen being returned and thus thought it had logged out. Not the case. That's being echoed back to save to $debug.

I've made some further changes to functions and now have things to a point where I'm able to successfully tftp an HP config up to the server. There's some error checking down the line that cacti uses to ensure that the copy was successful, and that's broken. That has a domino effect and breaks some of the other tools in the routerconfigs UI. (E.g the backups link shows no backups for the device b/c that check fails and thus the proper tables aren't updated)

I'll work on getting that sorted out.
For this reason, I disabled the checking of the configuration files. I noted that on line 187 this is where it begins to check the config. If you open the functions.php file with something like notepad++ you can see the line numbers.
Congrats on getting that working! Will you be sharing your functions.php and database entry information so we can use your work?

grady
Cacti User
Posts: 60
Joined: Wed Feb 20, 2008 5:02 pm

#30 Post by grady » Fri Mar 26, 2010 8:53 am

Yep, I'll make sure to post it. I'm going to try to get the sanity checks working. I'd like for a non admin to be able to diff the configs in the cacti UI during a maintenance window in case of an emergency or something. It's not a must, but it'd be nice to have.

Post Reply