LuCI2 (OpenWrt web 管理界面)


English

LuCI2 (OpenWrt web 管理界面)

一直以來OpenWrt都是采用Lua寫的web管理界面LuCI,(開機速度慢不說,居然比不過騰達等弱路由器開機速度)。 LuCI需要使用多個Lua擴展(如 ubus, luci.model.uci, nixio.fs, 等等)去存取系統信息和設置. 不幸的是這種解決方案在慢CPU和低內存的低配機器設備上是個災難,此方案相當消耗資源且並不能很好的工作。

這導致開發了LuCI2, 一個不同架構的新的web管理節目。它不再使用Lua,而是使用靜態HTML頁面加JavaScript XHR方法。 這意味着從OpenWrt設備中下載后在客戶端(瀏覽器)中構建HTML頁面, 通過ubus存取各種系統數據(通過uhttpd-mod-ubus提供基於HTTP的接口API).

重要說明

如上所述, LuCI2通過ubus和OpenWrt子系統通信(包括如networkservice以及其它)。遺憾的是並非每個主要的OpenWrt工具都自己注冊了ubus,如LuCI2不能使用opkg(安裝包管理)。 LuCI2通過提供在附加ubus名稱空間的rpcd插件解決了這個問題。前面說的opkg它在ubus中注冊了一個新的luci2.opkg路徑來訪問。

綜上所述, LuCI2包括兩個方面: 打包的HTML/CSS/JS文件 (htdocs) 和一些在OpenWrt環境下運行的附加小工具。

在接下來的章節中,你會找到各種關於LuCI2開發幫助的細節。

菜單

首先需要知道的是瀏覽器接收到的關於LuCI2菜單並不固定寫死在任何文件。代替的是通過ubus使用luci.ui路徑和menu方法。可以通過使用以下命令來查看:

ubus call luci2.ui menu '{ "ubus_rpc_session": "invalid" }'

內部的rpcd插件分析在目錄/usr/share/rpcd/menu.d的所有文件,當前用戶(基於傳遞的ubus_rpc_session)不允許增加或者刪除條目。這將導致在一個二級菜單限制當前有權限訪問的條目。

頂層菜單項用下面的JSON定義:

"foo": {
	"title": "Foo",
	"index": 12
}

(並且通過index值排序)。

第二層菜單項通過同樣的方法定義:

"foo/bar": {
	"title": "Bar",
	"acls": [ "baz", "qux" ],
	"view": "foo/bar",
	"index": 5
}

注意,第二層菜單項可以在獨立的文件中定義,這樣就可以方便的添加新的菜單項定義而不用修改原有的文件。

模板

每一個LuCI2子頁面必須有一個存放在/www/luci2/template/目錄下的模板。它們提供了非常簡單的內容替換區的HTML文件。需要注意的是它們不包含任何變量的引用,這是JavaScript的功勞:用JavaScript去讀取並寫入內容。在為本地化系統(i18n)這些文件中唯一特定的語法類似下面的標記:

<p><%:Hello world%></p>

視圖

從模板中分離,每個LuCI2子頁面也需要一個定義並存放在/www/luci2/view/目錄下的一個視圖,視圖是使用了子頁面特定對象的L.ui.view擴展的JavaScript文件。Javascript中需要提供execute方法的實現,該方法將在膜拜加載后被執行。可選的也可以提供子頁面的titledescription屬性。

有一點需要注意的是需要提供execute方法,創建視圖失敗有幾種原因,特別是當它需要ubus加載額外的數據使用的時候。所以在execute方法中提供成功或者失敗的信息是有意義的。不幸的是在異步方法加載特定數據的時候,不能簡單的返回truefalse,解決的方法是返回一個Promise對象來運行推遲提供是否成功的信息。

最簡單的視圖可以如下所示:

L.ui.view.extend({
	title: L.tr('Foo'),			/* 可選的標題 */
	description: 'Bar',			/* 可選的描述 */

	execute: function() {
		var deferred = $.Deferred();	/* 創建一個延遲對象 */
		deferred.resolve();		/* 立即Resolve,它不做任何事可以返回失敗 */
		return deferred.promise();	/* 返回Promise對象 (延遲對象的子集) */
	}
});

通過ubus通訊

在開始之前,需要知道的是LuCI2提供了一些存取UCI系統的一些幫助。如果寫一個簡單的管理/etc/config/下配置文件不需要完全知道ubus的調用方法,則可以跳過該部分。

構建一些更復雜的LuCI2視圖前最好先弄明白使用到的ubus調用。完整的對象和方法列表可以通過運行ubus -v list命令來得到。

下面這個簡單的例子調用了log對象和write方法,它需要提供一個event參數傳遞進去。使用ubus命令行國內根據是,需要如下所示命令:

ubus call log write '{ "event": "Foo" }'

LuCI2為ubus通訊提供了一個叫做L.rpc.declare的工具,它可以如魔法般的幫助JavaScript訪問ubus方法。注意,定義聲明方法的時候方法並不被執行,參數也未傳遞,這是為以后調用准備的一個方法。下面這個示例的方法調用了log對象的write方法:

var writeToLog = L.rpc.declare({
	object: 'log',
	method: 'write',
	params: [ 'event' ]
})

定義了一次這個方法后,可以在任意時間通過如下示例簡單調用:

writeToLog('Foo');

在上面的例子中執行結果被忽略了,如果視圖需要處理ubus返回的數據時不能忽略執行結果。下面例子將通過訪問system對象的info方法來描述返回結果的處理。在命令行下通過以下命令訪問:

# ubus call system info
{
	"uptime": 123,
	"localtime": 1234567890,
	"load": [
		1,
		2,
		3
	],
	"memory": {
		"total": 67108864,
		"free": 33554432,
		"shared": 0,
		"buffered": 16777216
	},
	"swap": {
		"total": 0,
		"free": 0
	}
}

在LuCI2(JavaScript)定義上面的訪問如下所示:

var readSystemInfo = L.rpc.declare({
	object: 'system',
	method: 'info',
	expect: { memory: { } }			/* 可選, 只提取結果的一部分memory */
})

通過簡單的調用.then方法可訪問結果數據

readSystemInfo().then(function(memory) {
	console.log(memory);
});

怎樣測試

在"feeds.conf"中添加一個feed:

src-git luci2 git://git.openwrt.org/project/luci2/ui.git

安裝luci2包:

./scripts/feeds update
./scripts/feeds install luci2

在menuconfig中勾選Luci2包並編譯新固件。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM