Refactor to use LuaJIT and improve performance

Integrate LuaJIT as an optional runtime for better performance, with a fallback to standard Lua 5.4. Update Dockerfile to install LuaJIT and create a wrapper script for execution. Enhance network module with socket fallback support and update README to reflect these changes and configuration options.
This commit is contained in:
Bastian (BaM)
2025-09-15 08:44:19 +02:00
parent fff635c2d7
commit 8cb6d55782
9 changed files with 219 additions and 48 deletions

View File

@@ -5,13 +5,10 @@
-- - Optional Wake-on-LAN is native (no external tools).
-- - Optional port-wait is provided but commented out (mirrors original bash idea).
local socket = require("socket")
-- Import modules
local config = require("config")
local utils = require("utils")
local network = require("network")
local ssh = require("ssh")
local ollama_manager = require("ollama_manager")
local session_check = require("session_check")

View File

@@ -25,7 +25,7 @@ config.SSH_IDENTITY_FILE = getenv("SSH_IDENTITY_FILE", "/root/.ssh/id_rsa")
config.ERROR_PATTERN = getenv(
"ERROR_PATTERN",
("[ERROR] Document analysis failed: connect EHOSTUNREACH %s:%d"):format(
config.OLLAMA_HOST,
config.OLLAMA_HOST,
config.OLLAMA_PORT
)
)
@@ -39,4 +39,10 @@ config.WOL_PORT = tonumber(getenv("WOL_PORT", "9"))
-- Optional: wait for service to come up (kept commented to stay minimal)
-- config.UP_WAIT_TIMEOUT = tonumber(getenv("UP_WAIT_TIMEOUT", "90"))
-- Debug configuration
function config.is_debug()
local debug_env = os.getenv("DEBUG")
return debug_env and (string.lower(debug_env) == "true" or debug_env == "1")
end
return config

View File

@@ -1,11 +1,43 @@
-- Network utilities module for auto-boot-ollama-host
-- Provides port checking and Wake-on-LAN functionality
-- Provides port checking, Wake-on-LAN functionality, and socket fallback support
local socket = require("socket")
local utils = require("utils")
-- Try to load socket module, fallback to basic implementation for LuaJIT
local socket
local socket_available = true
local ok, err = pcall(function() socket = require("socket") end)
if not ok then
print("Warning: socket module not available, using fallback implementation")
print("Error:", err)
socket_available = false
-- Create a minimal socket fallback for LuaJIT
socket = {
gettime = function() return os.time() end,
sleep = function(sec) os.execute("sleep " .. tostring(sec)) end,
tcp = function() return nil end, -- Will cause network functions to fail gracefully
udp = function() return nil end,
udp4 = function() return nil end
}
end
local network = {}
-- Check if socket module is fully functional
function network.is_socket_available()
return socket_available and socket and socket.tcp and socket.udp
end
-- Check if socket module is available for sleep operations
function network.is_sleep_available()
return socket_available and socket and socket.sleep
end
-- Get the socket module (either real or fallback)
function network.get_socket()
return socket
end
-- Check if a TCP port is accepting connections within a timeout (seconds)
function network.port_is_up(host, port, timeout_sec)
host = tostring(host or "127.0.0.1")
@@ -13,6 +45,14 @@ function network.port_is_up(host, port, timeout_sec)
local timeout = tonumber(timeout_sec or 1) or 1
if port <= 0 then return false end
-- Fallback to basic check if socket is not available
if not network.is_socket_available() then
utils.log("Socket module not available, using basic port check")
local cmd = string.format("nc -z -w1 %s %d 2>/dev/null", host, port)
local result = os.execute(cmd)
return result == 0
end
local deadline = socket.gettime() + timeout
while socket.gettime() < deadline do
local tcp = socket.tcp()
@@ -28,32 +68,40 @@ end
-- Convert MAC address string to bytes
-- "AA:BB:CC:DD:EE:FF" -> 6 bytes
local function mac_to_bytes(mac)
local bytes = {}
for byte in mac:gmatch("(%x%x)") do
table.insert(bytes, tonumber(byte, 16))
end
if #bytes ~= 6 then return nil end
return string.char(table.unpack(bytes))
end
-- local function mac_to_bytes(mac)
-- local bytes = {}
-- for byte in mac:gmatch("(%x%x)") do
-- table.insert(bytes, tonumber(byte, 16))
-- end
-- if #bytes ~= 6 then return nil end
-- return string.char(table.unpack(bytes))
-- end
-- Send Wake-on-LAN magic packet
function network.send_wol(mac_str, bcast_ip, port)
-- Build magic packet
local bytes = {}
for byte in mac_str:gmatch("(%x%x)") do
table.insert(bytes, tonumber(byte, 16))
for byte in mac_str:gmatch("(%x%x)") do
table.insert(bytes, tonumber(byte, 16))
end
if #bytes ~= 6 then return false, "invalid MAC" end
local mac = string.char(table.unpack(bytes))
local packet = string.rep(string.char(0xFF), 6) .. mac:rep(16)
-- Fallback to external tool if socket is not available
if not network.is_socket_available() then
utils.log("Socket module not available, using external wakeonlan tool")
local cmd = string.format("wakeonlan -i %s -p %d %s", bcast_ip, port, mac_str)
local result = os.execute(cmd)
return result == 0, result ~= 0 and "wakeonlan command failed" or nil
end
-- Create IPv4 UDP socket (udp4 if available), bind to IPv4 wildcard to lock AF_INET
local udp = assert((socket.udp4 or socket.udp)())
udp:settimeout(2)
assert(udp:setsockname("0.0.0.0", 0)) -- force IPv4 family
assert(udp:setoption("broadcast", true)) -- allow broadcast
assert(udp:setsockname("0.0.0.0", 0)) -- force IPv4 family
assert(udp:setoption("broadcast", true)) -- allow broadcast
local ok, err = udp:sendto(packet, bcast_ip, port)
udp:close()

View File

@@ -1,7 +1,6 @@
-- Ollama service management module for auto-boot-ollama-host
-- Handles starting, stopping, and monitoring Ollama service
local socket = require("socket")
local utils = require("utils")
local network = require("network")
local ssh = require("ssh")
@@ -11,7 +10,7 @@ local ollama_manager = {}
-- Start Ollama service via SSH
function ollama_manager.start_service(config)
utils.log("SSH is reachable. Starting Ollama service...")
socket.sleep(10)
utils.sleep(10)
-- Start ollama service using nssm
ssh.execute("nssm start ollama", config.SSH_USER, config.OLLAMA_HOST, config.SSH_PORT, config.SSH_IDENTITY_FILE)
@@ -23,7 +22,7 @@ function ollama_manager.start_service(config)
-- Wait for service to become available
if network.port_is_up(config.OLLAMA_HOST, config.OLLAMA_PORT, 90) then
utils.log("Ollama service is reachable again.")
socket.sleep(30)
utils.sleep(30)
return true
else
utils.log("Timeout waiting for Ollama service to come up after SSH command.")
@@ -44,7 +43,7 @@ function ollama_manager.stop_service_and_shutdown(config)
-- Shutdown the host
ssh.execute("shutdown.exe /s /t 0", config.SSH_USER, config.OLLAMA_HOST, config.SSH_PORT, config.SSH_IDENTITY_FILE)
socket.sleep(5)
utils.sleep(5)
end
return ollama_manager

View File

@@ -2,13 +2,23 @@
-- Provides SSH command execution functionality
local utils = require("utils")
local config = require("config")
local ssh_module = {}
-- Check if DEBUG environment variable is set to "true"
local function is_debug()
local debug_env = os.getenv("DEBUG")
return debug_env and (string.lower(debug_env) == "true" or debug_env == "1")
-- Quote a string for safe single-quoted POSIX shell context
local function sq(s)
-- Replace ' with: '\'' (close, escape quote, reopen)
return "'" .. tostring(s):gsub("'", "'\\''") .. "'"
end
-- Helper function to log SSH commands with proper formatting
local function log_ssh_command(prefix, command, full_command)
if config.is_debug() then
utils.log(prefix .. full_command)
else
utils.log(prefix .. sq(command))
end
end
-- Execute a remote command over SSH
@@ -20,11 +30,6 @@ function ssh_module.execute(command, user, host, port, identity_file)
port = tonumber(port or 22) or 22
identity_file = tostring(identity_file or "")
-- Quote a string for safe single-quoted POSIX shell context
local function sq(s)
-- Replace ' with: '\'' (close, escape quote, reopen)
return "'" .. tostring(s):gsub("'", "'\\''") .. "'"
end
-- Build base ssh command (run locally)
-- -oBatchMode to avoid interactive prompts
@@ -51,7 +56,7 @@ function ssh_module.execute(command, user, host, port, identity_file)
-- Pass remote command as provided; caller is responsible for proper quoting
table.insert(pieces, "--")
-- Quote the remote command to prevent shell interpretation of && and ||
table.insert(pieces, "'" .. command:gsub("'", "'\\''") .. "'")
table.insert(pieces, sq(command))
-- Join with spaces for os.execute
local function join(args)
@@ -61,12 +66,8 @@ function ssh_module.execute(command, user, host, port, identity_file)
local full = join(pieces)
-- Log based on DEBUG environment variable
if is_debug() then
utils.log("SSH exec: " .. full)
else
utils.log("SSH exec: " .. "'" .. command:gsub("'", "'\\''") .. "'")
end
-- Log SSH command
log_ssh_command("SSH exec: ", command, full)
local ok, reason, code = os.execute(full)
if ok == true or ok == 0 then
@@ -111,7 +112,7 @@ function ssh_module.execute_with_output(command, user, host, port, identity_file
-- Pass remote command as provided
table.insert(pieces, "--")
-- Quote the remote command to prevent shell interpretation of && and ||
table.insert(pieces, "'" .. command:gsub("'", "'\\''") .. "'")
table.insert(pieces, sq(command))
-- Join with spaces for io.popen
local function join(args)
@@ -120,12 +121,8 @@ function ssh_module.execute_with_output(command, user, host, port, identity_file
local full = join(pieces)
-- Log based on DEBUG environment variable
if is_debug() then
utils.log("SSH exec (with output): " .. full)
else
utils.log("SSH exec (with output): " .. "'" .. command:gsub("'", "'\\''") .. "'")
end
-- Log SSH command
log_ssh_command("SSH exec (with output): ", command, full)
-- Use io.popen to capture output
local fh = io.popen(full, "r")

View File

@@ -15,4 +15,15 @@ function utils.getenv(name, def)
return (v ~= nil and v ~= "") and v or def
end
-- Sleep function with fallback support for LuaJIT compatibility
function utils.sleep(seconds)
-- Try to use socket.sleep if available, fallback to os.execute
local ok, socket = pcall(require, "socket")
if ok and socket and socket.sleep then
socket.sleep(seconds)
else
os.execute("sleep " .. tostring(seconds))
end
end
return utils