schema: more fixes for issues that came up while testing on device

Signed-off-by: John Crispin <john@phrozen.org>
This commit is contained in:
John Crispin
2021-05-14 16:52:59 +02:00
parent 339994a865
commit 54af4e6321
22 changed files with 265 additions and 152 deletions

10
command/cmd.uc Normal file → Executable file
View File

@@ -1,5 +1,13 @@
#!/usr/bin/ucode
{%
let fs = require("fs");
let uci = require("uci");
let ubus = require("ubus");
let capabfile = fs.open("/etc/ucentral/capabilities.json", "r");
let capab = json(capabfile.read("all"));
let cmdfile = fs.open(ARGV[2], "r");
let cmd = json(cmdfile.read("all"));
let id = ARGV[3];
let ctx = ubus.connect();
if (!ctx) {

View File

@@ -1,11 +1,6 @@
{%
let verbose = args.verbose ? true : false;
if (args.bands) {
for (let band in args.bands)
ctx.call("wifi", "scan", { band });
} else {
ctx.call("wifi", "scan");
}
ctx.call("wifi", "scan");
system("sleep 5");
let scan = ctx.call("wifi", "scan_dump", {verbose});
let survey = ctx.call("wifi", "survey");

View File

@@ -5,6 +5,7 @@
let uci = require("uci");
let ubus = require("ubus");
let math = require("math");
let cursor = uci ? uci.cursor() : null;
let conn = ubus ? ubus.connect() : null;
@@ -176,12 +177,22 @@ let ethernet = {
return sort(keys(matched));
},
calculate_names: function(interface) {
calculate_name: function(interface) {
let vid = interface.vlan ? interface.vlan.id : '';
let name = interface.role + vid;
let ipv4_mode = interface.ipv4 ? interface.ipv4.addressing : 'none';
return (interface.role == 'upstream' ? 'wan' : 'lan') + vid;
},
calculate_names: function(interface) {
let name = this.calculate_name(interface);
let ipv4_mode = interface.ipv4 ? interface.ipv4.addressing : 'none';
let ipv6_mode = interface.ipv6 ? interface.ipv6.addressing : 'none';
return name;
return (
(ipv4_mode == 'none') || (ipv6_mode == 'none') ||
(ipv4_mode == 'static' && ipv6_mode == 'static')
) ? [ name ] : [ name + '_4', name + '_6' ];
}
};

View File

@@ -0,0 +1,14 @@
# Basic configuration
set network.loopback=interface
set network.loopback.ifname='lo'
set network.loopback.proto='static'
set network.loopback.ipaddr='127.0.0.1'
set network.loopback.netmask='255.0.0.0'
add network device
set network.@device[-1].name=up
set network.@device[-1].type=bridge
add network device
set network.@device[-1].name=down
set network.@device[-1].type=bridge

View File

@@ -1,4 +1,6 @@
{%
let math = require("math");
// Skip interfaces previously marked as conflicting.
if (interface.conflicting) {
warn("Skipping conflicting interface declaration");
@@ -7,14 +9,7 @@
}
// Check this interface for role/vlan uniqueness...
let this_vid = interface.vlan ? interface.vlan.id : '';
if (!this_vid) {
if (interface.role == 'upstream')
this_vid = 2;
else
this_vid = 1;
}
let this_vid = interface.vlan ? interface.vlan.id : 1;
for (let other_interface in state.interfaces) {
if (other_interface == interface)
@@ -28,6 +23,12 @@
}
}
// check if a downstream interface with a vlan has a matching upstream interface
if (interface.vlan && interface.role == "downstream" && index(vlans, this_vid) < 0) {
warn("Trying to create a downstream interface with a VLAN ID, without matching upstream interface.");
return;
}
// Gather related BSS modes and ethernet ports.
let bss_modes = map(interface.ssids, ssid => ssid.bss_mode);
let eth_ports = ethernet.lookup_by_interface_spec(interface);
@@ -41,27 +42,15 @@
return;
}
// Compute unique logical name and netdev name to use
let name = ethernet.calculate_names(interface);
let bridgedev = 'bridge';
let netdev = '';
// store the VLAN id assigned to this interface
push(vlans, this_vid);
// If this interface enables host isolation, we need to turn it into a
// dedicated isolated netdev...
if (interface.isolate_hosts) {
// If there are any ethernet ports or multiple SSIDs participating,
// we need to spawn a new bridge interface.
if (length(eth_ports) > 0 || length(bss_modes) > 1)
netdev = 'br-' + name;
}
// ... upstream interfaces with a vlan require the @upstream syntax
else if (interface.role == 'upstream' && interface.vlan) {
netdev = "@upstream." + this_vid;
}
// ... otherwise we program a VLAN on top of the global bridge.
else {
netdev = bridgedev + '.' + this_vid;
}
// Compute unique logical name and netdev name to use
let name = ethernet.calculate_name(interface);
let bridgedev = 'up';
if (interface.role == "downstream")
bridgedev = 'down';
let netdev = bridgedev + '.' + this_vid;
// Determine the IPv4 and IPv6 configuration modes and figure out if we
// can set them both in a single interface (dualstack) or whether we need
@@ -72,38 +61,23 @@
(ipv4_mode == 'none') || (ipv6_mode == 'none') ||
(ipv4_mode == 'static' && ipv6_mode == 'static')
);
// upstream interfaces always have a routing metric of 0
if (interface.role == "upstream")
interface.metric = 0;
%}
# Network configuration
add network interface
set networ.@interface[-1].ifname='lo'
set networ.@interface[-1].proto='static'
set networ.@interface[-1].ipaddr='127.0.0.1'
set networ.@interface[-1].netmask='255.0.0.0'
add network device
set network.@device[-1].name=bridge
set network.@device[-1].type=bridge
{% if (interface.isolate_hosts && netdev): %}
set network.{{ name }}_dev=device
set network.{{ name }}_dev.type=bridge
set network.{{ name }}_dev.name={{ netdev }}
add network bridge-vlan
set network.@bridge-vlan[-1].device={{ bridgedev }}
set network.@bridge-vlan[-1].vlan={{ this_vid }}
{% for (let i, port in eth_ports): %}
{{ i ? 'add_list' : 'set' }} network.{{ name }}_dev.ifname={{ port }}
{{ i ? 'add_list' : 'set' }} network.@bridge-vlan[-1].ports={{ port }}{{ ((interface.role == 'upstream') && interface.vlan) ? ':t' : '' }}
{% endfor %}
{% elif (!interface.isolate_hosts): %}
set network.{{ name }}_vlan=bridge-vlan
set network.{{ name }}_vlan.device={{ bridgedev }}
set network.{{ name }}_vlan.vlan={{ this_vid }}
{% for (let i, port in eth_ports): %}
{{ i ? 'add_list' : 'set' }} network.{{ name }}_vlan.ports={{ port }}{{ ((interface.role == 'upstream') && interface.vlan) ? ':t' : '' }}
{% endfor %}
{% endif %}
{% if (use_dualstack): %}
set network.{{ name }}=interface
set network.{{name}}=interface
set network.{{ name }}.ucentral_name={{ s(interface.name) }}
set network.{{ name }}.ucentral_path={{ s(location) }}
set network.{{ name }}.ifname={{ netdev }}
set network.{{ name }}.metric={{ interface.metric }}
{% if (ipv4_mode == 'static'): %}
@@ -121,17 +95,17 @@ set network.{{ name }}.peerdns={{ b(!length(interface.ipv4.use_dns)) }}
{% else %}
set network.{{ name }}.proto=dhcpv6
{% endif %}
{% if (interface.role == 'upstream'): %}
{% if (interface.role == 'upstream' && interface.vlan): %}
set network.{{ name }}.ip4table={{ this_vid }}
set network.{{ name }}.ip6table={{ this_vid }}
{% endif %}
{% else %}
{% if (ipv4_mode != 'none'): %}
set network.{{ name }}_4=interface
set network.{{name}}=interface_4
set network.{{ name }}_4.ucentral_name={{ s(interface.name) }}
set network.{{ name }}_4.ifname={{ netdev }}
set network.{{ name }}_4.metric={{ interface.metric }}
{% if (interface.role == 'upstream'): %}
{% if (interface.role == 'upstream' && interface.vlan): %}
set network.{{ name }}_4.ip4table={{ this_vid }}
{% endif %}
{% if (ipv4_mode == 'static'): %}
@@ -142,11 +116,11 @@ set network.{{ name }}_4.proto=dhcp
{% endif %}
{% endif %}
{% if (ipv6_mode != 'none'): %}
set network.{{ name }}_6=interface
set network.{{name}}=interface_6
set network.{{ name }}_6.ucentral_name={{ s(interface.name) }}
set network.{{ name }}_6.ifname={{ netdev }}
set network.{{ name }}_6.metric={{ interface.metric }}
{% if (interface.role == 'upstream'): %}
{% if (interface.role == 'upstream' && interface.vlan): %}
set network.{{ name }}_6.ip6table={{ this_vid }}
{% endif %}
{% if (ipv6_mode == 'static'): %}
@@ -161,27 +135,19 @@ set network.{{ name }}_6.proto=dhcp
{% if (use_dualstack && interface.role == "downstream" && interface.vlan): %}
add network rule
set network.@rule[-1].in={{ name }}
set network.@rule[-1].lookup={{ this_vid }}
set network.@rule[-1].lookup={{ interface.vlan.id }}
{% endif %}
{%
include('interface/firewall.uc', {
interface,
networks: use_dualstack ? [ name ] : [ name + '_4', name + '_6' ]
});
include('interface/firewall.uc');
if (interface.ipv4)
include('interface/dhcp.uc', {
interface,
name: use_dualstack ? name : name + '_4'
});
include('interface/dhcp.uc');
for (let i, ssid in interface.ssids) {
include('ssid.uc', {
location: location + '/ssids/' + i,
ssid,
interface,
networks: use_dualstack ? [ name ] : [ name + '_4', name + '_6' ]
ssid
});
}
%}

View File

@@ -1,4 +1,4 @@
{% let name = ethernet.calculate_name(interface) %}
{% let dhcp = interface.ipv4.dhcp || { ignore: 1 } %}
add dhcp dhcp
set dhcp.@dhcp[-1].interface={{ s(name) }}

View File

@@ -1,8 +1,8 @@
{% for (let n, network in networks): %}
{% if (interface.role == upstream): %}
{% for (let name in ethernet.calculate_names(interface)): %}
{% if (interface.role == "upstream"): %}
add firewall zone
set firewall.@zone[-1].name={{ s(network) }}
set firewall.@zone[-1].network={{ s(network) }}
set firewall.@zone[-1].name={{ s(name) }}
set firewall.@zone[-1].network={{ s(name) }}
set firewall.@zone[-1].input='REJECT'
set firewall.@zone[-1].output='ACCEPT'
set firewall.@zone[-1].forward='REJECT'
@@ -10,14 +10,14 @@ set firewall.@zone[-1].masq=1
set firewall.@zone[-1].mtu_fix=1
{% else %}
add firewall zone
set firewall.@zone[-1].name={{ s(network) }}
set firewall.@zone[-1].network={{ s(network) }}
set firewall.@zone[-1].name={{ s(name) }}
set firewall.@zone[-1].network={{ s(name) }}
set firewall.@zone[-1].input='ACCEPT'
set firewall.@zone[-1].output='ACCEPT'
set firewall.@zone[-1].forward='ACCEPT'
add firewall forwarding
set firewall.@forwarding[-1].src={{ network }}
set firewall.@forwarding[-1].dest=upstream{{ interface.vlan ? interface.vlan.id : '' }}
set firewall.@forwarding[-1].src={{ name }}
set firewall.@forwarding[-1].dest='wan{{ interface.vlan ? interface.vlan.id : '' }}'
{% endif %}
{% endfor %}

View File

@@ -96,7 +96,7 @@ set wireless.{{ phy.section }}.require_mode={{ s(match_require_mode(radio.requir
set wireless.{{ phy.section }}.txpower={{ radio.tx_power }}
set wireless.{{ phy.section }}.legacy_rates={{ b(radio.legacy_rates) }}
set wireless.{{ phy.section }}.chan_bw={{ radio.bandwidth }}
{% if (phy.he_mac_capa && match(htmode, /HE.*/)): %}
{% if (radio.he_settings && phy.he_mac_capa && match(htmode, /HE.*/)): %}
set wireless.{{ phy.section }}.he_bss_color={{ radio.he_settings.bss_color }}
set wireless.{{ phy.section }}.multiple_bssid={{ b(radio.he_settings.multiple_bssid) }}
set wireless.{{ phy.section }}.ema={{ b(radio.he_settings.ema) }}

View File

@@ -44,51 +44,54 @@
{% let id = wiphy.allocate_ssid_section_id(phy) %}
{% let crypto = validate_encryption(); %}
{% if (!crypto) continue; %}
set wireless.{{ id }}=wifi-iface
set wireless.{{ id }}.device={{ phy.section }}
{% for (let i, network in networks): %}
{{ i ? 'add_list' : 'set' }} wireless.{{ id }}.network={{ network }}
add wireless wifi-iface
set wireless.@wifi-iface[-1].ucentral_path={{ s(location) }}
set wireless.@wifi-iface[-1].device={{ phy.section }}
{% for (let i, name in ethernet.calculate_names(interface)): %}
{{ i ? 'add_list' : 'set' }} wireless.@wifi-iface[-1].network={{ name }}
{% endfor %}
set wireless.{{ id }}.ssid={{ s(ssid.name) }}
set wireless.{{ id }}.mode={{ ssid.bss_mode }}
set wireless.{{ id }}.bssid={{ ssid.bssid }}
set wireless.{{ id }}.hidden={{ b(ssid.hidden_ssid) }}
set wireless.{{ id }}.time_advertisement={{ ssid.broadcast_time }}
set wireless.{{ id }}.isolate={{ b(ssid.isolate_clients) }}
set wireless.{{ id }}.uapsd={{ b(ssid.power_save) }}
set wireless.{{ id }}.rts_threshold={{ ssid.rts_threshold }}
set wireless.{{ id }}.multicast_to_unicast={{ b(ssid.unicast_conversion) }}
set wireless.{{ id }}.beacon_rate={{ ssid.rates.beacon }}
set wireless.{{ id }}.mcast_rate={{ ssid.rates.multicast }}
set wireless.@wifi-iface[-1].ssid={{ s(ssid.name) }}
set wireless.@wifi-iface[-1].mode={{ ssid.bss_mode }}
set wireless.@wifi-iface[-1].bssid={{ ssid.bssid }}
set wireless.@wifi-iface[-1].hidden={{ b(ssid.hidden_ssid) }}
set wireless.@wifi-iface[-1].time_advertisement={{ ssid.broadcast_time }}
set wireless.@wifi-iface[-1].isolate={{ b(ssid.isolate_clients) }}
set wireless.@wifi-iface[-1].uapsd={{ b(ssid.power_save) }}
set wireless.@wifi-iface[-1].rts_threshold={{ ssid.rts_threshold }}
set wireless.@wifi-iface[-1].multicast_to_unicast={{ b(ssid.unicast_conversion) }}
{% if (ssid.rates): %}
set wireless.@wifi-iface[-1].beacon_rate={{ ssid.rates.beacon }}
set wireless.@wifi-iface[-1].mcast_rate={{ ssid.rates.multicast }}
{% endif %}
{% if (ssid.rrm): %}
set wireless.{{ id }}.ieee80211k={{ b(ssid.rrm.neighbor_reporting) }}
set wireless.{{ id }}.ftm_responder={{ b(ssid.rrm.ftm_responder) }}
set wireless.{{ id }}.stationary_ap={{ b(ssid.rrm.stationary_ap) }}
set wireless.{{ id }}.lci={{ b(ssid.rrm.lci) }}
set wireless.{{ id }}.civic={{ ssid.rrm.civic }}
set wireless.@wifi-iface[-1].ieee80211k={{ b(ssid.rrm.neighbor_reporting) }}
set wireless.@wifi-iface[-1].ftm_responder={{ b(ssid.rrm.ftm_responder) }}
set wireless.@wifi-iface[-1].stationary_ap={{ b(ssid.rrm.stationary_ap) }}
set wireless.@wifi-iface[-1].lci={{ b(ssid.rrm.lci) }}
set wireless.@wifi-iface[-1].civic={{ ssid.rrm.civic }}
{% endif %}
{% if (ssid.roaming): %}
set wireless.{{ id }}.ieee80211r=1
set wireless.{{ id }}.ft_over_ds={{ b(ssid.roaming.message_exchange == "ds") }}
set wireless.{{ id }}.ft_psk_generate_local={{ b(ssid.roaming.generate_psk) }}
set wireless.{{ id }}.mobility_domain={{ ssid.roaming.domain_identifier }}
set wireless.@wifi-iface[-1].ieee80211r=1
set wireless.@wifi-iface[-1].ft_over_ds={{ b(ssid.roaming.message_exchange == "ds") }}
set wireless.@wifi-iface[-1].ft_psk_generate_local={{ b(ssid.roaming.generate_psk) }}
set wireless.@wifi-iface[-1].mobility_domain={{ ssid.roaming.domain_identifier }}
{% endif %}
set wireless.{{ id }}.ieee80211w={{ match_ieee80211w() }}
set wireless.{{ id }}.encryption={{ crypto.proto }}
set wireless.{{ id }}.key={{ crypto.key }}
set wireless.@wifi-iface[-1].ieee80211w={{ match_ieee80211w() }}
set wireless.@wifi-iface[-1].encryption={{ crypto.proto }}
set wireless.@wifi-iface[-1].key={{ crypto.key }}
{% if (crypto.auth): %}
set wireless.{{ id }}.auth_server={{ crypto.auth.host }}
set wireless.{{ id }}.auth_port={{ crypto.auth.port }}
set wireless.{{ id }}.auth_secret={{ crypto.auth.secret }}
set wireless.@wifi-iface[-1].auth_server={{ crypto.auth.host }}
set wireless.@wifi-iface[-1].auth_port={{ crypto.auth.port }}
set wireless.@wifi-iface[-1].auth_secret={{ crypto.auth.secret }}
{% endif %}
{% if (crypto.acct): %}
set wireless.{{ id }}.acct_server={{ crypto.acct.host }}
set wireless.{{ id }}.acct_port={{ crypto.acct.port }}
set wireless.{{ id }}.acct_secret={{ crypto.acct.secret }}
set wireless.{{ id }}.acct_interval={{ crypto.acct.interval }}
set wireless.@wifi-iface[-1].acct_server={{ crypto.acct.host }}
set wireless.@wifi-iface[-1].acct_port={{ crypto.acct.port }}
set wireless.@wifi-iface[-1].acct_secret={{ crypto.acct.secret }}
set wireless.@wifi-iface[-1].acct_interval={{ crypto.acct.interval }}
{% endif %}
{% if (ssid.rate_limit.ingress_rate || ssid.rate_limit.egress_rate): %}
{% if (ssid.rate_limit && (ssid.rate_limit.ingress_rate || ssid.rate_limit.egress_rate)): %}
add ratelimit rate
set ratelimit.@rate[-1].ssid={{ s(ssid.name) }}
set ratelimit.@rate[-1].ingress={{ ssid.rate_limit.ingress_rate }}

View File

@@ -1,5 +1,22 @@
{%
include('unit.uc', { location: '/unit', unit: state.unit });
// reject the config if there is no valid upstream configuration
let upstream;
for (let i, interface in state.interfaces) {
if (interface.role != 'upstream')
continue;
upstream = interface;
}
if (!upstream) {
location = '/';
warn('Configuration must contain at least one valid upstream interface. Rejecting whole file');
die('Configuration must contain at least one valid upstream interface. Rejecting whole file');
}
include('base.uc');
if (state.unit)
include('unit.uc', { location: '/unit', unit: state.unit });
for (let service in state.services)
include('services/' + service + '.uc', {
@@ -16,8 +33,17 @@
for (let i, radio in state.radios)
include('radio.uc', { location: '/radios/' + i, radio });
for (let i, interface in state.interfaces)
include('interface.uc', { location: '/interfaces/' + i, interface });
let vlans = [];
function iterate_interfaces(role) {
for (let i, interface in state.interfaces) {
if (interface.role != role)
continue;
include('interface.uc', { location: '/interfaces/' + i, interface, vlans });
}
}
iterate_interfaces("upstream");
iterate_interfaces("downstream");
if (state.config_raw)
include("config_raw.uc", { location: '/config_raw', config_raw: state.config_raw });

View File

@@ -8,13 +8,15 @@ let schemareader = require("schemareader");
let renderer = require("renderer");
let fs = require("fs");
let inputfile = fs.open("/tmp/test.json", "r");
let inputfile = fs.open(ARGV[2], "r");
let inputjson = json(inputfile.read("all"));
let error = 0;
inputfile.close();
let logs = [];
try {
let logs = [];
let batch = renderer.render(schemareader.validate(inputjson), logs);
fs.stdout.write("Log messages:\n" + join("\n", logs) + "\n\n");
@@ -35,10 +37,28 @@ try {
for (let cmd in [ 'uci -c /tmp/config-shadow commit',
'cp /tmp/config-shadow/* /etc/config/',
'reload_config', 'rm -rf /tmp/config-shadow' ])
'rm -rf /tmp/config-shadow',
'reload_config'])
system(cmd);
fs.unlink('/etc/ucentral/ucentral.active');
fs.symlink(ARGV[2], '/etc/ucentral/ucentral.active');
}
catch (e) {
error = 1;
warn("Fatal error while generating UCI: ", e, "\n", e.stacktrace[0].context, "\n");
}
let ubus = require("ubus").connect();
ubus.call("ucentral", "result", {
uuid: inputjson.uuid || 0,
id: +ARGV[3] || 0,
status: {
error,
text: error ? "Failed" : "Success",
rejected: logs || []
}
});
%}

View File

@@ -29,6 +29,8 @@ properties:
- ap
- sta
- mesh
- wds
default: ap
bssid:
description:
Override the BSSID of the network, only applicable in adhoc or sta mode.

View File

@@ -17,3 +17,4 @@ properties:
enum:
- 802.1ad
- 802.1q
default: 802.1q

View File

@@ -12,6 +12,7 @@ properties:
description:
The reporting interval defined in seconds.
type: integer
minimum: 60
types:
description:
A list of names of subsystems that shall be reported periodically.
@@ -32,6 +33,7 @@ properties:
description:
The reporting interval defined in seconds.
type: integer
minimum: 60
dhcp-snooping:
description:
DHCP snooping allows us to intercept DHCP packages on interface that are

View File

@@ -3,6 +3,10 @@ $schema: http://json-schema.org/draft-07/schema#
description: OpenWrt uCentral schema
type: object
properties:
uuid:
description:
The uniquie ID of the configuration. This is the unix timestamp of when the config was created.
type: integer
unit:
$ref: "https://ucentral.io/schema/v1/unit/"
globals:

View File

@@ -224,6 +224,9 @@ function instantiateInterfaceVlan(value) {
assert(value["proto"] in [ "802.1ad", "802.1q" ], "Property interface.vlan.proto must be one of [ \"802.1ad\", \"802.1q\" ]");
obj.proto = value["proto"];
}
else {
obj.proto = "802.1q";
}
return obj;
}
@@ -954,9 +957,12 @@ function instantiateInterfaceSsid(value) {
if (exists(value, "bss-mode")) {
assert(type(value["bss-mode"]) == "string", "Property interface.ssid.bss-mode must be of type string");
assert(value["bss-mode"] in [ "ap", "sta", "mesh" ], "Property interface.ssid.bss-mode must be one of [ \"ap\", \"sta\", \"mesh\" ]");
assert(value["bss-mode"] in [ "ap", "sta", "mesh", "wds" ], "Property interface.ssid.bss-mode must be one of [ \"ap\", \"sta\", \"mesh\", \"wds\" ]");
obj.bss_mode = value["bss-mode"];
}
else {
obj.bss_mode = "ap";
}
if (exists(value, "bssid")) {
assert(type(value["bssid"]) == "string", "Property interface.ssid.bssid must be of type string");
@@ -1376,6 +1382,7 @@ function instantiateMetrics(value) {
if (exists(value, "interval")) {
assert(type(value["interval"]) == "int", "Property metrics.statistics.interval must be of type integer");
assert(value["interval"] >= 60, "Property metrics.statistics.interval must be >= 60");
obj.interval = value["interval"];
}
@@ -1407,6 +1414,7 @@ function instantiateMetrics(value) {
if (exists(value, "interval")) {
assert(type(value["interval"]) == "int", "Property metrics.health.interval must be of type integer");
assert(value["interval"] >= 60, "Property metrics.health.interval must be >= 60");
obj.interval = value["interval"];
}
@@ -1468,6 +1476,11 @@ function newUCentralState(value) {
let obj = {};
if (exists(value, "uuid")) {
assert(type(value["uuid"]) == "int", "Property UCentralState.uuid must be of type integer");
obj.uuid = value["uuid"];
}
if (exists(value, "unit")) {
obj.unit = instantiateUnit(value["unit"]);
}

41
system/capabilities.uc Normal file → Executable file
View File

@@ -1,15 +1,30 @@
#!/usr/bin/ucode
{%
capa = {};
ctx = ubus.connect();
capa.compatible = replace(board.model.id, ',', '_');
capa.model = board.model.name;
capa.network = board.network;
if (board["bridge"])
capa["bridge-vlan"] = true;
if (board["switch"])
capa["switch"] = board["switch"];
wifi = ctx.call("wifi", "phy");
if (length(wifi))
capa.wifi = wifi;
print(capa);
push(REQUIRE_SEARCH_PATH,
"/usr/lib/ucode/*.so",
"/usr/share/ucentral/*.uc");
let ubus = require("ubus");
let fs = require("fs");
let boardfile = fs.open("/etc/board.json", "r");
let board = json(boardfile.read("all"));
boardfile.close();
capa = {};
ctx = ubus.connect();
capa.compatible = replace(board.model.id, ',', '_');
capa.model = board.model.name;
capa.network = board.network;
if (board["bridge"])
capa["bridge-vlan"] = true;
if (board["switch"])
capa["switch"] = board["switch"];
wifi = ctx.call("wifi", "phy");
if (length(wifi))
capa.wifi = wifi;
capafile = fs.open("/etc/ucentral/capabilities.json", "w");
capafile.write(capa);
capafile.close();
%}

13
system/crashlog.uc Executable file
View File

@@ -0,0 +1,13 @@
{%
if (!fs.stat("/sys/fs/pstore/dmesg-ramoops-0"))
return 0;
let fd = fs.open("/sys/fs/pstore/dmesg-ramoops-0", "r");
let line, lines = [];
while (line = fd.read("line"))
push(lines, trim(line));
fd.close();
let fd = fs.open("/tmp/crashlog", "w");
fd.write({crashlog: lines});
fd.close();
print(lines);
%}

7
system/health.uc Normal file → Executable file
View File

@@ -1,4 +1,9 @@
#!/usr/bin/ucode
{%
let fs = require("fs");
let uci = require("uci");
let ubus = require("ubus");
state = {
unit: {},
interfaces: {}
@@ -103,7 +108,7 @@ catch(e) {
let errors = length(state.interfaces);
if (!errors)
delete(state.interfaces);
delete(state, "interfaces");
let sanity = 100 - (errors * 100 / count);

0
system/probe_services.uc Normal file → Executable file
View File

7
system/state.uc Normal file → Executable file
View File

@@ -1,4 +1,11 @@
#!/usr/bin/ucode
{%
let fs = require("fs");
let uci = require("uci");
let ubus = require("ubus");
let cfgfile = fs.open("/etc/ucentral/ucentral.active", "r");
let cfg = json(cfgfile.read("all"));
/* set up basic functionality */
if (!cursor)
cursor = uci.cursor();

View File

@@ -3,6 +3,9 @@
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"uuid": {
"type": "integer"
},
"unit": {
"$ref": "#/$defs/unit"
},
@@ -231,7 +234,8 @@
"enum": [
"802.1ad",
"802.1q"
]
],
"default": "802.1q"
}
}
},
@@ -858,8 +862,10 @@
"enum": [
"ap",
"sta",
"mesh"
]
"mesh",
"wds"
],
"default": "ap"
},
"bssid": {
"type": "string",
@@ -1174,7 +1180,8 @@
"type": "object",
"properties": {
"interval": {
"type": "integer"
"type": "integer",
"minimum": 60
},
"types": {
"type": "array",
@@ -1193,7 +1200,8 @@
"type": "object",
"properties": {
"interval": {
"type": "integer"
"type": "integer",
"minimum": 60
}
}
},