BlueOnyx に機能を追加する (その1)

スポンサーリンク

BlueOnyx のコンパネに機能を追加する方法をメモしておく。今回は例として,cron ジョブをコンパネで設定できるようにする。


スポンサーリンク

CODB オブジェクト定義

BlueOnyx は CODB という独自のデータベースで、設定値などの情報を管理している。

CODB のオブジェクト一覧は cceclient の CLASSES コマンドで確認できる。

# /usr/sausalito/bin/cceclient
100 CSCP/0.80
200 READY
CLASSES
110 CLASS pam_abl_settings
110 CLASS FtpSite
110 CLASS Vsite
110 CLASS VsiteServices
110 CLASS EmailAlias
  :

これに cronConf と cronJob というオブジェクトを追加する。

サーバの /usr/sausalito/schemas/base/ ディレクトリ配下に適当なディレクトリを作成し,そこに xml ファイルでスキーマ (型) を記述する。

# mkdir /usr/sausalito/schemas/base/cron
# cd /usr/sausalito/schemas/base/cron/
# vi cron.schema
<typedef name="words" type="re" data="^[^:\n\r]*$" />
<typedef name="crontime" type="re" data="^[^:\n\r]*$" />

<class name="cronConf" namespace="" version="1.0"
    createacl="ruleAll" destroyacl="ruleAll"
>
    <property
        name="user"
        type="accountname"
        readacl="ruleAll" writeacl="ruleAll"
    />
    <property
        name="email"
        type="scalar"
        readacl="ruleAll" writeacl="ruleAll"
    />
>
</class>

<class name="cronJob" namespace="" version="1.0"
    createacl="ruleAll" destroyacl="ruleAll"
>
    <property name="user"
        type="accountname"
        readacl="ruleAll" writeacl="ruleAll"
    />
    <property name="enabled"
        type="boolean"
        readacl="ruleAll" writeacl="ruleAll"
    />
    <property name="min"
        type="crontime"
        readacl="ruleAll" writeacl="ruleAll"
    />
    <property name="hour"
        type="crontime"
        readacl="ruleAll" writeacl="ruleAll"
    />
    <property name="day"
        type="crontime"
        readacl="ruleAll" writeacl="ruleAll"
    />
    <property name="mon"
        type="crontime"
        readacl="ruleAll" writeacl="ruleAll"
    />
    <property name="wday"
        type="crontime"
        readacl="ruleAll" writeacl="ruleAll"
    />
    <property name="command"
        type="words"
        readacl="ruleAll" writeacl="ruleAll"
    />
</class>

cced を再起動する。

# /etc/init.d/cced.init reload

再度 CLASSES を確認すると,cronConf と cronJob オブジェクトが追加されていることがわかる。

# /usr/sausalito/bin/cceclient
100 CSCP/0.80
200 READY
CLASSES
  :
110 CLASS cronConf
  :
110 CLASS cronJob
  :

メニューに項目を追加

コンパネのメニューに項目を追加する。今回は「個人プロフィール」に「cron」を追加する。

サーバの /usr/sausalito/ui/menu/base/ ディレクトリ配下に適当なディレクトリを作成し,そこに xml ファイルで設定を記述する。

# mkdir /usr/sausalito/ui/menu/base/cron
# cd /usr/sausalito/ui/menu/base/cron/
# vi cron.xml
<item id="base_cron" label="cron"
    description="edit crontab"
    url="/base/cron/cron.php"
>
    <parent id="base_personalProfile" order="20" />
</item>

メニューに cron が追加された。特に再起動などは必要ない。

メニュー

フロントエンドの作成

コンパネの画面を作成する。サーバの /usr/sausalito/ui/web/base/ ディレクトリ配下に適当なディレクトリを作成し,そこに PHP で作成していく。

# mkdir /usr/sausalito/ui/web/base/cron
# cd /usr/sausalito/ui/web/base/cron/
# vi cron.php
<?php

include_once("ServerScriptHelper.php");
include_once("uifc/PagedBlock.php");

$serverScriptHelper = new ServerScriptHelper();
$cceClient = $serverScriptHelper->getCceClient();
$factory = $serverScriptHelper->getHtmlComponentFactory("base-cron", "/base/cron/cronConfHandler.php");

$loginName = $serverScriptHelper->getLoginName();

list($oid) = $cceClient->find("cronConf", array("user" => $loginName));
$cronConf = ($oid > 0) ? $cceClient->get($oid) : array();

$page = $factory->getPage();

$block = new PagedBlock($page, "cronSettings", $factory->getLabel("cronSettings"));

$errors = $serverScriptHelper->getErrors();
if (count($errors) > 0) {
    $block->processErrors($errors, array('email' => 'email'));
}

$emailField = $factory->getEmailAddress(
    "email",
    $cronConf["email"],
    1
);
$emailField->setOptional('silent');
$block->addFormField(
    $emailField,
    $factory->getLabel("email")
);

$block->addButton($factory->getSaveButton($page->getSubmitAction()));

$jobButton = $factory->getButton('/base/cron/job.php', 'job_button');

$serverScriptHelper->destructor();

echo($page->toHeaderHtml());
echo($jobButton->toHtml());
echo($block->toHtml());
echo($page->toFooterHtml());

コンパネのメニューで cron をクリックすると,cron.php が表示される。

cron.php

この他の画面も作成していく。

# vi cronConfHandler.php
<?php

include_once("ServerScriptHelper.php");
include_once("AutoFeatures.php");

$serverScriptHelper = new ServerScriptHelper();
$cceClient = $serverScriptHelper->getCceClient();

$loginName = $serverScriptHelper->getLoginName();

// save
$attr = array(
    "user" => $loginName,
    "email" => $email
);

list($oid) = $cceClient->find("cronConf", array("user" => $loginName));
if ($oid > 0) {
    $cceClient->set($oid, "", $attr);
} else {
    $oid = $cceClient->create("cronConf", $attr);
}
$errors = $cceClient->errors();

if (count($errors) != 0)
    print $serverScriptHelper->toHandlerHtml("/base/cron/cron.php", $errors);
else
    print $serverScriptHelper->toHandlerHtml("/base/cron/cron.php");

$serverScriptHelper->destructor();
# vi job.php
<?php

$iam = '/base/cron/job.php';
$edit = '/base/cron/jobEdit.php';
$parent = '/base/cron/cron.php';

include("ServerScriptHelper.php");

$serverScriptHelper = new ServerScriptHelper();
$cceClient = $serverScriptHelper->getCceClient();
$factory = $serverScriptHelper->getHtmlComponentFactory("base-cron");
$i18n = $serverScriptHelper->getI18n("base-cron");

$loginName = $serverScriptHelper->getLoginName();

if ($_REMOVE) {
    $cron = $cceClient->get($_REMOVE);
    if ($cron['user'] != $loginName) {
        header("location: /error/forbidden.html");
        return;
    }
    $cceClient->destroy($_REMOVE);
}

$page = $factory->getPage();

// build the scroll list
$scrollList = $factory->getScrollList("jobList", array("enabled", "time", "command", "listAction"), array());
$scrollList->setAlignments(array("left", "left", "left", "center"));
$scrollList->setColumnWidths(array("1%", "150", "", "1%"));
$scrollList->addButton($factory->getAddButton("/base/cron/jobEdit.php"));

$oids = $cceClient->findx('cronJob', array('user' => $loginName));
foreach ($oids as $oid) {
    $cron = $cceClient->get($oid);

    foreach (array('min', 'hour', 'day', 'mon', 'wday', 'command') as $key)
        $$key = $cron[$key];

    $action = $factory->getCompositeFormField(array(
        $factory->getModifyButton(
            "$edit?_TARGET=$oid&_LOAD=1&TYPE=$type"),
        $factory->getRemoveButton(
            "javascript: confirmRemove('Do you remove it really?', '$oid', '$label')")
    ));

    $enabled = $factory->getBoolean('enabled', $cron['enabled'], "r");

    $scrollList->addEntry(array(
        $factory->getBoolean('enabled', $cron['enabled'], "r"),
        $factory->getSimpleText($min.' '.$hour.' '.$day.' '.$mon.' '.$wday),        $factory->getSimpleText($cron['command']),
        $action
    ), "", false);
}

$backButton = $factory->getBackButton($parent);

$serverScriptHelper->destructor();

print($page->toHeaderHtml());
print($scrollList->toHtml());
print($backButton->toHtml())

?>
<script type="text/javascript">
function confirmRemove(msg, oid, label) {
    //msg = top.code.string_substitute(msg, "", label);
    if (confirm(msg))
        location = "<?php print $iam; ?>?_REMOVE=" + oid;
}
</script>
<?php

print($page->toFooterHtml());
# vi jobEdit.php
<?php

include("ServerScriptHelper.php");

$serverScriptHelper = new ServerScriptHelper();
$cceClient = $serverScriptHelper->getCceClient();
$factory = $serverScriptHelper->getHtmlComponentFactory("base-cron", "/base/cron/jobEditHandler.php");

$loginName = $serverScriptHelper->getLoginName();

$page = $factory->getPage();

$form = $page->getForm();

$block = $factory->getPagedBlock("jobConf");

$add = TRUE;
if ($_TARGET) {
    $cron = $cceClient->get($_TARGET);
    if ($cron['user'] != $loginName) {
        header("location: /error/forbidden.html");
        return;
    }
    $add = FALSE;

    $hiddenOid = $factory->getTextBlock("_TARGET", $_TARGET);
    $hiddenOid->setOptional(true);
    $block->addFormField(
        $hiddenOid,
        $factory->getLabel("_TARGET"),
        "Hidden"
    );
}

$default = ($cron['enabled'] || $enabled) ? 1 : 0;
$block->addFormField(
    $factory->getBoolean("enabled", $default),
    $factory->getLabel("enabled")
);

foreach (array('min', 'hour', 'day', 'mon', 'wday') as $key) {
    $val = $$key;
    if ($add) {
        if ($val) {
            $default = $val;
        } else {
            $default = "*";
        }
    } else {
        $default = $cron[$key];
    }
    $block->addFormField(
        $factory->getTextField($key, $default, "rw"),
        $factory->getLabel($key)
    );
}

$default = ($add) ? "" : $cron['command'];
$block->addFormField(
    $factory->getTextField("command", $default, "rw"),
    $factory->getLabel("command")
);

$block->addButton($factory->getSaveButton($page->getSubmitAction()));
$block->addButton($factory->getCancelButton("/base/cron/job.php"));

$serverScriptHelper->destructor();

print($page->toHeaderHtml());
print $block->toHtml();
print($page->toFooterHtml());
# vi jobEditHandler.php
<?php

include("ServerScriptHelper.php");

$serverScriptHelper = new ServerScriptHelper();
$cceClient = $serverScriptHelper->getCceClient();
$i18n = $serverScriptHelper->getI18n("base-cron");

$loginName = $serverScriptHelper->getLoginName();

$errors = array();

// validation
foreach (array('min', 'hour', 'day', 'mon', 'wday') as $key) {
    $val = $$key;
    if (!preg_match('/^[0-9\*\-,]+$/', $val)) {
        $error_msg = "";
        $errors[] = new Error($error_msg);
    }
}

$attr = array(
    "user" => $loginName,
    "enabled" => $enabled,
    "min" => $min,
    "hour" => $hour,
    "day" => $day,
    "mon" => $mon,
    "wday" => $wday,
    "command" => $command
);

if (count($errors) == 0) {
    if ($_TARGET > 0) {
        $cron = $cceClient->get($_TARGET);
        if ($cron['user'] != $loginName) {
            header("location: /error/forbidden.html");
            return;
        }
        $cceClient->set($_TARGET, "", $attr);
    } else {
        $oid = $cceClient->create("cronJob", $attr);
    }

    $errors = array_merge($errors, $cceClient->errors());
}

if (count($errors) > 0) {
    print($serverScriptHelper->toHandlerHtml("/base/cron/jobEdit.php", $errors, true));
} else {
    print($serverScriptHelper->toHandlerHtml("/base/cron/job.php"));
}

$serverScriptHelper->destructor();

言語ファイルの作成

管理画面のラベルやメッセージに表示される内容の言語ファイルを作成する。

# vi ~/base-cron.eucjp.po
msgid  "cronSettings"
msgstr "cron 設定"

msgid  "email"
msgstr "メール通知先"

msgid  "job_button"
msgstr "cron ジョブを編集"

msgid  "job_button_help"
msgstr "cron ジョブを編集します。"

msgid  "jobList"
msgstr "cron ジョブ一覧"

msgid  "jobConf"
msgstr "cron ジョブ設定"

msgid  "enabled"
msgstr "有効"

msgid  "time"
msgstr "時刻"

msgid  "min"
msgstr "分"

msgid  "hour"
msgstr "時"

msgid  "day"
msgstr "日"

msgid  "mon"
msgstr "月"

msgid  "wday"
msgstr "曜日"

msgid  "command"
msgstr "コマンド"

msgid  "listAction"
msgstr "操作"

作成したファイルを msgfmt コマンドで変換する。

# msgfmt -o /usr/share/locale/ja/LC_MESSAGES/base-cron.mo ~/base-cron.eucjp.po

各画面が日本語化された。

日本語化された画面

イベントハンドラの作成

CODB の cronConf オブジェクトと cronJob オブジェクトが新規作成や変更,削除されるときに実行されるイベントハンドラを作成する。

サーバの /usr/sausalito/handlers/base/ ディレクトリ配下に適当なディレクトリを作成し,そこに Perl で記述する。この Perl スクリプトは root 権限で実行される。

# mkdir /usr/sausalito/handlers/base/cron
# cd /usr/sausalito/handlers/base/cron/
# vi cron.pl
#!/usr/bin/perl -I/usr/sausalito/perl/ -w

use strict;

use CCE;

use Sauce::Config;
use Sauce::Util;

my $cce = new CCE(
    Namespace => "cron",
    Domain => 'base-cron'
);

$cce->connectfd();

my $errors;

my $oid = $cce->event_oid();
my($success, $user, $old, $new) = $cce->get($oid);

my $cron = $cce->event_object();

my $loginName = ($user->{user}) ? $user->{user} : $old->{user};

if (!$loginName) {
    $cce->warn("nonExistentUser");
    $cce->bye("FAIL");
    exit(1);
}

my @pwent = getpwnam($loginName);
use File::Basename;
my $cron_file = "/var/spool/cron/" . basename($loginName);

my $data;

##--- conf ---##
my ($OID) = $cce->find('cronConf', {'user' => $loginName});
my $email;
if ($OID > 0) {
    my ($ok, $cron) = $cce->get($OID);
    if ($ok) {
        $email = $cron->{email};
    }
}
$data .= "MAILTO=\"$email\"\n";

##--- job ---##
my (@oids) = $cce->findx('cronJob', {'user' => $loginName});
foreach my $oid (@oids) {
    my ($ok, $cron) = $cce->get($oid);
    $data .= "#" if (!$cron->{enabled});
    $data .= "$cron->{min} $cron->{hour} $cron->{day} $cron->{mon} $cron->{wday} $cron->{command}\n";
}
chomp $data;

Sauce::Util::replaceblock(
    $cron_file,
    '# cron.pl: Do not edit below this line',
    $data,
    '# cron.pl: Do not edit above this line',
    0600
);

Sauce::Util::chmodfile(0600, $cron_file);
Sauce::Util::chownfile(@pwent[2,3], $cron_file);

$cce->bye("SUCCESS");
exit(0);

イベントハンドラの登録

作成したイベントハンドラが自動的に実行されるように設定する。

# mkdir /usr/sausalito/conf/base/cron
# cd mkdir /usr/sausalito/conf/base/cron/
# vi cron.conf
cronConf.* perl:base/cron/cron.pl
cronJob.*  perl:base/cron/cron.pl
cronJob._CREATE   perl:base/cron/cron.pl
cronJob._DESTROY  perl:base/cron/cron.pl

以上で BlueOnyx のコンパネに機能を追加することができた。次回,気力が沸けば rpm の作成および pkg 化をしていきたい。

参考ページ

コメント

タイトルとURLをコピーしました