From f5ad76acb315d9b53c03f5202bd35007ab5986ca Mon Sep 17 00:00:00 2001 From: Gregor Hartmann Date: Sun, 10 Jul 2022 17:01:21 +0200 Subject: [PATCH] Fix some bugs and problems in gossip (#3527) * Allow configuration of debugOutput to be performed * don't send to own IP or update own data * Use same socket to send and receive. Avoid problems in many opened and closed sockets to send * Add callback for REMOVEed hosts * Send broascast messages if seedList is empty * Adapt yeelink to new luacheck rules * Fix building of luac.cross for win to win2019 and VS 2019 --- .github/workflows/build.yml | 2 +- .gitignore | 2 ++ docs/lua-modules/gossip.md | 2 +- lua_modules/gossip/gossip.lua | 53 ++++++++++++++++++----------- lua_modules/gossip/gossip_tests.lua | 20 +++++++---- lua_modules/yeelink/yeelink_lib.lua | 12 +++---- 6 files changed, 57 insertions(+), 34 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cab26761..7ff0eaf5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -58,7 +58,7 @@ jobs: build_luac_cross_win: - runs-on: windows-latest + runs-on: windows-2019 steps: - uses: actions/checkout@v2 diff --git a/.gitignore b/.gitignore index 6ff93a2f..80a6cb96 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,8 @@ uz_zip # ignore VS Code files .vscode/** +# ignore VS files +.vs/** # ignore IDEA files .idea diff --git a/docs/lua-modules/gossip.md b/docs/lua-modules/gossip.md index e49e9ea1..da8cf133 100644 --- a/docs/lua-modules/gossip.md +++ b/docs/lua-modules/gossip.md @@ -68,7 +68,7 @@ gossip.setConfig(config) Sets the configuration for gossip. The available options are: -`seedList` : the list of seeds gossip will start with; this will be updated as new nodes are discovered. Note that it's enough for all nodes to start with the same IP in the seedList, as once they have one seed in common, the data will propagate +`seedList` : the list of seeds gossip will start with; this will be updated as new nodes are discovered. Note that it's enough for all nodes to start with the same IP in the seedList, as once they have one seed in common, the data will propagate. If the seedList is empty a broadcast is sent, so this can be used for automatic discovery of nodes. `roundInterval`: interval in milliseconds at which gossip will pick a random node from the seed list and send a `SYN` request diff --git a/lua_modules/gossip/gossip.lua b/lua_modules/gossip/gossip.lua index f4709d9a..a236fba5 100644 --- a/lua_modules/gossip/gossip.lua +++ b/lua_modules/gossip/gossip.lua @@ -15,11 +15,7 @@ end utils.debug = function(message) if gossip.config.debug then - if gossip.config.debugOutput then - gossip.config.debugOutput(message); - else - print(message); - end + gossip.config.debugOutput(message); end end @@ -105,15 +101,23 @@ state.start = function() return; end + -- sending to own IP makes no sense + for index, value in ipairs(gossip.config.seedList) do + if value == gossip.ip then + table.remove(gossip.config.seedList, index) + utils.debug('removing own ip from seed list') + end + end + gossip.networkState[gossip.ip] = {}; local localState = gossip.networkState[gossip.ip]; localState.revision = state.setRev(); localState.heartbeat = tmr.time(); localState.state = constants.nodeState.UP; - gossip.inboundSocket = net.createUDPSocket(); - gossip.inboundSocket:listen(gossip.config.comPort); - gossip.inboundSocket:on('receive', network.receiveData); + gossip.socket = net.createUDPSocket(); + gossip.socket:listen(gossip.config.comPort); + gossip.socket:on('receive', network.receiveData); gossip.started = true; @@ -128,16 +132,25 @@ end state.tickNodeState = function(ip) if gossip.networkState[ip] then local nodeState = gossip.networkState[ip].state; + local oldNodeState = nodeState; if nodeState < constants.nodeState.REMOVE then nodeState = nodeState + constants.nodeState.TICK; gossip.networkState[ip].state = nodeState; end + if oldNodeState == constants.nodeState.DOWN then + if gossip.updateCallback then gossip.updateCallback(gossip.networkState[ip]); end + end end end -- Network +network.broadcastIp = "255.255.255.255" network.pushGossip = function(data, ip) + if not gossip.started then + utils.debug('Gossip not started.'); + return; + end gossip.networkState[gossip.ip].data = data; network.sendSyn(nil, ip); end @@ -145,10 +158,12 @@ end network.updateNetworkState = function(updateData) if gossip.updateCallback then gossip.updateCallback(updateData); end for ip, data in pairs(updateData) do - if not utils.contains(gossip.config.seedList, ip) then + if not utils.contains(gossip.config.seedList, ip) and ip ~= network.broadcastIp and ip ~= gossip.ip then table.insert(gossip.config.seedList, ip); end - gossip.networkState[ip] = data; + if ip ~= gossip.ip then + gossip.networkState[ip] = data; + end end end @@ -170,17 +185,16 @@ network.pickRandomNode = function() return gossip.config.seedList[randomListPick]; end utils.debug( - 'Seedlist is empty. Please provide one or wait for node to be contacted.'); - return nil; + 'Seedlist is empty. Using broadcast IP '..network.broadcastIp..' to discover.'); + return network.broadcastIp; end network.sendData = function(ip, data, sendType) - local outboundSocket = net.createUDPSocket(); - data.type = sendType; - local dataToSend = sjson.encode(data); - data.type = nil; - outboundSocket:send(gossip.config.comPort, ip, dataToSend); - outboundSocket:close(); + data.type = sendType + local dataToSend = sjson.encode(data) + data.type = nil + gossip.socket:send(gossip.config.comPort, ip, dataToSend) + utils.debug("Sent "..#dataToSend.." bytes") end network.receiveSyn = function(ip, synData) @@ -235,7 +249,8 @@ constants.defaultConfig = { seedList = {}, roundInterval = 15000, comPort = 5000, - debug = false + debug = false, + debugOutput = print }; constants.comparisonFields = {'revision', 'heartbeat', 'state'}; diff --git a/lua_modules/gossip/gossip_tests.lua b/lua_modules/gossip/gossip_tests.lua index dec7a719..0ecc55ec 100644 --- a/lua_modules/gossip/gossip_tests.lua +++ b/lua_modules/gossip/gossip_tests.lua @@ -24,6 +24,7 @@ file.putcontents = dummy local Ip_1 = '192.168.0.1'; local Ip_2 = '192.168.0.2'; +local Ip_own = '192.168.0.3'; -- test runner @@ -186,20 +187,19 @@ function Test.utils_getMinus() state = constants.nodeState.SUSPECT; }; - --local diff1 = utils.getMinus(data1, data2); + local diff1 = utils.getMinus(data1, data2); local diff2 = utils.getMinus(data2, data1); - --assert(diff1[Ip_1] ~= nil and diff1[Ip_2] == nil); + assert(diff1[Ip_1] ~= nil and diff1[Ip_2] == nil); assert(diff2[Ip_1] == nil and diff2[Ip_2] ~= nil); - end -- state function Test.state_setRev() - gossip.ip = Ip_1; - gossip.networkState[Ip_1] = {}; - gossip.networkState[Ip_1].revision = -1; + gossip.ip = Ip_own; + gossip.networkState[Ip_own] = {}; + gossip.networkState[Ip_own].revision = -1; assert(state.setRev() == 0, 'Revision not initialized to 0.'); end @@ -223,6 +223,7 @@ end -- network function Test.network_updateNetworkState_no_callback() + gossip.ip = Ip_own; local updateData = {} updateData[Ip_1] = { revision = 1, @@ -232,7 +233,12 @@ function Test.network_updateNetworkState_no_callback() updateData[Ip_2] = { revision = 1, heartbeat = 700, - state = constants.nodeState.UP + state = constants.nodeState.DOWN + }; + updateData[Ip_own] = { + revision = 1, + heartbeat = 800, + state = constants.nodeState.DOWN }; network.updateNetworkState(updateData); -- send duplicate data diff --git a/lua_modules/yeelink/yeelink_lib.lua b/lua_modules/yeelink/yeelink_lib.lua index 70c6c4a4..9e6a1cfc 100644 --- a/lua_modules/yeelink/yeelink_lib.lua +++ b/lua_modules/yeelink/yeelink_lib.lua @@ -54,10 +54,10 @@ end) -- apikey must be -> string <- -- e.g. xxx.init(00000,00000,"123j12b3jkb12k4b23bv54i2b5b3o4") --======================================== -function M.init(_device, _sensor, _apikey) - device = tostring(_device) - sensor = tostring(_sensor) - apikey = _apikey +function M.init(device_, sensor_, apikey_) + device = tostring(device_) + sensor = tostring(sensor_) + apikey = apikey_ if dns == "0.0.0.0" then tmr.create():alarm(5000,tmr.ALARM_AUTO,function () if dns == "0.0.0.0" then @@ -90,9 +90,9 @@ end -- --e.g. xxx.update(233.333) --============================================================ -function M.update(_datapoint) +function M.update(datapoint_) - datapoint = tostring(_datapoint) + datapoint = tostring(datapoint_) sk:on("connection", function()