LuCI2 (OpenWrt web 管理界面) 介绍

LuCI2是OpenWrt的新一代Web管理界面,采用静态HTML和JavaScript XHR与ubus通信,减少资源消耗。它不再依赖Lua扩展,而是通过uhttpd-mod-ubus提供HTTP接口API。LuCI2包含打包的HTML/CSS/JS文件和运行于OpenWrt环境的小工具。菜单、模板和视图是其核心组成部分,通过ubus进行系统数据交互。
摘要由CSDN通过智能技术生成

https://oldwiki.archive.openwrt.org/zh-cn/doc/techref/luci2

一直以来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通过ubusOpenWrt子系统通信(包括如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
}

注意,第二层菜单项可以在独立的文件中定义,这样就可以方便的添加新的菜单项定义而不用修改原有的文件。

下面是源码“feeds\luci2\luci2\share\menu.d”中status这个菜单对应的源文件status.json的内容;

 

{

    "status": {

        "title": "Status",

        "index": 10

    },

    "status/overview": {

        "title": "Overview", 在菜单上文字显示为”Overview”

        "acls": [ "status" ],

        "view": "status/overview",

        "index": 10  同级菜单项通过index进行排序显示

    },

    "status/routes": {

        "title": "Routes",在菜单上文字显示为” Routes”

        "acls": [ "status" ],

        "view": "status/routes",

        "index": 20

    },

    "status/syslog": {

        "title": "System Log",

        "acls": [ "status" ],

        "view": "status/syslog",

        "index": 30

    },

    "status/dmesg": {

        "title": "Kernel Log",

        "acls": [ "status" ],

        "view": "status/dmesg",

        "index": 40

    },

    "status/processes": {

        "title": "Processes",

        "acls": [ "status" ],

        "view": "status/processes",

        "index": 50

    }

}

 

 

 

模板

每一个LuCI2子页面必须有一个存放在/www/luci2/template/目录下的模板。它们提供了非常简单的内容替换区的HTML文件。需要注意的是它们不包含任何变量的引用,这是JavaScript的功劳:用JavaScript去读取并写入内容。在为本地化系统(i18n)这些文件中唯一特定的语法类似下面的标记:

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

 

下图是源码中定义的每一个luCI2子页面对应的模板;

feeds/luci2/luci2/htdocs/luci2/template/

├── network.diagnostics.htm

├── network.hosts.htm

├── network.interfaces.htm

├── network.routes.htm

├── network.switch.htm

├── network.wireless.htm

├── status.dmesg.htm

├── status.overview.htm

├── status.processes.htm

├── status.routes.htm

├── status.syslog.htm

├── system.admin.htm

├── system.cron.htm

├── system.leds.htm

├── system.software.htm

├── system.startup.htm

├── system.system.htm

├── system.upgrade.htm

└── system.users.htm

 

 

 

视图

从模板中分离,每个LuCI2子页面也需要一个定义并存放在/www/luci2/view/目录下的一个视图,视图是使用了子页面特定对象的L.ui.view扩展的JavaScript文件。Javascript中需要提供execute方法的实现,该方法将在模板加载后被执行。可选的你也可以提供子页面的titledescription属性。

下图是LuCI2源码中对应的页面源码,

feeds/luci2/luci2/htdocs/luci2/view

├── network.diagnostics.js

├── network.hosts.js

├── network.interfaces.js

├── network.routes.js

├── network.switch.js

├── network.wireless.js

├── status.dmesg.js

├── status.overview.js

├── status.processes.js

├── status.routes.js

├── status.syslog.js

├── system.admin.js

├── system.cron.js

├── system.leds.js

├── system.software.js

├── system.startup.js

├── system.system.js

├── system.upgrade.js

└── system.users.js

 

 

有一点需要注意的是需要提供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系统的一些方法。LUCI2提供两种方式访问后台数据;

  1. 通过UCI系统
  2. 通过ubus通讯

可以介绍如何通过ubus通讯读写后台数据;如果通过UCI系统简单的管理/etc/config/下配置文件不需要完全知道ubus的调用方法,则可以跳过该部分。

构建一些更复杂的LuCI2视图前最好先弄明白使用到的ubus调用。完整的对象和方法列表可以通过运行ubus -v list命令来得到。

下面这个简单的例子调用了log对象的write方法,这个方法需要提供一个event参数。可以在命令行中测试一个ubus通讯,如下所示:

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

LuCI2ubus通讯提供了一个叫做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:

# 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/luci2/luci2/htdocs/luci2/session.js”中使用ubus的情况

 

Class.extend({

       login: L.rpc.declare({

              object: 'session',

              method: 'login',

              params: [ 'username', 'password' ],

              expect: { '': { } }

       }),

上面这个函数处理登陆认证;使用到”session”对象的“login”方法;需要两个参数'username', 'password';也就是用户名和密码;

 

       access: L.rpc.declare({

              object: 'session',

              method: 'access',

              params: [ 'scope', 'object', 'function' ],

              expect: { access: false }

       }),

 

 

 

怎样测试

"feeds.conf"中添加一个feed:

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

安装luci2:

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

menuconfig中勾选Luci2包并编译新固件。

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值