SolitonNKは結果の表示だけじゃない。スクリプトを使って解析結果を基にアクションさせよう。(2/3) anko スクリプトの書き方・使い方
SolitonNKでankoスクリプトを用いて、解析結果を基にアクションさせるための説明の2回目記事は、スクリプトの書き方です。
SolitonNKの anko モジュールは、検索パイプライン内で使える汎用スクリプトツールです。スクリプト作成は手間がかかりますが、検索エントリの非常に柔軟な操作ができるようになります。そしてスクリプトを作成したら、他のユーザーとも簡単に共有できます。
ankoスクリプト言語そのものについては、anko本家のドキュメントを参照していただくとして、本記事では SolitonNK で anko スクリプトをどのように使うかを簡単に説明します。
ankoスクリプトの管理
検索でankoスクリプトを実行するには、スクリプトを含むテキストファイルをリソースとしてアップロードする必要があります。
リソースの作成や管理は、ユーザーインターフェイスのメインメニューの中にある「リソース/Resources」の画面でできます。新しいリソースを作成するには、右上の「追加/Add」ボタンを選択します。
新しいリソースの追加用画面では、リソース名と、(必要に応じて)説明を記述し(、そのリソースを読み取れるするグループを選択し)、アップロードするファイルを選択します。[保存/SAVE]ボタンを押すとリソースがアップロードして保存されます。
スクリプト記述内容を変更するには、元のテキストファイルのスクリプトを編集してから、ファイルをリソースに再アップロードしてください。(今後、スクリプト作成を簡単にする統合テキストエディターが追加される予定です)
ankoスクリプトを実行する
スクリプトを実行するには、検索実行欄で anko
を指定し、その後にスクリプトの名前、スクリプト用の引数を続けます。たとえば、引数として2つの数値を取る foo
という名前のスクリプトを実行するに パイプラインの記述の中で anko foo 1 3
と記述すればよいです。
ankoスクリプトを書く
ankoスクリプトは、eval
コマンドと同じ構文を使用します。以下の例とevalモジュールのセクションを参照してください。
必須の関数定義
SolitonNKの検索欄から用いる ankoスクリプトにおいては、 Process
関数か、Main
関数かどちらかが必ず用いられなければなりません。これらの関数は引数をとりません。この2つの関数は、全く異なる検索エントリ処理方法の選択肢となります。
Process
関数が定義されている場合、検索エントリごとに1回呼び出されます。エントリの列挙値はローカル変数として扱われ、Process
関数の戻り値としては、エントリがパイプラインを続行できる場合には true
、できない場合には false
が返されます。
Main
関数が定義されている場合、呼び出しは一度だけです。したがって、readEntry
関数やwriteEntry
関数を用いて各検索エントリの結果を取得し、パイプラインに渡すようにして、その後の処理ができるようにする必要があります。
可能な限り、Main
関数よりも、概念的にシンプルなProcess
関数を使ってスクリプトを書くことが推奨されています。
オプションの関数定義
Parse
関数やFinalize
関数をスクリプトに含めることができます。
Parse
関数は、Process
関数あるいはMain
関数より前に呼び出され、引数の配列としてコマンドライン引数を与えることができます。Parse
関数で、返り値としてnil
が返されると、引数が正常に処理されたことを表します。nil
以外の戻り値はエラーとして扱われ、ユーザーに表示されます。スクリプトの引数を解析する方法のサンプルについては、以下のサンプルスクリプトを参照してください。
注意:Parse関数は必ず明示的な値を返り値として返します。返り値がnil
の場合は解析の成功の通知です。それ以外値が返ってきた場合は、エラーを示します。エラーが発生した場合について、問題を説明する文字列を返すようにすることをお勧めします。
Finalize
関数は、Process
関数あるいはMain
関数が完了した後の、スクリプトの最後に実行されるコードです。これは、リソースを作成する必要がある時に活用するのに適した場所です。
Process()関数を使用したサンプルスクリプト
このサンプルスクリプトは、スクリプトの引数指定によって2種類の動作モードが選択できます。'build'(作成)モードでは、packet
モジュールから抽出された"SrcIP"フィールドを使用して、その時点での検索によって見つかった全てのIPアドレスのリストを作成し、そのリストをリソースとして保存します。 'apply'(適用)モードでは、以前の'build'モードの検索いよって作成されたテーブルを取得し、 'apply'モードの検索に用いられているエントリーから、"SrcIP"フィールドに以前に見つかったIPアドレスが記載されているエントリを全て削除します。
なお、このスクリプトは、ネットワーク上の新しいデバイスを探すために使用されてきましたが、現在はlookup
モジュールにこのスクリプトと同じ機能を持っていて、より柔軟に使えます。
table = make(map[string]interface)
task = "build"
var json = import("encoding/json")
# first arg = "build" or "apply"
func Parse(args) {
errstr = "argument must be 'build' or 'apply'"
if len(args) == 1 {
task = args[0]
} else {
return errstr
}
switch task {
case "apply":
# load the table
data, _ = getResource("lookuptable")
json.Unmarshal(data, &table)
case "build":
default:
return errstr
}
return nil
}
func Process() {
if task == "build" {
s = toString(SrcIP)
table[s] = true
return true
} else if task == "apply" {
s = toString(SrcIP)
# create & set an enumerated value named "new" to true or false
setEnum("new", !table[s])
# if it's not in the table, return true
return !table[s]
}
}
func Finalize() {
if task == "build" {
data, err = json.Marshal(table)
if err != nil {
return err
}
return setResource("lookuptable", data)
}
}
"SrcIP"列挙値は、Process
関数内の他の変数と同様に読み取れるように見えますが、列挙値を設定するためにはsetEnum
関数を使用してアクションを明示する必要があります。
table
およびtask
変数は、関数定義の外部で宣言されていることに注意してください。組み込みのjsonエンコーディングライブラリも、関数定義の前にインポートされます。
Main()関数を使用したサンプルスクリプト
Main
関数を使用してスクリプトを記述するのは、Process
関数の場合より難しいですが、エントリを複製する必要がある場合は、Main
関数を使わねばなりません。
次のスクリプトでは、Modbus
メッセージを含むエントリを読み取ります。メッセージがタイプ0x10
(「複数のレジスタを書き込む」)というリクエストの場合、スクリプトは書き込まれるレジスタごとに元のエントリを1回複製し、単一のレジスタアドレス+レジスタ値を含む「RegAddr」および「RegValue」列挙値を各エントリにアタッチします。
注:このスクリプトは単独では機能しません。書かれているように、それはパイプラインのより上流での別のankoスクリプトの出力を利用することを目的としていたため、「Request」や「WriteAddr」などの列挙値が用いられています。
func Main() {
for i = 0; i != -1; i++ {
ent, err = readEntry()
if err != nil {
return
}
# Check if this is a request or a response
Request, err = getEntryEnum(ent, "Request")
if err != nil {
#Request isn't set, this isn't a modbus packet, skip
continue
}
# read the function value
Function, err = getEntryEnum(ent, "Function")
if err != nil {
continue
}
ReqResp, err = getEntryEnum(ent, "ReqResp")
if err != nil {
continue
}
if Request == true {
if Function == 0x10 {
# write multiple registers
Addr, err = getEntryEnum(ent, "WriteAddr")
if err != nil {
writeEntry(ent)
continue
}
Count, err = getEntryEnum(ent, "WriteCount")
if err != nil {
writeEntry(ent)
continue
}
if Count == 0 || len(ReqResp) < 5 + 2*Count {
writeEntry(ent)
continue
}
for j = 0; j < Count; j++ {
newEnt = cloneEntry(ent)
# read the register value
val = (toInt(ReqResp[5+(2*j)]) << 8) | toInt(ReqResp[5+(2*j)+1])
setEntryEnum(newEnt, "RegAddr", Addr + j)
setEntryEnum(newEnt, "RegValue", val)
err = writeEntry(newEnt)
if err != nil {
continue
}
}
} else {
writeEntry(ent)
}
}
}
}
Main
関数を用いたスクリプト記述では、Process
関数では不要だったreadEntry
、cloneEntry
、writeEntry
といった関数によってエントリを明示的に管理・操作してることに注意してください。また、列挙型の値の読みだしには、getEntryEnumand
関数やsetEntryEnum
関数を使用していて、変数の扱いとは異なっていることにも注意してください。
SolitonNKに用意されているanko スクリプトの書き方の説明はここまでです。
次の記事ではいよいよ、検索結果を基にウェブにアクションするサンプルスクリプトを紹介します。
Author And Source
この問題について(SolitonNKは結果の表示だけじゃない。スクリプトを使って解析結果を基にアクションさせよう。(2/3) anko スクリプトの書き方・使い方), 我々は、より多くの情報をここで見つけました https://qiita.com/kakawai/items/49b1e8eaa9c91504b34b著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .