sobota, 12 listopada 2011

Cacti, hex-y i inne stringi

Kilka tygodni temu zrobiłem upgrade Cacti do 0.8.7h z jakiegoś wcześniejszego wydania (któraś wcześniejsza literka w alfabecie). Na pierwszy rzut oka wszystko było ok, do momentu kiedy ludzie zaczęli zgłaszać, że wykresy się nie rysują, albo się źle rysują. Co ciekawe, problemy dotyczyły tylko wykresów związanych z MySQL-em oraz MongoDB.
Pierwsza myśl -> coś się zwaliło w skrypcie. Co prawda dwa różne skrypty, no ale. Odpalenie z ręki każdego skryptu i zonk, wszystko chodzi prawidłowo, nawet wartości są ok. No to w cacti uruchomienie trybu debug i zaczęły się dziać cuda. Skrypt wykonuje się poprawnie, w wyniku zwraca poprawną wartość, zapamiętywana odpowiedź jest poprawna, a do rrd zapisywana jest wartość z czapy, na pierwszy rzut oka totalnie pokręcona nie mająca specjalnie związku z poprawnym wynikiem. Znajomość moja php nie jest raczej na wysokim poziomie, ale
 echo 'dupa'; 
napisać potrafię, co do dalszego szukania błędu jest wystarczającą umiejętnością.

Koniec końców trafiłem na kawałek takiego kodu
                        [...]
                        /* special case of one value output: hexadecimal to decimal conversion */
                        }elseif (is_hexadecimal($value)) {
                                /* attempt to accomodate 32bit and 64bit systems */
                        [...]
,gdzie is_hexadecimal wyraźnie nie tak się zachowuje, tzn w starej wersji wyglądało to tak:
/* is_hexadecimal - test whether a string represents a hexadecimal number,
     ignoring space and tab, and case insensitive.
   @arg $hexstr - the string to test
   @arg 1 if the argument is hex, 0 otherwise, and FALSE on error */
function is_hexadecimal($hexstr) {
        return preg_match('/^[a-fA-F0-9 \t]*$/', $hexstr);
}
W nowej wersji wygląda to tak:
/* is_hexadecimal - test whether a string represents a hexadecimal number,
     ignoring space and tab, and case insensitive.
   @arg $hexstr - the string to test
   @arg 1 if the argument is hex, 0 otherwise, and FALSE on error */
function is_hexadecimal($hexstr) {
        return preg_match('/^[a-fA-F0-9: \t]*$/', $hexstr);
}
Różnica dość subtelna, ale ':' powoduje, że wynik typu 'cd: 3' jest interpretowany jako hex i rezultacie staje się liczbą 3283 zamiast po prostu 3.

Jest co prawda zgłoszony jest bug, ale rozwiązanie zaproponowane:
/* is_hexadecimal - test whether a string represents a hexadecimal number,
     ignoring space and tab, and case insensitive.
   @arg $hexstr - the string to test
   @arg 1 if the argument is hex, 0 otherwise, and FALSE on error */
function is_hexadecimal($hexstr) {
        $hexstr = trim($hexstr);
        $i      = 0;
        $length = strlen($hexstr);
        while ($i < $length) {
                $part = substr($hexstr,$i,2);
                $i += 2;

                if (!preg_match('/[a-fA-F0-9]/', $part)) {
                        return false;
                } elseif ($i < $length) {
                        if (substr($hexstr,$i,1) != ":") {
                                return false;
                        }elseif ($i + 1 == $length) {
                                return false;
                        }
                        $i++;
                }else{
                        $i++;
                }
        }

        return true;
}
, nadal nie działa. W efekcie kolega programista zasugerował, aby funkcja wyglądała tak:
/* is_hexadecimal - test whether a string represents a hexadecimal number,
     ignoring space and tab, and case insensitive.
   @arg $hexstr - the string to test
   @arg 1 if the argument is hex, 0 otherwise, and FALSE on error */
function is_hexadecimal($hexstr) {
        $hexstr = trim($hexstr);
        $i      = 0;
        $length = strlen($hexstr);
        while ($i < $length) {
                $part = substr($hexstr,$i,2);
                $i += 2;
                if(strlen($part) != 2) {
                        return false;
                }
                if (!preg_match('/[a-fA-F0-9]/', $part)) {
                        return false;
                } elseif ($i < $length) {
                        if (substr($hexstr,$i,1) != ":") {
                                return false;
                        }elseif ($i + 1 == $length) {
                                return false;
                        }
                        $i++;
                }else{
                        $i++;
                }
        }

        return true;
}
Teraz klucz:wartość będzie rozpoznawana prawidłowo, reszta stringów aa:bb:cc... będzie hex-em. Nadal nie rozwiązuje to problemu kiedy faktycznie mamy do czynienia z hex-em typu 'cd:02', no ale nie można mieć wszystkiego. W moim środowisku nigdzie nie odczytuje hexów, więc jest mi to obojętne.

Smutne, ale wychodzi brak trzymania się sztywnych reguł jak powinna wyglądać odpowiedź skryptu przez twórców cacti. Wystarczyły określić prostą regułę jak może nazywać się klucz, albo od jakich liter nie może się zaczynać lub też jaką musi mieć minimalną długość zamiast próbować tworzyć rozwiązania 'broken by design'.

Brak komentarzy: