API - Problem trying get pools

Hi!

I’m trying get IP pools from API, when I try from terminal I get:

[admin@mktk] > ip pool print 
 # NAME                                                                                                                     RANGES                         
 0 pool_red_slave                                                                                                           192.168.3.100-192.168.3.200
 1 pool_red_slave2                                                                                                          192.168.4.100-192.168.4.200
 2 pool_privadas                                                                                                            172.16.1.2-172.16.1.254        
 3 pool_prueba                                                                                                              10.10.10.100-10.10.10.200      
 4 pool_DVR                                                                                                                 192.168.2.100-192.168.2.200

When I try with PEAR2_Net_RouterOS PHP command ‘/ip/pool/print’ I get only the first result:

.id “*1”
name “pool_red_slave”
next-pool “pool_red_slave2”
ranges “192.168.3.100-192.168.3.200”

Routerboard is RB1100Hx2 v5.20

Someone got the same behavior?

How does your PHP code look like?

I suspect you may be using the ability to directly read the first response, as opposed to iterating over all responses.

Thanks Boen_robot!

// $vars contain all login data
$response = test($vars, '/ip/pool/print');

function test($nas_access_data, $commands){
	$response = array("error" => 0);
	$login = mktk_login($nas_access_data);

	if($login['error'] == 0){
		$Request = new \PEAR2\Net\RouterOS\Request($commands);				
		$result = $login['client']->sendSync($Request);
		$response[$commands] = $result->getAllArguments();	
	}
	else $response = $login;
	return $response;
}

Right. Just as I thought.

$result contains all responses, but

$result->getAllArguments();

tells you all arguments of the first response, not all arguments of all responses.

To get all of them, you need to actually iterate over the responses or seek the collection.

For example:

      $result = $login['client']->sendSync($Request);
      $response[$commands] = array();
      foreach ($result as $item) {   
        $response[$commands][] = $item->getAllArguments();
      }
    

would make $response[‘/ip/pool/print’] a numeric array, where each member is an array of all arguments for that particular response. In other words, $response[‘/ip/pool/print’][0] will contain the arguments for the first pool, $response[‘/ip/pool/print’][1] for the second pool, etc.

Thanks Boen_robot!

My code works fine without modifications with getAllArguments() in cases where only return arguments:

/system/resource/print
/ppp/active/print count-only
/ip/pool/used/print count-only
/interface/monitor-traffic interface=ether1 once

But, for the case of commands that return a collection (as ‘/ip/pool/print’) I should iterate over the response.
Then, how can I know in advanced, wich kind of response I will get?

If I known this I will do this (pseudocode):

$result = $login['client']->sendSync($Request);
if($result is collection){
	//if is collection type iterate
      foreach ($result as $item) {   
            $response[$commands][] = $item->getAllArguments();
      }
}
else{
	// if not...
	$response[$command] = $result->getAllArguments();
}

How can I know if the response is a collection or not?
Is possible?

All responses are a collection of at least 1 response, so it’s safe to unconditionally iterate over everything.

All examples you gave are ones where there’s exactly 1 response in the collection. One that contains everything you need in fact, which is why calling getAllArguments() works as expected with them. The primary reason why iteration is not required is exactly due to cases like those.

You can check the count by calling the count() function over the collection, e.g.

if(count($result) > 1) {
//You MUST iterate
} else {
//You don't have to, but you COULD iterate if you want to
} 

First, thank for your time here and in PEAR API package development, is very useful for me and other people.

Good point, but this logic not always work.
Some commands count() return 1 or 2 with only 1 result, for example:

/system/resource/print
responseTypes = array(
	[0] =>'!re'
	[1] =>'!done'
)
count = 2

/ppp/active/print count-only
responseTypes = array(
	[0] =>'!done'
) 
count = 1

/ip/pool/used/print count-only
responseTypes = array(
	[0] =>'!done'
)
count = 1


/interface/monitor-traffic interface wan_ether1 once
responseTypes = array(
	[0] =>'!re'
	[1] =>'!done'
)
count = 2

/ip/pool/print
responseTypes = array(
	[0] =>'!re'
	[1] =>'!re'
	[2] =>'!re'
	[3] =>'!re'
	[4] =>'!re'
	[5] =>'!done'
) 
count = 6

Why some commands return !re+!done and others only !done?

Final solution, code proposed for you with a little modification.


			$result_count = count($result);
			if($result_count == 1){
				$response[$command] = $result->getAllArguments();
			}
			else if($result_count > 1){
				foreach ($result as $item) {
					if($item->getType() == "!re"){
						$response[$command][] = $item->getAllArguments();
					}
				}
			}

Anyway, I’m still interested to know why some commands return !re+!done and others only !done :smiley:

Thanks!

First, thank for your time here and in PEAR API package development, is very useful for me and other people.

Appreciated. Thanks :slight_smile: .

Anyway, I’m still interested to know why some commands return !re+!done and others only !done

Well, it’s a MikroTik decision (as opposed to mine), that’s for sure… I can only speculate for their rationale.

Typically, the reason is if the command MAY return zero or more than one item, but that’s obviously not the case with “/system/resource/print”.

I’m guessing forwards compatibility - In the case of “print”, you’re explicitly specifying a mode of operation that is known to always return a single value (so they can afford to switch from multiple !re responses to a single !done one), but with resources, perhaps they might decide to later change the way it works, so that there are multiple data responses (e.g. one per “group”, as seen in Winbox), and some of the stuff we see now will remain at the first response (e.g. the version section), and the responses you get now would be compatible with the responses then.


BTW, there’s no way a response collection could be less than 1, so you may as well simplify your code. Further, if you’re going to filter by type, you can use the collection’s getAllOfType() method, e.g.

         if(count($result) == 1){
            $response[$command] = $result->getAllArguments();
         }
         else {
            $response[$command] = array();
            foreach ($result->getAllOfType('!re') as $item) {
                  $response[$command][] = $item->getAllArguments();
            }
         } 

Note: With both your code and this one, in a case of error, an empty array will be returned, which may not be what you want; If you need to check for errors, check $result->getAllOfType(‘!trap’)

thanks for the explanation!