Community discussions

MikroTik App
 
alphahawk
Member Candidate
Member Candidate
Topic Author
Posts: 101
Joined: Fri Mar 28, 2008 6:40 pm

PHP API script add carriage return

Wed May 02, 2012 7:57 pm

There is probably a easy fix for this but for the life of me I cannot figure it out
if ($API_SWITCH->connect($ip,$port_alt,$GW_username,$GW_password)) {
						$API_SWITCH->write('/tool/netwatch/add',false);
						$API_SWITCH->write('=disabled=no',false);
						$API_SWITCH->write('=down-script=:log info "ap '. $netwatch['inventory'][$i]['IP'] . ' down"\r\n/tool e-mail send to="some@email.com" subject="ap '. $netwatch['inventory'][$i]['IP'] . ' down at ' . $netwatch['PName'] . '" body="ap '. $netwatch['inventory'][$i]['IP'] . ' down at ' . $netwatch['PName'] . '" tls=yes',false);
						$API_SWITCH->write('=host='. $netwatch['inventory'][$i]['IP'],false);
						$API_SWITCH->write('=interval=10m',false);
						$API_SWITCH->write('=up-script=:log info "switch 1 up"\r\n/tool e-mail send to="some@email.com" subject="ap '. $netwatch['inventory'][$i]['IP'] . ' up at ' . $netwatch['PName'] . '" body="ap '. $netwatch['inventory'][$i]['IP'] . ' up at ' . $netwatch['PName'] . '" tls=yes');
						$read_switch = $API_SWITCH->read(false);
I get this on debug
Connected...
<<< [18] /tool/netwatch/add
<<< [12] =disabled=no
<<< [192] =down-script=:log info "ap 10.0.100.101 down"\r\n/tool e-mail send to="some@email.com" subject="ap 10.0.100.101 down at Demo" body="ap 10.0.100.101 down at Demo" tls=yes
<<< [18] =host=10.0.100.101
<<< [13] =interval=10m
<<< [177] =up-script=:log info "switch 1 up"\r\n/tool e-mail send to="some@email.com" subject="ap 10.0.100.101 up at Demo" body="ap 10.0.100.101 up at  Demo" tls=yes
>>> [5/5 bytes read.
>>> [5, 9] !done
>>> [7/7 bytes read.
>>> [7, 1] =ret=*8
Disconnected...
and in the actualy netwatch on up or down its the same
:log info "switch 1 up"\r\n/tool e-mail send to="some@email.com" subject="ap 10.0.100.2 up at Demo" body="ap 10.0.100.2 up at Air2Data Demo\" tls=yes
With the line feeds and carriage returns the script its broken. Any ideas how to fix this?
 
User avatar
boen_robot
Forum Guru
Forum Guru
Posts: 2400
Joined: Thu Aug 31, 2006 4:43 pm
Location: europe://Bulgaria/Plovdiv

Re: PHP API script add carriage return

Wed May 02, 2012 8:26 pm

Write out the actual new lines OR use double quotes, and escape double quotes in your script OR use a Heredoc.

Within single quotes, "\r" and "\n" aren't interpreted...

I'd personally go for a Heredoc in this case:
                  $API_SWITCH->write('=down-script=' .
<<<SCRIPT
:log info "ap {$netwatch['inventory'][$i]['IP']} down"
/tool e-mail send to="some@email.com" subject="ap {$netwatch['inventory'][$i]['IP']} down at {$netwatch['PName']}" body="ap {$netwatch['inventory'][$i]['IP']} down at {$netwatch['PName']}" tls=yes
SCRIPT
                  , false);
                  $API_SWITCH->write('=up-script=' .
<<<SCRIPT
:log info "switch 1 up"
/tool e-mail send to="some@email.com" subject="ap {$netwatch['inventory'][$i]['IP']} up at {$netwatch['PName']}" body="ap {$netwatch['inventory'][$i]['IP']} up at {$netwatch['PName']}" tls=yes
SCRIPT
                  ); 
(you just need to escape any "{" and "}" that are supposed to be part of the script, rather than for PHP to interpret; you have none right now though)
Last edited by boen_robot on Thu May 10, 2012 5:47 pm, edited 1 time in total.
 
alphahawk
Member Candidate
Member Candidate
Topic Author
Posts: 101
Joined: Fri Mar 28, 2008 6:40 pm

Re: PHP API script add carriage return

Tue May 08, 2012 9:28 pm

Boen_robot thanks that helps a little. The problem is it adds the new line and since it doesnt have a =down-script= associated on the front it doesn't actually put it in I believe. Maybe I missed it but is there a way to append more info to a value you are passing?

Here is what I get
<<< [43] =down-script=:log info "ap 10.0.100.2 down"
<<< [139] /tool e-mail send to="some@email.com" subject="ap 10.0.100.2 down at  Demo" body="ap 10.0.100.2 down at Demo" tls=yes
and unfortunatly in the netwatch the second line is not added


Thanks
 
User avatar
boen_robot
Forum Guru
Forum Guru
Posts: 2400
Joined: Thu Aug 31, 2006 4:43 pm
Location: europe://Bulgaria/Plovdiv

Re: PHP API script add carriage return

Thu May 10, 2012 5:31 pm

It seems that client has a bug of sorts, in that it interprets multiple lines as different words of a command. I'm actually somewhat surprised that RouterOS doesn't decline the whole request due to this, but instead ignores unrecognized words (i.e. the second lines of the two scripts). But come to think of it, RouterOS is doing the right thing.

I've made a test with my client, and it doesn't seem to be affected by such a bug. With it, you can do it like this:
<?php
namespace PEAR2\Net\RouterOS;
require_once 'PEAR2/Net/RouterOS/Autoload.php';

//Define $netwatch, $ip, $GW_username, $GW_password, $port_alt and $i somewhere around here

$client = new Client($ip, $GW_username, $GW_password, $port_alt);

//Note: If the whole thing is within a loop, as the $i seems to imply,
//the following line can be moved outside of the loop for the sake of performance.
$addRequest = new Request('/tool netwatch add interval=10m disabled=no');

$addRequest
    ->setArgument('host', $netwatch['inventory'][$i]['IP'])
    ->setArgument('down-script',
<<<SCRIPT
:log info "ap {$netwatch['inventory'][$i]['IP']} down"
/tool e-mail send to="some@email.com" subject="ap {$netwatch['inventory'][$i]['IP']} down at {$netwatch['PName']}" body="ap {$netwatch['inventory'][$i]['IP']} down at {$netwatch['PName']}" tls=yes
SCRIPT
    )
    ->setArgument('up-script',
<<<SCRIPT
:log info "switch 1 up"
/tool e-mail send to="some@email.com" subject="ap {$netwatch['inventory'][$i]['IP']} up at {$netwatch['PName']}" body="ap {$netwatch['inventory'][$i]['IP']} up at {$netwatch['PName']}" tls=yes
SCRIPT
    );
$client->sendSync($addRequest); 
 
oooscar
Frequent Visitor
Frequent Visitor
Posts: 84
Joined: Sun Jan 05, 2014 12:56 pm
Location: Spain
Contact:

Re: PHP API script add carriage return

Sat Feb 21, 2015 1:55 am

Still the same problem.... trying to add a script with mikrotik API but carry return is not send....
The code works due ; but i would like to add diferents lines into script is it possible ?
	$API->write("/system/script/add",false);
	$API->write("=name=TEST_API",false);
	$command='=source=:local hh [:pick [/system clock get time] 0 2];';
	$command=$command.':log info "ap down"; ';
	$command=$command.':log info "ap down1"; ';
	$command=$command.' ';
	$API->write($command,true);
 
User avatar
boen_robot
Forum Guru
Forum Guru
Posts: 2400
Joined: Thu Aug 31, 2006 4:43 pm
Location: europe://Bulgaria/Plovdiv

Re: PHP API script add carriage return

Sat Feb 21, 2015 2:52 pm

Use double quotes in PHP, and use "\r\n" or "\n" or whatever sorts of new lines you wish your script to have ("\r\n" if you want them to be visible from Winbox).

For readability, you could add them as separate strings, concatenated onto the main ones, e.g.
  $command = '=source=:local hh [:pick [/system clock get time] 0 2];' . "\r\n";
   $command .= ':log info "ap down"; ' . "\r\n";
   $command .= ':log info "ap down1"; ' . "\r\n"; 
 
oooscar
Frequent Visitor
Frequent Visitor
Posts: 84
Joined: Sun Jan 05, 2014 12:56 pm
Location: Spain
Contact:

Re: PHP API script add carriage return

Sat Feb 21, 2015 3:29 pm

boen_robot,

it is not working... on the mikrotik is only sent the first line when I add "\r\n" or "\n".

Also i'm trying to configure PER2 into XAMPP localhost PC but can't make it working.
I did download all the files
Net_RouterOS-master\src\PEAR2\Net\RouterOS

also download form other sources Autoload.php but can't make it working.

Is there any place to show how to install step by step PER2 ?
The examples are into \Net_RouterOS-master\examples and for examples callback-and-loop.php has:

use PEAR2\Net\RouterOS;
require_once 'PEAR2/Autoload.php';

then i run http://localhost/apiMikrotik2/Net_Route ... d-loop.php

and I get:

Warning: require_once(PEAR2/Autoload.php): failed to open stream: No such file or directory in D:\xampp\htdocs\apiMikrotik2\Net_RouterOS-master\examples\callback-and-loop.php on line 4

Fatal error: require_once(): Failed opening required 'PEAR2/Autoload.php' (include_path='.;D:\xampp\php\PEAR;D:\xampp\php\PEAR2') in D:\xampp\htdocs\apiMikrotik2\Net_RouterOS-master\examples\callback-and-loop.php on line 4

I add into php.ini (include_path='.;D:\xampp\php\PEAR;D:\xampp\php\PEAR2') .

Help please !
Thanks
 
User avatar
boen_robot
Forum Guru
Forum Guru
Posts: 2400
Joined: Thu Aug 31, 2006 4:43 pm
Location: europe://Bulgaria/Plovdiv

Re: PHP API script add carriage return

Sat Feb 21, 2015 4:01 pm

it is not working... on the mikrotik is only sent the first line when I add "\r\n" or "\n".
Odd... I know the other PHP client is buggy, but I don't remember it having THAT bug.

EDIT: Oh wait... alphahawk had the same issue in this very topic... Damn, this topic is old! I had honestly forgotten about that bug.
I did download all the files
Net_RouterOS-master\src\PEAR2\Net\RouterOS

also download form other sources Autoload.php but can't make it working.
On the page from my signature, there's archives with everything needed (also available from the "Releases" at GitHub). You could download the PHAR, and include that file itself. This is the easiest way. You can replace 'PEAR2\Autoload.php' with the path to the PHAR file, and it should work the same.

Or you could download the zip or tgz files, and extract them. They include the Autoload.php as well as everything else. If you go for the zip/tgz archives, you need to also modify your PHP include path. Instead of
'.;D:\xampp\php\PEAR;D:\xampp\php\PEAR2'
it should point to the source folders of PEAR and PEAR2, i.e.
'.;D:\xampp\php\PEAR\pear;D:\xampp\php\PEAR2\php'
so that ultimately, 'PEAR2\Autoload.php' would resolve to 'D:\xampp\php\PEAR2\php\PEAR2\Autoload.php', and the rest of the source files (the zip/tgz 'src\PEAR2\Net\RouterOS' folder) would be in 'D:\xampp\php\PEAR2\php\PEAR2\Net\RouterOS'.
 
oooscar
Frequent Visitor
Frequent Visitor
Posts: 84
Joined: Sun Jan 05, 2014 12:56 pm
Location: Spain
Contact:

Re: PHP API script add carriage return

Sat Feb 21, 2015 7:17 pm

Now i can test with both environments api_mt_include_1.6.php and PEAR2/Net/RouterOS

Like you said both of them don't add carry-return:
This code adds one line with the first two logs, carry return, and one line with next two logs.....
$addRequest
    ->setArgument('name', 'TEST_API')
    ->setArgument('source',
<<<SCRIPT
:log info "ap 1.1.1.1 down";
:log info "ap 2.2.2.2 down";
SCRIPT
."\r\n".
<<<SCRIPT
:log info "ap 1.1.1.3 down";
:log info "ap 2.2.2.4 down";
SCRIPT
    );
Is there any workarround to solve this bug ?
With PEAR2 how can I handle if the script is added correcly i execute twice the script, scond run should show me that the script allready exists.

I don't know if with this API's solutions would be the best solution i need... i explai:
I have about 100 routers in differents places all of them with almost the same configuration. I'm developing a hotspot solution and sometimes I have to add a new rule on garden or add DNS or change things on all the routers but keep the rest of configuration.
Now I can connect all of them throw VPN from office.
I'm thinking on develop a PHP ROBOT than updetes automatically all the routers.
So I need a good error control.
The idea is to add in a script all the update and execut it.

Do you think this could be the right solution ?

Thanks
 
User avatar
boen_robot
Forum Guru
Forum Guru
Posts: 2400
Joined: Thu Aug 31, 2006 4:43 pm
Location: europe://Bulgaria/Plovdiv

Re: PHP API script add carriage return

Sat Feb 21, 2015 8:12 pm

That behavior is expected. The new lines ARE there (and RouterOS respects them when executing the script). They simply aren't visible in Winbox in particular.

If you want to ensure they'll be visible in Winbox, you need to normalize all newlines to "\r\n", as opposed to just "\n", which is what most PHP editors do with newlines. One easy way to do this normalization is with a regular expression, e.g.:
    ->setArgument('source',
        preg_replace('~\R~u', "\r\n",
<<<SCRIPT
:log info "ap 1.1.1.1 down";
:log info "ap 2.2.2.2 down";

:log info "ap 1.1.1.3 down";
:log info "ap 2.2.2.4 down";
SCRIPT
        )
    ); 
The idea is to add in a script all the update and execut it.
If you need to reuse the script later down the road again, adding it in the fashion you're doing (and then running it as needed) is the proffered way of doing it. If you want to only apply changes once though, there's three better ways to go about it.

1. If you are confident in your changes (e.g. you've already tested them in a lab environment and/or the changes are minor enough to not be error prone), you can use the Util class, and it's exec() method, e.g.
$util = new RouterOS\Util($client);
$util->exec('
:log info "ap 1.1.1.1 down";
:log info "ap 2.2.2.2 down";

:log info "ap 1.1.1.3 down";
:log info "ap 2.2.2.4 down";
'
); 
(See this page in the GitHub wiki for details on that)

You don't need to normalize new lines with that, since RouterOS accepts "\n" just fine, and you won't see the script in Winbox anyway.

2. If you aren't sure about a particular action, and you want to check if it succeeded, you should not have it in script, but with API instead. The API protocol allows you to inspect errors returned by RouterOS, at which point you can act accordingly. As long as the connection to the router is active, you can for example keep count of the previously successful commands, and on error, call the "/undo" command that many times to cancel the entire thing (OR take an alternative code path for that one command... it's up to you).

3. If your changes are so critical that they may disrupt the connection, then add a scheduler script that will execute itself after 1 second and remove itself as its first order of business (and then the rest of your script afterwards). Close the connection (by calling unset($client)) immediately afterwards to avoid an exception in PHP if the connection does go down indeed.

As part of that scheduler script, you should add another scheduler script that will modify the new state back to the original one after some more time (say, 1 minute). Before that time is up, PHP would reconnect, and perhaps verify the new settings, and if all checks out, remove the "cleanup" scheduled script before it executes.
 
oooscar
Frequent Visitor
Frequent Visitor
Posts: 84
Joined: Sun Jan 05, 2014 12:56 pm
Location: Spain
Contact:

Re: PHP API script add carriage return

Sun Feb 22, 2015 12:51 am

Thanks a lot !
The three options will help me.
Now i'm starting to program the first upgrade and get an error.
I have to upgrade a script line (will write all source script with exec) into all my routers.

1)
When I use local variables that must begin with $ into RouterOS script PHP interpretes even is into <<<SCRIPT <<< that
is a PHP variable and a warning appears
Notice: Undefined variable: routerSerial in D:\xampp\htdocs\apiMikrotik3\PEAR2_Net_RouterOS-1.0.0b5\examples\test.php on line 61

The result on the RouterOS script:


:if ([:len [/ip hotspot find where disabled and name=""]] > 0) do={

.....
$util->setMenu('/system/script');
$util->exec('set TEST_API source="'.
utf8_decode(
str_replace('"', '\"',
preg_replace('~\R~u', "\r\n",
<<<SCRIPT
:local AP "no";
:local routerSerial 43CE02D0ABAE;

/system script job remove numbers=[/system script job find where script ="swb_safe_fetch"];

#Mira si hotspot ACTIU
:local HotspotEstat "";
:if ([:len [/ip hotspot find where disabled and name="$routerSerial"]] > 0) do={
:set HotspotEstat "&HOTSPOT=desactivat";
} else={
:set HotspotEstat "&HOTSPOT=activat";
}
....
Thanks !!
 
User avatar
boen_robot
Forum Guru
Forum Guru
Posts: 2400
Joined: Thu Aug 31, 2006 4:43 pm
Location: europe://Bulgaria/Plovdiv

Re: PHP API script add carriage return

Sun Feb 22, 2015 1:11 am

There's no need for the whole wrapping, decoding and string replacement thing.

Just have the source directly in there.

And to avoid the warning, surround the source with either single quotes, or NEWDOC, e.g.
$util->exec('
:local AP "no";
:local routerSerial 43CE02D0ABAE;

/system script job remove numbers=[/system script job find where script ="swb_safe_fetch"];

#Mira si hotspot ACTIU
:local HotspotEstat "";
:if ([:len [/ip hotspot find where disabled and name="$routerSerial"]] > 0) do={
:set HotspotEstat "&HOTSPOT=desactivat";
} else={
:set HotspotEstat "&HOTSPOT=activat";
}
...
');
Also, I'm noticing some of these might be different from one router to the other (like, routerSerial?). If they are, consider placing them outside the script, reuse the source as a "template", which you fill with those values, e.g.
$source = '
/system script job remove numbers=[/system script job find where script ="swb_safe_fetch"];

#Mira si hotspot ACTIU
:local HotspotEstat "";
:if ([:len [/ip hotspot find where disabled and name="$routerSerial"]] > 0) do={
:set HotspotEstat "&HOTSPOT=desactivat";
} else={
:set HotspotEstat "&HOTSPOT=activat";
}
...
';

$routersInfo = array(
    array(
        'conn' => array('hostname1', 'username', 'password'),
        'params' => array(
            'ap' => 'no',
            'routerSerial' => '43CE02D0ABAE'
        )
    ),
    array(
        'conn' => array('hostname2', 'username', 'password'),
        'params' => array(
            'ap' => 'yes',
            'routerSerial' => 'DEADBEEFFADE'
        )
    ),
);

foreach ($routersInfo as $routerInfo) {
    $util = new RouterOS\Util(
        $client = new RouterOS\Client($routerInfo['conn'][0], $routerInfo['conn'][1], $routerInfo['conn'][2])
    );
    $util->exec($source, $routerInfo['params']);
}
The second variable in exec() adds the array keys as local variables, with the values being sanitized so that you don't accidently cause code injection.
 
oooscar
Frequent Visitor
Frequent Visitor
Posts: 84
Joined: Sun Jan 05, 2014 12:56 pm
Location: Spain
Contact:

Re: PHP API script add carriage return

Sun Feb 22, 2015 2:02 am

That sounds good.... but where I idicate to set TEST_API source on your example ?

$util->setMenu('/system/script');
$util->exec('set TEST_API source="'. .....

What do you mean: single quotes, or NEWDOC ?
 
User avatar
boen_robot
Forum Guru
Forum Guru
Posts: 2400
Joined: Thu Aug 31, 2006 4:43 pm
Location: europe://Bulgaria/Plovdiv

Re: PHP API script add carriage return

Sun Feb 22, 2015 2:06 am

That sounds good.... but where I idicate to set TEST_API source on your example ?

$util->setMenu('/system/script');
$util->exec('set TEST_API source="'. .....
You don't.

The very idea of exec() is to automatically add a script (with a randomly generated name, to avoid potential conflicts with real scripts), run it, and remove it right afterwards. It abstracts away all that (as well as variables, as in the example above), so that you can concentrate on the source of the script itself.
What do you mean: single quotes, or NEWDOC ?
Single quoted string
'/system/script'
Same string with NEWDOC notation:
<<<'SCRIPT'
/system/script
SCRIPT;
NEWDOC is similar to HEREDOC, i.e.
<<<SCRIPT
/system/script
SCRIPT;
(notice the missing single quotes around "SCRIPT")

but it doesn't interpolate variables, just like how single quotes don't.
 
oooscar
Frequent Visitor
Frequent Visitor
Posts: 84
Joined: Sun Jan 05, 2014 12:56 pm
Location: Spain
Contact:

Re: PHP API script add carriage return

Tue Feb 24, 2015 3:34 pm

Ok,
then i can't use exec because the first thing I have to do to all may routers is upgrade an script (add a line to this script).

The idea should be to delete the old script and create a new one with las version.

I have still having problems with the variables php and router_os_scripts ($) :

http://forum.mikrotik.com/viewtopic.php ... 57#p470266

Any suggest ?
Thanks a lot for your help.
 
User avatar
boen_robot
Forum Guru
Forum Guru
Posts: 2400
Joined: Thu Aug 31, 2006 4:43 pm
Location: europe://Bulgaria/Plovdiv

Re: PHP API script add carriage return

Tue Feb 24, 2015 5:40 pm

Well, you already had it right before... It's just that to modify an existing script, you need to use "set", and not "add", i.e.
$setRequest = new RouterOS\Request('/system script set');
$setRequest
    ->setArgument('numbers', 'TEST_API')
    ->setArgument('source',
        preg_replace('~\R~u', "\r\n", '
:log info "ap 1.1.1.1 down";
:log info "ap 2.2.2.2 down";

:log info "ap 1.1.1.3 down";
:log info "ap 2.2.2.4 down";
'
        )
    );
$client->sendSync($setRequest); 
That would modify an existing script called "TEST_API", without running it. To then run it, you'd need to call the "run" command, i.e.
$client->sendSync(new RouterOS\Request('/system script run number=TEST_API')); 
 
oooscar
Frequent Visitor
Frequent Visitor
Posts: 84
Joined: Sun Jan 05, 2014 12:56 pm
Location: Spain
Contact:

Re: PHP API script add carriage return

Mon Mar 02, 2015 12:09 am

I'm getting the error:
Fatal error: Maximum execution time of 30 seconds exceeded in D:\xampp\htdocs\apiMikrotik3\PEAR2_Net_RouterOS-1.0.0b5\src\PEAR2\Net\RouterOS\Communicator.php on line 222
when executing
$util = new RouterOS\Util($client = new RouterOS\Client('192.168.90.253', xxx,xxx));
....
$addRequest = new RouterOS\Request('/system script set');

$addRequest
		->setArgument('numbers', 'Sinc')
		->setArgument('source',
		 preg_replace('~\R~u', "\r\n",$Sinc
));

$client->sendSync($addRequest,$responseHandler);
echo 'OK Sinc <br>';
	
$util->setMenu('/ip hotspot walled-garden');
$util->exec('remove numbers=[/ip hotspot walled-garden find ]');
echo 'OK walled-garden remove <br>';
   
And woks PERFECT like this (exec first and then request):
$util = new RouterOS\Util($client = new RouterOS\Client('192.168.90.253', xxx,xxx));
....
$util->setMenu('/ip hotspot walled-garden');
$util->exec('remove numbers=[/ip hotspot walled-garden find ]');
echo 'OK walled-garden remove <br>';

$addRequest = new RouterOS\Request('/system script set');

$addRequest
		->setArgument('numbers', 'Sinc')
		->setArgument('source',
		 preg_replace('~\R~u', "\r\n",$Sinc
));

$client->sendSync($addRequest,$responseHandler);
echo 'OK Sinc <br>';
	

   
I need fist to upgrade script and then execute some commands ....

How to do it ?

Thanks !
 
User avatar
boen_robot
Forum Guru
Forum Guru
Posts: 2400
Joined: Thu Aug 31, 2006 4:43 pm
Location: europe://Bulgaria/Plovdiv

Re: PHP API script add carriage return

Mon Mar 02, 2015 12:44 am

First things first...
sendSync() doesn't accept callbacks. The $responseHandler argument is completely ignored here. It certainly shouldn't break things though.


I can't seem to replicate the problem (even with the callback in there; it's ignored as expected, and the script runs and finished normally)...

There may be something else in your code (in the "..." part) that is causing this. Or it's some problem with a specific version of PHP or RouterOS.

Here's the exact ENTIRE test file I tried
<?php
use PEAR2\Net\RouterOS;
require_once 'PEAR2_Net_RouterOS-1.0.0b5.phar';

$util = new RouterOS\Util($client = new RouterOS\Client('ros.example.com', 'apifull', 'apifull'));

echo 'Connected<br>';
$Sinc = '
:put "LINE1";
:put "LINE2";
';
$responseHandler = function() {};

$addRequest = new RouterOS\Request('/system script set');

$addRequest
      ->setArgument('numbers', 'Sinc')
      ->setArgument('source',
       preg_replace('~\R~u', "\r\n",$Sinc
));

$client->sendSync($addRequest,$responseHandler);
echo 'OK Sinc <br>';
   
$util->setMenu('/ip hotspot walled-garden');
$util->exec('remove numbers=[/ip hotspot walled-garden find ]');
echo 'OK walled-garden remove <br>';
The router is a RouterOS 6.27 x86 VM with a pre-existing empty script called "Sinc" (added for the test), and the PHP version is 5.6.4 on Windows Server 2008 R2.

What's your setup (versions and all)?
 
User avatar
boen_robot
Forum Guru
Forum Guru
Posts: 2400
Joined: Thu Aug 31, 2006 4:43 pm
Location: europe://Bulgaria/Plovdiv

Re: PHP API script add carriage return

Mon Mar 02, 2015 1:34 am

Trying to probe further, I've found a different, yet possibly related issue.

For some reason, exec() hangs when used in conjunction with charset conversion, which is in fact what the error message you're seeing alludes to. Line 222 of Communicator.php is inside iconvStream() - a method only called when charset conversion is used. For some reason, it hangs on that line... or is in an infinite loop around it, I don't yet fully understand why this hang is happening.

The hang happens regardless of whether exec() is first or second though, so this is not the exact same issue you're experiencing, but again - probably related.

I'll investigate further, but in the mean time, just remove any use of charset conversion (i.e. any calls to Client::setCharset()... that's probably somewhere inside the "..." part, right?). If you really need it, use the iconv() function around the source of exec() (similarly to preg_replace()).
 
oooscar
Frequent Visitor
Frequent Visitor
Posts: 84
Joined: Sun Jan 05, 2014 12:56 pm
Location: Spain
Contact:

Re: PHP API script add carriage return

Sun Mar 15, 2015 7:41 pm

Yes,
it was charset.

But can't make working iconv.

with this i send my script and comments with correct language:
$client->setCharset(
		array(
			RouterOS\Communicator::CHARSET_REMOTE => 'windows-1252',
			RouterOS\Communicator::CHARSET_LOCAL  => 'UTF-8'
		)
	);
but if i change setCharset to:

$_SwB_Sinc=iconv("UTF-8", "windows-1252",$_SwB_Sinc);

it won't set the new script:
$addRequest = new RouterOS\Request('/system script set');

	$addRequest
		->setArgument('numbers', '_SwB_Sinc')
		->setArgument('source',
		 preg_replace('~\R~u', "\r\n",$_SwB_Sinc
	));

	$client->sendSync($addRequest,$responseHandler);
without iconv sets ok the script with wrong charset.

I set header php like:

header('Content-Type: text/html; charset=utf-8');

What do you recomend ?

Thanks
 
User avatar
boen_robot
Forum Guru
Forum Guru
Posts: 2400
Joined: Thu Aug 31, 2006 4:43 pm
Location: europe://Bulgaria/Plovdiv

Re: PHP API script add carriage return

Mon Mar 16, 2015 1:06 am

Try to do the charset conversion after the line normalization. The line normalization needs the input to be UTF-8, so if you're already making the input windows-1252, that might be making preg_replace() fail, and cause problems from there.

i.e.
   $addRequest
      ->setArgument('numbers', '_SwB_Sinc')
      ->setArgument('source',
          iconv('UTF-8', 'windows-1252', preg_replace('~\R~u', "\r\n", $_SwB_Sinc))
      );
Also, your PHP file needs to actually be UTF-8 encoded "for real". Check with Notepad's "Save as..." dialog if you're not sure.
 
User avatar
boen_robot
Forum Guru
Forum Guru
Posts: 2400
Joined: Thu Aug 31, 2006 4:43 pm
Location: europe://Bulgaria/Plovdiv

Re: PHP API script add carriage return

Mon Mar 16, 2015 9:15 pm

On second thought, Client::setCharset() applies changes immediately, so you can simply turn it off before doing exec(), so:
   $client->setCharset(
      array(
         RouterOS\Communicator::CHARSET_REMOTE => 'windows-1252',
         RouterOS\Communicator::CHARSET_LOCAL  => 'UTF-8'
      )
   );
   $addRequest = new RouterOS\Request('/system script set');

   $addRequest
      ->setArgument('numbers', '_SwB_Sinc')
      ->setArgument('source',
       preg_replace('~\R~u', "\r\n",$_SwB_Sinc
   ));

   $client->sendSync($addRequest);
   echo 'OK Sinc <br>';
  
   $client->setCharset(null);
   $util->setMenu('/ip hotspot walled-garden')->exec('remove [find]');
   echo 'OK walled-garden remove <br>'; 
Or you can avoid the problem entirely by not using exec() at all... In your case, you only want to remove all items in the walled garden, so you can just use Util's remove() method, i.e.:
   $client->setCharset(
      array(
         RouterOS\Communicator::CHARSET_REMOTE => 'windows-1252',
         RouterOS\Communicator::CHARSET_LOCAL  => 'UTF-8'
      )
   );
   $addRequest = new RouterOS\Request('/system script set');

   $addRequest
      ->setArgument('numbers', '_SwB_Sinc')
      ->setArgument('source',
       preg_replace('~\R~u', "\r\n",$_SwB_Sinc
   ));

   $client->sendSync($addRequest);
   echo 'OK Sinc <br>';
  
   $util->setMenu('/ip hotspot walled-garden')->remove();
   echo 'OK walled-garden remove <br>';
(that last one is also a little more efficient, since an exec() needs to create, run and remove a script, while remove() calls "remove" on the current menu over the API protocol, and nothing more... Well, in the case of zero arguments like above, it also calls a "find" command to get the IDs, but that's still just 2 API commands, instead of 3)

And while we're on the subject of Util's CRUD methods, you can also use its set() method for the script, e.g.
   $client->setCharset(
      array(
         RouterOS\Communicator::CHARSET_REMOTE => 'windows-1252',
         RouterOS\Communicator::CHARSET_LOCAL  => 'UTF-8'
      )
   );

   $util->setMenu('/system script')->set(
      '_SwB_Sinc',
      array(
         'source' => preg_replace('~\R~u', "\r\n", $_SwB_Sinc)
      )
   );
   echo 'OK Sinc <br>';
  
   $util->setMenu('/ip hotspot walled-garden')->remove();
   echo 'OK walled-garden remove <br>';  
 
oooscar
Frequent Visitor
Frequent Visitor
Posts: 84
Joined: Sun Jan 05, 2014 12:56 pm
Location: Spain
Contact:

Re: PHP API script add carriage return

Fri Mar 20, 2015 2:18 pm

Good !!
with the first solution was working
   $addRequest
      ->setArgument('numbers', '_SwB_Sinc')
      ->setArgument('source',
          iconv('UTF-8', 'windows-1252', preg_replace('~\R~u', "\r\n", $_SwB_Sinc))
      );
 
But i like the delete part.

Yes after doing to many exec calls it goes a bit slow. Even if i have to work this scripts to 50 routers what happens with PHP timeout ? I haven't test yet but usally PHP after 50 seconds will finish code with tiemout ? How can I handle this ?

This is las part of the code :
$dhcp_server_add_dns_server='/ip dhcp-server network set dns-server=8.8.8.8,8.8.4.4 [find comment="hotspot network"]';

$hs_interim='/ip hotspot profile set radius-interim-update=6m [find dns-name=hotspot.xxxxxxx.com]';

$wlan_country='/interface wireless set country="spain" [find name="wlan1"]';

$new_version='/interface bridge set comment="xxx_v18.3" [find name="bridge-hs"]';

		/* DNS Static + Proxy */
		$util->exec($dhcp_server_add_dns_server);
		echo 'OK dhcp-server hotspot add dns_server 8.8.8.8<br>';

		/* Interim Radius */
		$util->exec($hs_interim);
		echo 'OK hotspot interim<br>';
		
		/* wlan country */
		$util->exec($wlan_country);
		echo 'OK wlan country<br>';
		
		/* DNS Static + Proxy */
		$util->exec($new_version);
		echo 'OK newV_version<br>';	
		
		/* Executa Scripts Garden */
		$client->sendSync(new RouterOS\Request('/system script run number=_Swb_Startup'));
		echo 'OK startup<br>';	



Thanks again !!
 
User avatar
boen_robot
Forum Guru
Forum Guru
Posts: 2400
Joined: Thu Aug 31, 2006 4:43 pm
Location: europe://Bulgaria/Plovdiv

Re: PHP API script add carriage return

Fri Mar 20, 2015 3:37 pm

By default, PHP has a timeout of 30 seconds per HTTP request.

One way to avoid this is to run PHP from the command line, where there are no limits at all.

Alternatively (i.e. if you want a web application, as PHP intended), you could divide the work across HTTP requests, and make each PHP request redirect to the next, which would redirect to the next, and so on, until the last one redirects to a "success" message. I think there are libraries out there that try to abstract this away, so that you simply assign a callback to be ran for each request... But I've never actually used any myself, so I can't recommend a particular one.

If each router takes less than 30 seconds to configure, then it would be simplest to simply run each router in a separate PHP request. So something like:
//File: apply.php
$routers = array(
    array('hostname1', 'admin', 'password'),
    array('hostname2', 'admin', 'password'),
    array('hostname3', 'admin', 'password')
);
$target = isset($_GET['router']) && isset($routers[$_GET['router']]) ? $routers[$_GET['router']] : null;
if (null === $target) {
    header('Location: ok.php');//Success page
    exit();
}

$client = new RouterOS\Client($target[0], $target[1], $target[2]);

//
//Apply settings on this router here
//

//Ask the browser to make a new HTTP request, targeting the next router
header('Location: apply.php?router=' . (1+(int)$_GET['router']));
exit(); 
(when actually running this, you'd need to run "apply.php?router=0")

Yes after doing to many exec calls it goes a bit slow.
In general, it's better to either combine everything into as few exec() calls as possible, or not use exec() to begin with.

So either:
$script = '
/ip dhcp-server network set dns-server=8.8.8.8,8.8.4.4 [find comment="hotspot network"]
/ip hotspot profile set radius-interim-update=6m [find dns-name=hotspot.xxxxxxx.com]
/interface wireless set country="spain" [find name="wlan1"]
/interface bridge set comment="xxx_v18.3" [find name="bridge-hs"]
';
$util->exec($script);
echo 'OK script'; 
or
$util->setMenu('/ip dhcp-server network')->set(
    RouterOS\Query('comment', 'hotspot network'),
    array('dns-server' => '8.8.8.8,8.8.4.4')
);
$util->setMenu('/ip hotspot profile')->set(
    RouterOS\Query('dns-name', 'hotspot.xxxxxxx.com'),
    array('radius-interim-update' => '6m')
);
$util->setMenu('/interface wireless')->set('wlan1', array('country' => 'spain'));
$util->setMenu('/interface bridge')->set('bridge-hs', array('comment' => 'xxx_v18.3')); 
 
oooscar
Frequent Visitor
Frequent Visitor
Posts: 84
Joined: Sun Jan 05, 2014 12:56 pm
Location: Spain
Contact:

Re: PHP API script add carriage return

Sun Mar 22, 2015 11:46 am

Hi,
i will check wich is the best if running batch PHP or web PHP and prevent timeout. Will share when donne.

Now I relise that first of all i have to upgrade more tan 100 rb951 to routerOS 6.25 before upgrade my scripts. I have 6.25 on my server...
I use:
/tool fetch url=http://www.xxxx.com/yyyy/routeros-mipsbe-6.25.npk

but how you would do to upgrade automatically all of them?

First i have to download the npk
Then maybe a job at 2.00am to reboot all routers and then upgrade firmware ..

Have you donne some similar or tell me the best option ?

Thanks !
 
User avatar
boen_robot
Forum Guru
Forum Guru
Posts: 2400
Joined: Thu Aug 31, 2006 4:43 pm
Location: europe://Bulgaria/Plovdiv

Re: PHP API script add carriage return

Sun Mar 22, 2015 2:23 pm

Have you donne some similar or tell me the best option ?
I haven't, but here are two ideas:

1.
Using PHP, upload the NPK to each router (ideally via FTP), and then via API, schedule a script which will remove itself first, and then do a reboot... Don't forget that first part, or you risk making the routers reboot continuously.

The schedule script needs to start at minimum 2 seconds after the file upload is finished, as RouterOS takes that much to actually write the file to HDD (it's in memory before that).

So f.e.
copy('routeros-mipsbe-6.25.npk', 'ftp://admin:password@router/routeros-mipsbe-6.25.npk');
$util->setMenu('/system scheduler')->add(
    array(
        'name'=>'REBOOT',
        'interval'=> '4s',
        'on-event' => '/system scheduler remove REBOOT;/system reboot'
    )
);

//Disconnect immediately; If the router has already rebooted by the time you disconnect, there will be an exception
unset($util, $client);
2.
If you'd prefer to have the latest version you can just issue
$client->sendSync(
    new RouterOS\Request('/system package update upgrade once=""')
);
$util->setMenu('/system scheduler')->add(
    array(
        'name'=>'REBOOT',
        'interval'=> '2s',
        'on-event' => '/system scheduler remove REBOOT;/system reboot'
    )
);

//Disconnect immediately; If the router has already rebooted by the time you disconnect, you'd get an exception
unset($util, $client);
 
oooscar
Frequent Visitor
Frequent Visitor
Posts: 84
Joined: Sun Jan 05, 2014 12:56 pm
Location: Spain
Contact:

Re: PHP API script add carriage return

Sat Mar 28, 2015 12:08 pm

Hi again,
I found this that makes really easy to upgrade lots of routers with no timeout.... Works great and is really easy to adapt to API calls.
http://stackoverflow.com/questions/5533 ... er-timeout

I upgrade more than 100 routers to 6.25 and firmware as well:

Resume:
		$responses = $client->sendSync(new RouterOS\Request('/system/routerboard/print'));
		$rb_Model=$responses->getProperty('model');
		
		if ($rb_Model=="951Ui-2HnD" || $rb_Model=="2011UiAS-2HnD" || $rb_Model=="1100AHx2" || $rb_Model=="951-2n"){
			if ($rb_Model=="951Ui-2HnD" || $rb_Model=="2011UiAS-2HnD" || $rb_Model=="951-2n") 
				$client(new RouterOS\Request('/tool fetch url=http://www.aaaaaaa.com/ros/routeros-mipsbe-6.25.npk'));
			if ($rb_Model=="1100AHx2") 
				$client(new RouterOS\Request('/tool fetch url=http://www.aaaaaaa.com/ros/routeros-powerpc-6.25.npk'));
					
			
			$util->setMenu('/system scheduler')->add(
			array(
					'name'=>'REBOOT',
					'interval'=> '60s',
					'on-event' => '/system scheduler remove REBOOT;/system reboot'
					)
			);
			$util->setMenu('/system scheduler')->add(
			array(
					'name'=>'START_FIRM',
					'start-time'=> 'startup',
					'on-event' => '/system scheduler remove START_FIRM;
:if ([/system routerboard get current-firmware] != [/system routerboard get upgrade-firmware]) do={
:log error "Bios need upgrade";
/system script add name="upgrade" source="/system routerboard upgrade";
/system script run upgrade;
:delay 5;
/system script remove [find name=upgrade];
:delay 5;
/system reboot} else={:log warning "Nothing"}'
					)
			);
That Works really well only some timeout on clients that connection is really slow.

Then I upgrade my scripts:
....
$all='
/ip firewall nat 
.....
/ip firewall filter
....	
/ip dns static
add address=.....

/ip proxy
set enabled=yes

/ip proxy access
....
/ip dhcp-server network set dns-server=8.8.8.8,8.8.4.4 [find comment="hotspot network"]

/ip hotspot profile set radius-interim-update=6m [find dns-name=hotspot.aaaaaa.com]

/interface wireless set country="spain" [find name="wlan1"]

/interface bridge set comment="v18.3" [find name="bridge-hs"]

/ip dns cache flush

/system leds set [find leds=led1] interface=ether1
';

....

			/* Borra Garden + Garden IP */
			$util->setMenu('/ip hotspot walled-garden');
			$util->exec('remove numbers=[/ip hotspot walled-garden find ]');
			//echo 'OK walled-garden remove <br>';

			$util->setMenu('/ip hotspot walled-garden ip');
			$util->exec('remove numbers=[/ip hotspot walled-garden ip find ]');
			//echo 'OK walled-garden ip remove <br>';	
			
			/* Executa Scripts Garden */
			$client->sendSync(new RouterOS\Request('/system script run number=s_Apple'));
			$client->sendSync(new RouterOS\Request('/system script run number=s_Global_Garden')); 
			//echo 'OK Executat s_Global_Garden i s_Apple<br>';	
			
			/* all */
			$util->exec($all);

In most of the cases Works ok. But i found 10 or more routers than EXECUTE ok before
/* all */
$util->exec($all);

But this last exec (with more than 10 sentences) is not executing. Then I copy these 10 sentences to the router manually and Works ok. So is not an error on the sentences than makes the script not running.

How I can handle this. I think:
1) Maybe some safe mode and commit.
2) Some error control to the exec ? I have the code into try catch while running this exec and any catch.

Thanks.
 
User avatar
boen_robot
Forum Guru
Forum Guru
Posts: 2400
Joined: Thu Aug 31, 2006 4:43 pm
Location: europe://Bulgaria/Plovdiv

Re: PHP API script add carriage return

Sat Mar 28, 2015 1:12 pm

But this last exec (with more than 10 sentences) is not executing. Then I copy these 10 sentences to the router manually and Works ok. So is not an error on the sentences than makes the script not running.
That exec() script might be on the edge of acceptable script length... Above 4096 bytes, RouterOS simply refuses to run any scripts in "/system script", be they from API or directly from that menu. exec() also adds the menu and the script's name as a variable, so even if your actual script is under 4096 bytes, if it's close (like, let's say 4060 bytes), that extra data probably puts it over the edge.

File import does not have that restriction... But the problem with doing that is that file writes are extremely slow (2s at least), especially over the API (where it's 4 seconds; 2 for the write, 2 for the initial file creation).

Considering the large scale you're already on, that performance penalty is probably acceptable, so... try to do this:
$util->filePutContents('script.rsc', $all);
$client->sendSync(new RouterOS\Request('/import file-name=script.rsc'));
$util->filePutContents('script.rsc', null);//remove the file    
1) Maybe some safe mode and commit.
The "safe mode", as seen in Winbox/CLI, has been proposed before, and while MikroTik have acknowledged it should be implemented eventually, they haven't committed to a particular release, as it is apparently difficult to re-implement this for the API protocol.

I see no way to reliably and easily work around this...
2) Some error control to the exec ? I have the code into try catch while running this exec and any catch.
The return value of exec() contains the API responses during the script run, which should contain errors, if there were any. However, the error messages aren't much useful for runtime errors, as they don't point you to the line that caused the runtime error, and the script just stops after whatever that mysterious line was.

It's better to do such error handling within the script itself, using the ":do on-error=" construct, e.g.
:do {
#try to configure the router
} on-error={
/log warning "SCRIPT: Error during configuration attempt";
}
and then read the log, searching for your error messages (in this case, one starting with "SCRIPT:").

If it was a syntax error (or an error before the entire run), you can see it in the return value of exec(), with line numbers and such... Though keep in mind line numbers are off by at least 2 lines (due to the menu and script name being prepended to the script).

Who is online

Users browsing this forum: No registered users and 45 guests