How to create find function witch API + PHP script?

I want to create a script with this function

tool netwatch enable [find host~"192.168.1.12"]

[edit]Всъщност… колко зле си с английския?[/edit]

The API protocol doesn’t allow for regular expression filtering, but you can do such filtering on the PHP side.

Alternatively, you can abuse another characteristic that the API protocol does allow, which is to compare IP addresses as numbers. By the look of your regex, I assume you’re after IPs between 192.168.1.120 and 192.168.1.129, as well as the IP 192.168.1.12 itself.

Using my PHP client (see my signature), you can do it like so:

<?php
namespace PEAR2\Net\RouterOS;
require_once 'PEAR2_Net_RouterOS-1.0.0b3.phar';

$client = new Client('192.168.1.1', 'admin', 'password');

$printRequest = new Request(
    '/tool netwatch print .proplist=.id',
    Query::where('host', '192.168.1.120', Query::ACTION_GREATHER_THAN)
        ->andWhere('host', '192.168.1.129', Query::ACTION_LESS_THAN)
        ->orWhere('host', '192.168.1.120')
        ->orWhere('host', '192.168.1.129')
        ->orWhere('host', '192.168.1.12')
);

$idList = '';
foreach ($client->sendSync($printRequest)->getAllOfType(Response::TYPE_DATA) as $response) {
    $idList .= $response->getArgument('.id'), ',';
}
$idList = rtrim($idList, ',');

$enableRequest = new Request('/tool netwatch enable');
$enableRequest->setArgument('numbers', $idList);
$client->sendSync($enableRequest); 

А без PEAR2 може ли да стане?

Да. Хващаш PHAR файла и го include-ваш, както в кода горе. Всичко необходимо е вградено в самия PHAR. Не е задължително да имаш PEAR или PEAR2.


<< Translation for English speakers: >>



Като цяло измислих това и работи безотказно, но искам да попитам като бързодействие кое ще се представя по-добре?

$ARRAY = $API->comm('/tool/netwatch/print');

 for ($i=0; $i<250; $i++)
	{
		$regtable = $ARRAY[$i];
		$IP=$regtable['host'];
		$id=$regtable['.id'];
		if ($IP==$host) {
		$ARRAY = $API->comm("/tool/netwatch/enable", array(
    	'.id'=> ''.$id.'',));
		}
	}

И още един въпрос, как караш микротика да праща заявки, защото аз за целта използвам fetch-а?

Определено моя код ще е по-бърз - тука минаваш през всичко в netwatch-а, и го проверяваш, а при моя MikroTik-а ги проверява, след което PHP директно получава само необходимото.

А това, че така както си го написал само едно конкретно IP ще се търси (вместо всякакво IP започващо с 192.168.1.12), само междy другото.


А за пращане на (HTTP?) заявки… fetch-а разбира се. То няма друго.

<< Translation for English speakers: >>



Да не пускам нова тема искам да попитам: имаш ли идея как да направя скрипт в MT, който да записва нов ред в rsc файл?

Нещо подобно?

:local imeNaFaila "file.rsc"
/file set $imeNaFaila contents=([/file get $imeNaFaila contents] . "\nСъдържание на нов ред")

Самоче имай в предвид, че колкото по-голям става файла, толкова повече RAM от рутера ще ти точи това, тъй като цялото съдържание се зарежда преди да бъде допълнено. Ако искаш това да не става, ще трябва да използваш примерно PHP, където файла ще се тегли и допълва на уеб сървъра, след което резултатът ще се изпраща. В този вариант, вместо “повече RAM” ти е нужно повече време (заради прехвърлянето м-ду рутера и уеб сървъра).

Ако този файл представлява някакъв backup, май ще е най-добре ако просто правиш каквото ще правиш и пуснеш един export накрая.

<< Translation for English speakers: >>



Не идеята ми е в този файл да се записват всички недоставени fetch заявки, и евентуално да го пускам на определено време за преизпълнение

Тогава вероятно файла няма да става много голям => горното ще ти свърши работа.

По принцип аз бих използвал PHP с някаква база данни (където ще се пазят провалените заявки), след което да upload-вам каквото трябва в рутера. Даже ако ползваш API-то, upload-ване не е нужно. Просто използваш API-то да направиш fetch, и пазиш URL-а в базата при провал.

<< Translation for English speakers: >>



ОК, но как да разбере външният сървър, че заявката не е пристигнала?

Отговор винаги пристига всъщност. Въпроса е дали е отговора, който очакваш.

Ако status-а е finished, значи заявката е дала файл с код 200 (OK). При всякакви други обстоятелства, status-а е failed.

В API протокола има един бъг с fetch-а, че не прекъсва автоматично… с моя клиент лесно се “workaround”-ва това. Примерно:

<?php
namespace PEAR2\Net\RouterOS;
require_once 'PEAR2_Net_RouterOS-1.0.0b3.phar';

$client = new Client('192.168.1.1', 'admin', 'password');

$client->sendAsync(
    new Request('/tool fetch url="http://example.com/index.html" keep-result="yes"', null, 'f'),
    function ($response) {
        switch ($response->getArgument('status')) {
        case 'failed':
            //Запис в базата данни тук
            //Без break;
        case 'finished':
            return true;
        }
    }
);
$client->loop(); 

<< Translation for English speakers: >>



Това е ясно, но ще работи когато заявката за fetch се изпълнява от сървъра. А моята идея е да я изпълнява рутера

С горния метод, заявката се изпълнява от рутера. Сървъра просто кара рутера да изпълни заявката.

т.е. положението е
сървър → API заявка към рутер
рутер → HTTP заявка към външен сървър
външен сървър → HTTP отговор към рутер (предава съдържанието на файла)
рутер → API отговор към сървър (НЕ предава съдържанието на файла, а само статусът)

Разликата е единствено в това ТИ къде натискаш бутона/правиш график. Да, при горното ще трябва периодичното изпълнение да се настрои на сървъра, но самата заявка все пак ще се прави от рутера (и резултатът ще пристига директно там, вместо да минава през сървъра).

Би могъл вместо да настройваш график на сървъра, да настроиш рутера периодично да направи една HTTP заявка към сървъра, която от своя страна ще накара руера да изпълни всичко останало. т.е.
рутер → HTTP заявка към сървър
сървър → API заявка към рутер

По този начин хем рутера започва процедурата, хем имаш пълен контрол над цялата работа, докато си в PHP.


<< Translation for English speakers: >>



Да но точно идеята на целият код е при дадена ситуация рутера да изпраща заявка до сървъра

Каква е тая ситуация? Я дай пълния сценарий… може би има начин да стане по-елегантно.

<< Translation for English speakers: >>



Идеята ми е netwatch да следи състоянието на даден хост и при промяна в състоянието да изпраща заявка до web сървър, който да записва инфото в BD. Като целта е тази заявка винаги да достига до web сървъра, било то рано или късно.

Вместо да записваш целия URL, може просто да записваш датите в един файл (по един файл за всеки хост; всяка дата на нов ред), и безусловно да правиш заявка към сървъра. Сървъра от своя страна ще източва файла, попълвайки базата данни, след което ще го изчисти.

По този начин пак съществува потенциала за препълване на RAM-а, но вероятността за това става значително по-малка, тъй като размера на една дата е по-малка от цял URL съдържащ същата тази дата.

Интересно решение, които ми дава друга идея, че за да не се налага по някакъв начин периодично изпълнение на скрипт от Web сървъра. Може да го организирам нещата, така че при промяна в статуса на netwatch да се стартира скрипт, който да кара web сървъра да изтегли сам записания файл и да си обнови информацията?
Така би ли следвало да се използват най-малко ресурси?
И един малък въпрос, какво би станало ако по време на четене на файла се записва в него?

Е да де. Точно това предлагам и аз.

Част от отговорността на RouterOS като операционна система е автоматично да поставя ключалки за четене и писане - ако четенето дойде първо, писането чака четенето да свърши преди да пише и обратното.

Друг е въпроса, че между всяка операция, ключалката се освобождава, т.е. ако сървъра реши да изчисти файла точно в микросекундата между

[/file get $imeNaFaila contents]

и писането на нов ред, новият файл ще съдържа старите данни (плюс новият ред) вмесо да бъде изчистен.

При положение, че пишеш датите (включително времената до секунда), това не е голям проблем - правиш си базата данни да изисква уникално съдържание за всеки запис, при което следващото обновяване просто няма да запише старите времена повторно и евентуално вече ще изчисти файла.

От друга страна, на сървъра е хубаво да си вземеш файла чрез FTP, използвайки fopen() в “r+b” режим, с цел да придобиеш пишеща ключалка (т.е. да гарантираш, че четенето на данните няма да настъпи преди изчистването на файла).

В архива на клиента се съдържа една под-библиотека - Net_Transmitter - която може да подсигури комуникацията в случая.

Примерно:

<?php
require_once 'PEAR2_Net_RouterOS-1.0.0b3.phar';

$trans = new PEAR\Net\Transmitter\Stream($file = fopen('ftp://admin:password@192.168.1.1/192.168.1.12.txt', 'r+b'));
$mysqli = new mysqli('127.0.0.1', 'root', '', 'db');

$query = "INSERT INTO `failures` (`dateTime`) VALUES ";
try {
    while (true) {
        //21 === strlen("mmm/DD/YYYY-HH:MM:SS\n")
        $dateTime = DateTime::createFromFormat('M/j/Y-H:i:s', ucfirst(rtrim($trans->receive(21))))
            ->format('Y-m-d H:i:s');
        $query .= "('" . $mysqli->real_escape_string($dateTime) . "'), ";
    }
} catch (Exception $e) {
    //Приключихме с четенето на файла ИЛИ връзката е прекъснала по време на четенето.
}

$query = rtrim($query, ', ');
$mysqli->query($query);
ftruncate($file, 0);//Изчиства файла