From 7552fce8cc220e378c6b28384cc759aba437ce13 Mon Sep 17 00:00:00 2001 From: Karl Palsson Date: Tue, 18 Sep 2018 12:47:40 +0000 Subject: [PATCH] luci-app-mosquitto: import new application Originally developed and maintained at https://github.com/remakeelectric/owrt_pub_feeds/tree/master/luci-app-mosquitto Vs the original, this version has been converted to use the luci.mk style, and has dropped the now obsolete ucitrack entries. Signed-off-by: Karl Palsson --- applications/luci-app-mosquitto/Makefile | 13 + .../luasrc/controller/mosquitto.lua | 16 ++ .../luasrc/model/cbi/mosquitto.lua | 229 ++++++++++++++++++ 3 files changed, 258 insertions(+) create mode 100644 applications/luci-app-mosquitto/Makefile create mode 100644 applications/luci-app-mosquitto/luasrc/controller/mosquitto.lua create mode 100644 applications/luci-app-mosquitto/luasrc/model/cbi/mosquitto.lua diff --git a/applications/luci-app-mosquitto/Makefile b/applications/luci-app-mosquitto/Makefile new file mode 100644 index 000000000..1bfb7c3de --- /dev/null +++ b/applications/luci-app-mosquitto/Makefile @@ -0,0 +1,13 @@ +include $(TOPDIR)/rules.mk + +LUCI_TITLE:=Mosquitto LuCI interface +LUCI_DEPENDS:=+luci-mod-admin-full mosquitto +LUCI_PKGARCH:=all +LUCI_DESCRIPTION:=Provides a webadmin for most basic mosquitto parameters. + +PKG_MAINTAINER:= Karl Palsson +PKG_LICENSE:=Apache-2.0 + +include ../../luci.mk + +# call BuildPackage - OpenWrt buildroot signature diff --git a/applications/luci-app-mosquitto/luasrc/controller/mosquitto.lua b/applications/luci-app-mosquitto/luasrc/controller/mosquitto.lua new file mode 100644 index 000000000..587054eb1 --- /dev/null +++ b/applications/luci-app-mosquitto/luasrc/controller/mosquitto.lua @@ -0,0 +1,16 @@ +--[[ +LuCI - Lua Configuration Interface for mosquitto + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +]]-- + +module("luci.controller.mosquitto", package.seeall) + +function index() + entry({"admin", "services", "mosquitto"}, cbi("mosquitto"), _("Mosquitto")) +end diff --git a/applications/luci-app-mosquitto/luasrc/model/cbi/mosquitto.lua b/applications/luci-app-mosquitto/luasrc/model/cbi/mosquitto.lua new file mode 100644 index 000000000..78bc9f071 --- /dev/null +++ b/applications/luci-app-mosquitto/luasrc/model/cbi/mosquitto.lua @@ -0,0 +1,229 @@ +--[[ +LuCI model for mosquitto MQTT broker configuration management +Copyright eTactica ehf, 2018 + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +]]-- + +local datatypes = require("luci.cbi.datatypes") + +--- Like a Flag, but with an option to remove/set to default. +local function OptionalFlag(section, key, title, description) + local o = section:option(ListValue, key, title, description) + o.optional = true + o:value("", "Default") + o:value("1", "Enabled") + o:value("0", "Disabled") + return o +end + +m = Map("mosquitto", "Mosquitto MQTT Broker", + [[mosquitto - the blood thirsty +MQTT messaging broker. Note, only some of the available configuration files + are supported at this stage, use the checkbox below to use config generated + by this page, or the stock mosquitto configuration file in + /etc/mosquitto/mosquitto.conf]]) + +s = m:section(TypedSection, "owrt", "OpenWRT") +s.anonymous = true +p = s:option(Flag, "use_uci", "Use this LuCI configuration page", + [[If checked, mosquitto runs with a config generated + from this page. (Or from UCI directly) If unchecked, mosquitto + runs with the config in /etc/mosquitto/mosquitto.conf + (and this page is ignored)]]) + +s = m:section(TypedSection, "mosquitto", "Mosquitto") +s.anonymous = true + +p = s:option(MultiValue, "log_dest", "Log destination", + "You can have multiple, but 'none' will override all others") +p:value("stderr", "stderr") +p:value("stdout", "stdout") +p:value("syslog", "syslog") +p:value("topic", "$SYS/broker/log/[severity]") +p:value("none", "none") + +OptionalFlag(s, "no_remote_access", "Disallow remote access to this broker", + [[Outbound bridges will still work, but this will make the primary listener + only available from localhost]]) + +local o +o = s:option(Value, "sys_interval", "Time in seconds between updates of the $SYS tree", "Set to zero to disable") +o.datatype = "uinteger" +o.optional = true + +o = s:option(Value, "max_inflight_messages", "Max Inflight Messages", "Limit for message allowed inflight") +o.datatype = "uinteger" +o.optional = true +o = s:option(Value, "max_queued_messages", "Max Queued Messages", "Limit for message queue when offline") +o.datatype = "uinteger" +o.optional = true +o = s:option(Value, "max_queued_bytes", "Max Queued bytes", "Limit for message queue when offline, zero to disable)") +o.datatype = "uinteger" +o.optional = true + + +s = m:section(TypedSection, "persistence", "Persistence") +s.anonymous = true +s.addremove = false +s:option(Flag, "persistence", "Persistence enabled", "Should persistence to disk be enabled at all").rmempty = false +o = s:option(Value, "client_expiration", "Client expiration", "Remove persistent clients if they haven't reconnected in this period, eg 6h, 3d, 2w") +o.optional = true +o:depends("persistence", true) +o = OptionalFlag(s, "autosave_on_changes", "Autosave on changes", "Autosave interval applies to change counts instead of time") +o:depends("persistence", true) +o = s:option(Value, "autosave_interval", "Autosave interval", "Save persistence file after this many seconds or changes") +o.optional = true +o:depends("persistence", true) +o = s:option(Value, "file", "Persistent file name") +o.optional = true +o:depends("persistence", true) +o = s:option(Value, "location", "Persistent file path (with trailing/)", "Path to persistent file") +o.optional = true +o:depends("persistence", true) + +s = m:section(TypedSection, "listener", "Listeners", "You can configure additional listeners here") +s.addremove = true +s.anonymous = true +s:option(Value, "port", "Port").datatype = "port" + +o = s:option(ListValue, "protocol", "Protocol to use when listening") +o:value("", "Default") +o:value("mqtt", "MQTT") +o:value("websockets", "WebSockets") + +s:option(Value, "http_dir", "http_dir to serve on websockets listeners").optional = true +OptionalFlag(s, "use_username_as_clientid", "use_username_as_clientid") +o = s:option(Value, "cafile", "CA file path") +o.optional = true +o.datatype = "file" +o = s:option(Value, "capath", "CA path to search") +o.optional = true +o.datatype = "file" +o = s:option(Value, "certfile", "server certificate file (PEM encoded)") +o.optional = true +o.datatype = "file" +o = s:option(Value, "keyfile", "keyfile (PEM encoded)") +o.optional = true +o.datatype = "file" + +o = s:option(ListValue, "tls_version", "TLS Version", + "Depends on your openssl version, empty to support all") +o.optional = true +o:value("", "Default") +o:value("tlsv1") +o:value("tlsv1.1") +o:value("tlsv1.2") + +OptionalFlag(s, "require_certificate", "Require clients to present a certificate") +OptionalFlag(s, "use_identity_as_username", "use_identity_as_username") +s:option(Value, "crlfile", "CRL to use if require_certificate is enabled").optional = true +s:option(Value, "ciphers", "Ciphers control. Should match 'openssl ciphers' format").optional = true +s:option(Value, "psk_hint", "PSK Hint to provide to connecting clients").optional = true + +-- we want to allow multiple bridge sections +s = m:section(TypedSection, "bridge", "Bridges", + "You can configure multiple bridge connections here") +s.anonymous = true +s.addremove = true + +conn = s:option(Value, "connection", "Connection name", + "unique name for this bridge configuration") + +local function validate_address(self, value) + local host, port = unpack(luci.util.split(value, ":")) + if (datatypes.host(host)) then + if port and #port then + if not datatypes.port(port) then + return nil, "Please enter a valid port after the :" + end + end + return value + end + return nil, "Please enter a hostname or an IP address" +end + +addr = s:option(Value, "address", "address", "address[:port] of remote broker") +addr.datatype = "string" +addr.validate = validate_address + +-- TODO - make the in/out/both a dropdown/radio or something.... +topics = s:option(DynamicList, "topic", "topic", + "full topic string for mosquitto.conf, eg: 'power/# out 2'") + +OptionalFlag(s, "cleansession", "Clean session") +OptionalFlag(s, "notifications", "notifications", + "Attempt to notify the local and remote broker of connection status, defaults to $SYS/broker/connections//state") +s:option(Value, "notification_topic", "Topic to use for local+remote remote for notifications.").optional = true + +s:option(Value, "remote_clientid", "Client id to use on remote end of this bridge connection").optional = true +s:option(Value, "local_clientid", "Client id to use locally. Important when bridging to yourself").optional = true +o = s:option(Value, "keepalive_interval", "Keepalive interval for this bridge") +o.datatype = "uinteger" +o.optional = true +o = s:option(ListValue, "start_type", "How should this bridge be started") +o.optional = true +o:value("", "Default") +o:value("automatic", "Automatic, includes restarts") +o:value("lazy", "Automatic, but stopped when not used") +o:value("once", "Automatic, but no restarts") +o = s:option(Value, "restart_timeout", "How long to wait before reconnecting") +o.datatype = "uinteger" +o.optional = true +o = s:option(Value, "idle_timeout", "How long to wait before disconnecting") +o.datatype = "uinteger" +o.optional = true +o = s:option(Value, "threshold", "How many messages to queue before restarting lazy bridge") +o.datatype = "uinteger" +o.optional = true + +OptionalFlag(s, "try_private", "try_private", + "attempt to notify the remote broker that this is a bridge, not all brokers support this.") +s:option(Value, "remote_username", "Remote username").optional = true +o = s:option(Value, "remote_password", "Remote password") +o.optional = true +o.password = true + +s:option(Value, "identity", "PSK Bridge Identity", "Identity for TLS-PSK").optional = true + +-- no hex validation available in datatypes +local function validate_psk_key(self, value) + if (value:match("^[a-fA-F0-9]+$")) then + return value + end + return nil, "Only hex numbers are allowed (use A-F characters and 0-9 digits)" +end + +psk_key = s:option(Value, "psk", "Bridge PSK", "Key for TLS-PSK") +psk_key.password = true +psk_key.optional = true +psk_key.datatype = "string" +psk_key.validate = validate_psk_key + +b_tls_version = s:option(ListValue, "tls_version", "TLS Version", + "The remote broker must support the same version of TLS for the connection to succeed.") +b_tls_version:value("", "Default") +b_tls_version:value("tlsv1") +b_tls_version:value("tlsv1.1") +b_tls_version:value("tlsv1.2") +b_tls_version.optional = true + +o = s:option(Value, "cafile", "Path to CA file") +o.optional = true +o.datatype = "file" +o = s:option(Value, "capath", "Directory to search for CA files") +o.optional = true +o.datatype = "file" +o = s:option(Value, "certfile", "Path to PEM encoded server certificate file") +o.optional = true +o.datatype = "file" +o = s:option(Value, "keyfile", "Path to PEM encoded keyfile") +o.optional = true +o.datatype = "file" + +return m -- 2.25.1