[PHP] さくらインターネット リソース情報自動報告プログラム(PHP 版)

さくらインターネットのベースOS はFreeBSD。 また、とてもリソース管理が厳しく、 だからこそ快適に利用できる(と思い込んでいる)。

そんなさくらインターネットの唯一の欠点と言えなくもないのがデータベースの作成制限。しかし、先日の発表資料を読んだ時は思わすその日一日、いやいまでも喜びの余り走り出しす勢いだ。

さて、筆者はCORESERVER も利用しているが、こちらは全体的な順位やどれくらい負荷をかけているかがコントロールパネルで確認できる。

しかし、さくらインターネットは転送量とCPU 使用時間がわかるくらいで順位やドメインごとのデータは不明。さらに、そのCPU 使用時間や短期間でのアクセス増加を検知すると、優先度を下げたり503 エラーを発生させる。このような処理によってさらに安定した運用とリソース配分が行われている(たぶん)。

そうすると気になるのがCPU への負荷や転送量がどれくらいの時に制限を受けるかということだが、これは明示されていない。情報を集めるとある程度の予測は可能だが、急激な増加(特にクローラの集中砲火を浴びたとき)を知らずに受けたことなどで制限を受けることもある。これらの情報を集める過程で多かったのが、503 の頻発などを受けてサポートに問い合わせたり、コントロールパネルを確認したらリソースの消費に驚いたというものだ。

筆者は最近さくらインターネットに引っ越してきたこともあって、どれくらいの負荷がかかっているのかなどをこまめに確認している。さらに、ある程度情報が集まったり、”何か”が起きたときには記事にしようと普段から情報を集めている。が、面倒だ(何が面倒っていちいちID/パスワードでログインしなければならないし、複数アカウントあったらさらに面倒だし、グラフ作ったりするときのデータの整形や取りこぼしも・・)。そういうものこそ自動的にやってほしいもの。

標準機能としても特に見当たらず、また、随分探しまわったが、どういうわけか筆者のような人間が他に居ないのか、そういうツールが見当たらなかった。まぁ皆様Geek な人が多いからちょちょっと自作しているのかもしれない。

ということで、テキトーに作りました。

スポンサードリンク

動作例

データのみ

CSV フォーマットもどきで、コピー・アンド・ペーストすればグラフを使って視覚的に推移を把握することもできる。データフォーマットについては「データフォーマット」の項を参照。

※バージョン表示がついていないバージョンの時のもの

詳細付き

データのみだと必要な情報が見づらいかもしれない。詳細付きにすれば、文字情報でも内容を確認しやすくなる。

※バージョン0.09 の時のもの

iPhone で確認

gmail など複数のデバイスや場所で同一のメールを確認できるように設定しておくと、万一の負荷急増にも気がつける。気がつければ対応もできるし、それまでの時間も短縮できるかもしれない。

※バージョン 0.16 の時のもの

データ使用例

以下は同じデータで、表現の仕方が異なるだけ。

使用例1

使用例2

データフォーマット

cron で回そうかと思っているのと、ファイル関係は面倒なのでメールで送るように作りました。HTML で表示させることもできますのでどうぞご自由に。

メール本文には次の1行が書かれています(カスタマイズできます)

FMTCASEYJP01,2011/09/22 16:23:06,USER,13.6,46:29,1:28:12,895.0 ,1:28:18,887.3

データ形式は以下のとおり:

FMTCASEYJP01 ・・・・・・・・ フォーマットバージョン
2011/09/22 16:23:06 ・・・・ 取得した日時
USER ・・・・・・・・・・・・・ ユーザ名
13.6 ・・・・・・・・・・・・・ 現在のディスク使用量(%)
46:29 ・・・・・・・・・・・・ 現在のCPU 使用時間
1:28:12 ・・・・・・・・・・・ 昨日のCPU 使用時間
895.0 ・・・・・・・・・・・・・ 昨日の転送量(MB)
1:28:18 ・・・・・・・・・・・ 一昨日のCPU 使用時間
887.3  ・・・・・・・・・・・・ 一昨日の転送量(MB)

エクセルなどの表計算ソフトやCSV にコピペするだけで良いように(決して面倒だったからでは・・) 、時間はH:M:S (面倒だったのでHH:MM:SS ではない)hh:mm:ss 形式にして、転送量はGB の時はMB に直します。KB とかTB は経験ないので無視・・。

バージョンアップ

公開までにバグフィックス以外でバージョンアップしたところ。

  • Ver.0.05 CSV デリミタ指定を追加
  • Ver.0.06 テキストレポート機能の追加
  • Ver.0.07 取得したCPU 時間をh:m:s から hh:mm:ss に変更した
  • Ver.0.10 メール送信直前までにかかった時間を報告に追加
  • Ver.0.12 設定ファイルを別ファイルにして読みこむようにした
  • Ver.0.14 設置したサーバーがわからなくならないように、実行したホスト名を報告に追加

コード

コードがgdgdなのはやる気の表れ。

sakuracp.php

<?php

/* ==============================================
 *
 *  SAKURA Internet Resources Reporter
 *
 *             for CONTROL PANEL VERSION 1.2
 *
 * ==============================================
 *  License: GNU GPL
 *  Code   : PHP, UTF-8
 *  Auther : pc.casey.jp, 2011
 *  Version:
 *     Ver.0.01 2011.09.22 1st.
 *     Ver.0.02 2011.09.22 fix \s in data.
 *     Ver.0.03 2011.09.23 fix time format.
 *     Ver.0.04 2011.09.23 fix csv delimiter.
 *     Ver.0.05 2011.09.27 add csv delimiter.
 *     Ver.0.06 2011.09.27 add text report.
 *     Ver.0.07 2011.09.27 change time format.
 *     Ver.0.08 2011.09.27 change algorithm.
 *     Ver.0.09 2011.09.27 bugfix.
 *     Ver.0.10 2011.09.27 add work time.
 *     Ver.0.11 2011.09.29 use conf file.
 *     Ver.0.12 2011.09.29 fix get conf file path.
 *     Ver.0.13 2011.09.29 change get conf file path.
 *     Ver.0.14 2011.09.29 fix get host name.
 *     Ver.0.15 2011.09.29 fix get host name.
 *     Ver.0.16 2011.09.29 fix get host name.
 *  Usage  :
 *     - shell
 *        php /virtual/path/to/sakuracp.php
 *     - cron
 *        see sakuracp.sh file
 *   - http (no recomend)
 *        http://example.com/sakuracp.php
 */

/* ----------------------------------
 *    read config file
 * ----------------------------------
 */

//require_once(realpath(".") . '/sakuracp.conf');
require_once(dirname(__FILE__) . '/sakuracp.conf');

/* ----------------------------------
 *    define [do not change!]
 * ----------------------------------
 */

// program version
$version = "0.16";

// get start time
$sTime = time();

// loggin page url
$url = 'https://secure.sakura.ad.jp/rscontrol/?domain=';

// Resources page url
$url2="https://secure.sakura.ad.jp/rscontrol/rs/resource";

// program name
//$host_name = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : gethostbyaddr("127.0.0.1");
$host_name = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : php_uname("n");
$program_name  = "SAKURA Internet Resources Reporter ";
$program_name .= "for CONTROL PANEL VERSION 1.2 Ver.";
$program_name .= $version . " on " . $host_name;
$program_name .= " (C) http://pc.casey.jp , 2011";

// dev
//$data = "resource.htm";
$debug = false;

/* ------------------------------------
 *    MAIN section
 * ------------------------------------
 */

// start output
header("HTTP/1.0 200 OK");
header("Content-type:text/html;charset=utf-8");

// get date time
$date = date('Y/m/d H:i:s');

// get data
$data = getData();

// get detail
$details = getDetail($data);

// delete space from data
$details[0] = @preg_split("/\s+/", $details[0]);

// data temp
$details_new = $details;

// get text report
if($text_report){
    $mail_body .= getTextReport($details_new);
}

// get mail body
$msg = getMailBody($details_new);
$mail_body .= implode($delimiter, $msg);

// add mail footer
$mail_body .= "\n\n" . $program_name;
$eTime = time() - $sTime;
$mail_body .= " (" . $eTime . "sec+)";

// debug
if($debug){
    print $mail_body;
    $mail=false;
}

// mail send
if($mail){
    mailSend();
}

// end of script
print "OK!\n";
exit;

/* ------------------------------------
 *    get mail body
 * ------------------------------------
*/
// 2011.09.27 ver.0.02
function getMailBody($details_new){
    global $date;

    // output data
    $msg[] = 'FMTCASEYJP01';
    $msg[] = $date;

    // account
    //$msg[] = $details_new[0][1];
    $msg[] = $details_new[0][2];
    //$msg[] = " (" . $details_new[0][4] . ")";

    // disk
    //$msg[] = $details_new[0][6];
    //$msg[] = $details_new[0][7];
    //$msg[] = $details_new[0][8];
    $msg[] = $details_new[0][9];

    // cpu / transfer
    $msg[] = $details_new[1];
    //$msg[] = $details_new[2];
    $msg[] = $details_new[3];
    $msg[] = $details_new[4];
    $msg[] = $details_new[5];
    $msg[] = $details_new[6];

    return $msg;
}

/* ------------------------------------
 *    make text report
 * ------------------------------------
*/
// 2011.09.27 ver.0.03
function getTextReport($details_new){
    global $date;

    $text[] = "\n";

    // report date time
    $text[] = sprintf(
        "Report at %s",
        $date
    );

    // account
    $text[] = sprintf(
        "Account: %s",
        $details_new[0][2]
    );

    // disk
    $text[] = sprintf(
        "Disk Use: %s%%",
        $details_new[0][9]
    );

    $text[] = "Load Infomation:";

    // today
    $text[] = sprintf(
        "-0: %s",
        $details_new[1]
    );

    // today -1
    $text[] = sprintf(
        "-1: %s (%s MB)",
        $details_new[3],
        $details_new[4]
    );

    // today -2
    $text[] = sprintf(
        "-2: %s (%s MB)",
        $details_new[5],
        $details_new[6]
    );

    // end
    $text[] = "\n";

    // make and output
    return implode("\n", $text);
}

/* ------------------------------------
 *    mail send
 * ------------------------------------
*/
// 2011.09.19 ver.0.03
function mailSend(){
    // init
    global    $mail_addr_to,
            $mail_subject,
            $mail_body,
            $mail_addr_from;

    // set japanese
    mb_language("japanese");
    mb_internal_encoding("UTF-8");

    // mail send
    mb_send_mail(
        $mail_addr_to,
        $mail_subject,
        $mail_body,
        "From:" . $mail_addr_from
    );
}

/* ------------------------------------
 *    secure connection
 * ------------------------------------
 */
function getData(){
    // init
    global $url;
    global $url2;
    global $domain;
    global $password;

    require_once "HTTP/Client.php";

    $client =& new HTTP_Client();
    $classCookieManager = $client->getCookieManager();

    // 1st access, get token

    $client->get($url);
    $response = $client->currentResponse();

    // get token
    preg_match(
        "/name\=\"Token\" value\=\"(.*)\"/",
        $response['body'],
        $tokens
    );
    $token = $tokens[1];

    // 2nd access, send id, password, token, submit

    $login_params = array(
        "domain"    => $domain,
        "password"    => $password,
        "Token"        => $token,
        "Submit"    => "index"
    );
    $client->post($url, $login_params);
    //$response = $client->currentResponse();

    // 3rd access, save resources page data

    $client->get($url2);
    $response = $client->currentResponse();

    return $response['body'];
}

/* ------------------------------------
 *   data analyz
 * ------------------------------------
 */
function getDetail($body){
    global $path_dom;

    // for html Analyz
    include($path_dom . 'simple_html_dom.php');

    //$html = file_get_html($body);
    $html = str_get_html($body);

    // main info
    $main = $html->find('table.menubox td', 0)->plaintext;
    $ret[]= str_replace(array("(", ")", "%"), "", $main);

    // cpu time, transfer rate
    $p = $html->find('table.viewbox td');
    foreach($p as $k){
        // get strings
        $tmp = $k->innertext;

        // clean up
        // 2011.09.23 remove
        //$tmp = str_replace(array("&nbsp;", "秒"), "", $tmp);
        //$tmp = preg_replace("/(時間|分)/", ":", $tmp);

        // 2011.09.23 add
        $tmp = str_replace("&nbsp;", "", $tmp);
        if(preg_match('/(時間|分|秒)/', $tmp)){
            $tmp = strFormatChange($tmp);
        }

        // unit
        $u = "";
        preg_match('/([0-9\.]+)\s+?GB/', $tmp, $u);
        if($u){
            $tmp = $u[1] * 1024;
        }else{
            $tmp = str_replace(" MB", "", $tmp);
        }

        $ret[] = $tmp;
    }

    return $ret;

    // get user id
    //$uid = $html->find('a[target=_blank]', 2)->plaintext;
    //echo $uid;

    // get disk
    //$disk = $html->find('div table tbody tr td table tbody tr td', 0)->plaintext;
    //echo $disk;
}

/* ------------------------------------
 *    time strings changer
 * ------------------------------------
 *  2011.09.23 1st.
 */
function strFormatChange($tmp){
    // debug
    // jesus!
    //preg_match("/(?P<h>[0-9]+)?(時間)?(?P<m>[0-9]+)?分?(?P<s>[0-9]+)?秒?/u", $tmp, $times);
    //print_r($times);

    $time = "";

    // h
    $time .= preg_match("/([0-9]+)時間/u", $tmp, $times) ? $times[1] : "0";
    $time .= ":";

    // m
    $time .= preg_match("/([0-9]+)分/u", $tmp, $times) ? $times[1] : "00";
    $time .= ":";

    // s
    $time .= preg_match("/([0-9]+)秒/u", $tmp, $times) ? $times[1] : "00";

    $time_new = vsprintf("%02d:%02d:%02d", explode(":", $time));

    return $time_new;
}
?>

sakuracp.conf

<?php
/* ------------------------------------
 *    Settings
 * ------------------------------------
 */
$domain            = "XXXXX.sakura.ne.jp";
$password        = "XXXXX";

$mail            = ture;
$mail_subject    = "SAKURA Internet Resources Report";
$mail_body        = "GORIYOU WA KEIKAKU TEKINI :P";
$mail_addr_from    = "[email protected]";
$mail_addr_to    = "[email protected]";

$delimiter        = ",";
$text_report    = true;

$path_dom        = "./simplehtmldom/";
?>

sakuracp.sh

#
# shell script for sakuracp.php on coreserver, sample
#
# Auther : pc.casey.jp, 2011
# Code   : sh, LF
# License: GNU GPL
# Usage  : shell or cron
#  ex. every hour
#    0 * * * * sakuracp.sh
#  ex. every morning, no mail
#    0 5 * * * sakruacp.sh >/dev/null 2>&1
#

# /usr/local/bin/php /virtual/path/to/sakuracp.php

ダウンロード

調整中

PHP Simple HTML DOM Parser が必要です

※cron で回すか、公開領域に.htaccess などで制限をかけてアクセス制限してくださいね

※さくらインターネットのサーバー上では実行できないかも(参考文献参照)

※文頭のコントロールパネルの図は本サイトのものではありません

# ところで、さくらインターネットのセキュリティーに関して以前問題があったような(少なくとも誰かが怒ってたような)気がするんだけども、それと似通ったところでぽっかり開いた穴を見つけてしまったのだがどうしよう?これは仕様?

アップデート

BugFix されたVer.0.20 を以下に掲載:

さくらインターネット リソース情報自動報告プログラム(PHP 版)(Ver.0.20)

参考文献

さくらインターネット上でcron を実行することに関して

関連記事

スポンサードリンク

Comments

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です