Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,12 @@ o = s:taboption("smart", Flag, "auto_smart_switch", font_red..bold_on..translate
o.description = font_red..bold_on..translate("Auto Switch Url-test and Load-balance Group to Smart Group")..bold_off..font_off
o.default = 0

o = s:taboption("smart", ListValue, "smart_strategy", translate("Strategy Type"))
o.description = translate("Requires Smart core support for sticky-sessions strategy")
o:value("0", translate("Disable"))
o:value("sticky-sessions", translate("Sticky-sessions"))
o.default = "0"

o = s:taboption("smart", Value, "smart_policy_priority", translate("Policy Priority"))
o.default = ""
o.placeholder = "Premium:0.9;SG:1.3"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,12 @@ o:value("consistent-hashing", translate("Consistent-hashing"))
o:value("sticky-sessions", translate("Sticky-sessions"))
o:depends("type", "load-balance")

o = s:option(ListValue, "strategy_smart", translate("Strategy Type"))
o.rmempty = true
o.description = translate("Requires Smart core support for sticky-sessions strategy")
o:value("sticky-sessions", translate("Sticky-sessions"))
o:depends("type", "smart")

o = s:option(ListValue, "uselightgbm", translate("Uselightgbm"))
o.description = translate("Use LightGBM Model For Smart Group Weight Prediction")
o:value("false", translate("Disable"))
Expand Down
5 changes: 4 additions & 1 deletion luci-app-openclash/po/es/openclash.es.po
Original file line number Diff line number Diff line change
Expand Up @@ -1083,6 +1083,9 @@ msgstr "Sesiones persistentes"
msgid "Strategy Type"
msgstr "Tipo de estrategia"

msgid "Requires Smart core support for sticky-sessions strategy"
msgstr "Requiere un núcleo Smart compatible con la estrategia sticky-sessions"

msgid "Choose The Load-Balance's Strategy Type"
msgstr "Selecciona el tipo de estrategia de balanceo"

Expand Down Expand Up @@ -4042,4 +4045,4 @@ msgid "Note: oixCloud need a specific core installed, OpenClash will auto downlo
msgstr "Nota: oixCloud requiere la instalación de un núcleo específico; OpenClash lo descargará automáticamente"

msgid "After the core start, an oixCloud nodes provider will be added, which you can customize as needed"
msgstr "Tras iniciar el núcleo se añadirá un proveedor de nodos oixCloud que puedes personalizar"
msgstr "Tras iniciar el núcleo se añadirá un proveedor de nodos oixCloud que puedes personalizar"
3 changes: 3 additions & 0 deletions luci-app-openclash/po/zh-cn/openclash.zh-cn.po
Original file line number Diff line number Diff line change
Expand Up @@ -1083,6 +1083,9 @@ msgstr "粘性会话(相同的来源地址-目标地址使用同一个代理
msgid "Strategy Type"
msgstr "策略类型"

msgid "Requires Smart core support for sticky-sessions strategy"
msgstr "需要支持 sticky-sessions 策略的 Smart 内核"

msgid "Choose The Load-Balance's Strategy Type"
msgstr "选择负载均衡的策略类型"

Expand Down
2 changes: 1 addition & 1 deletion luci-app-openclash/root/etc/config/openclash
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ config openclash 'config'
option router_self_proxy '1'
option release_branch 'master'
option smart_enable '0'
option smart_strategy '0'
option dashboard_type 'Official'
option yacd_type 'Meta'
option append_default_dns '0'
Expand Down Expand Up @@ -294,4 +295,3 @@ config config_overwrite
option update_hour 'off'
option type 'http'
option order '1'

6 changes: 4 additions & 2 deletions luci-app-openclash/root/etc/init.d/openclash
Original file line number Diff line number Diff line change
Expand Up @@ -3135,6 +3135,7 @@ EOF
SMART_ENABLE_LGBM:int_bool \
SMART_POLICY_PRIORITY:string \
SMART_PREFER_ASN:int_bool \
SMART_STRATEGY:string \
SKIP_PROXY_ADDRESS:int_bool \
SMALL_FLASH_MEMORY:int_bool \
SOCKS_PORT:int \
Expand Down Expand Up @@ -3558,6 +3559,7 @@ get_config()
smart_policy_priority=$(uci_get_config "smart_policy_priority" || echo 0)
smart_enable_lgbm=$(uci_get_config "smart_enable_lgbm" || echo 0)
smart_prefer_asn=$(uci_get_config "smart_prefer_asn" || echo 0)
smart_strategy=$(uci_get_config "smart_strategy" || echo 0)

[ -z "$dns_port" ] && dns_port=7874 && uci -q set openclash.config.dns_port=7874
uci -q commit openclash
Expand Down Expand Up @@ -3604,7 +3606,7 @@ start_service()
/usr/share/openclash/yml_rules_change.sh \
"$enable_custom_clash_rules" "$TMP_CONFIG_FILE"\
"$enable_rule_proxy" "$router_self_proxy" "$lan_ip" "$enable_redirect_dns" "$en_mode"\
"$auto_smart_switch" "$smart_collect" "$smart_collect_rate" "$smart_policy_priority" "$smart_enable_lgbm" "$smart_prefer_asn"
"$auto_smart_switch" "$smart_collect" "$smart_collect_rate" "$smart_policy_priority" "$smart_enable_lgbm" "$smart_prefer_asn" "$smart_strategy"

#Custom overwrite
if [ -f "/tmp/yaml_overwrite.sh" ]; then
Expand Down Expand Up @@ -3822,4 +3824,4 @@ boot()
sleep "$delay_start"
fi
restart
}
}
7 changes: 5 additions & 2 deletions luci-app-openclash/root/usr/share/openclash/yml_groups_set.sh
Original file line number Diff line number Diff line change
Expand Up @@ -199,13 +199,14 @@ set_provider_groups()
yml_groups_set()
{
local section="$1"
local enabled config type name disable_udp strategy old_name test_url test_interval tolerance policy_filter uselightgbm collectdata policy_priority other_parameters icon
local enabled config type name disable_udp strategy strategy_smart old_name test_url test_interval tolerance policy_filter uselightgbm collectdata policy_priority other_parameters icon
config_get_bool "enabled" "$section" "enabled" "1"
config_get "config" "$section" "config" ""
config_get "type" "$section" "type" ""
config_get "name" "$section" "name" ""
config_get "disable_udp" "$section" "disable_udp" ""
config_get "strategy" "$section" "strategy" ""
config_get "strategy_smart" "$section" "strategy_smart" ""
config_get "old_name" "$section" "old_name" ""
config_get "test_url" "$section" "test_url" ""
config_get "test_interval" "$section" "test_interval" ""
Expand Down Expand Up @@ -298,6 +299,9 @@ yml_groups_set()
}

if [ "$type" = "smart" ]; then
[ "$strategy_smart" = "sticky-sessions" ] && {
echo " strategy: $strategy_smart" >>$GROUP_FILE
}
[ -n "$uselightgbm" ] && {
echo " uselightgbm: $uselightgbm" >>$GROUP_FILE
}
Expand Down Expand Up @@ -329,4 +333,3 @@ sed -i "s/#delete_//g" "$CONFIG_FILE" 2>/dev/null

/usr/share/openclash/yml_proxys_set.sh "$CONFIG_FILE" >/dev/null 2>&1
del_lock

Original file line number Diff line number Diff line change
Expand Up @@ -387,11 +387,12 @@ yml_other_set()

# smart auto switch
begin
if ('${8}' == '1' or '${9}' == '1' or '${11}' != '0' or '${12}' != '0' or '${12}' == '1' or '${13}' == '1') and Value.key?('proxy-groups') and Value['proxy-groups'].is_a?(Array) then
if ('${8}' == '1' or '${9}' == '1' or '${11}' != '0' or '${12}' != '0' or '${12}' == '1' or '${13}' == '1' or '${14}' == 'sticky-sessions') and Value.key?('proxy-groups') and Value['proxy-groups'].is_a?(Array) then
Value['proxy-groups'].each{|group|
threads << Thread.new {
if '${8}' == '1' and ['url-test', 'load-balance'].include?(group['type']) then
group['type'] = 'smart';
group['strategy'] = '${14}' if '${14}' == 'sticky-sessions';
group['uselightgbm'] = true if '${12}' == '1';
group['collectdata'] = true if '${9}' == '1';
group['sample-rate'] = '${10}'.to_f if '${9}' == '1';
Expand Down Expand Up @@ -428,4 +429,4 @@ yml_other_set()
end" 2>/dev/null >> $LOG_FILE
}

yml_other_set "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9" "${10}" "${11}" "${12}" "${13}"
yml_other_set "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9" "${10}" "${11}" "${12}" "${13}" "${14}"
84 changes: 84 additions & 0 deletions tests/openclash_smart_strategy_test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#!/usr/bin/env bash
set -euo pipefail

ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"

groups_set="$ROOT_DIR/luci-app-openclash/root/usr/share/openclash/yml_groups_set.sh"
rules_change="$ROOT_DIR/luci-app-openclash/root/usr/share/openclash/yml_rules_change.sh"
init_script="$ROOT_DIR/luci-app-openclash/root/etc/init.d/openclash"
groups_config="$ROOT_DIR/luci-app-openclash/luasrc/model/cbi/openclash/groups-config.lua"
overwrite_config="$ROOT_DIR/luci-app-openclash/luasrc/model/cbi/openclash/config-overwrite.lua"

assert_contains() {
local file="$1"
local pattern="$2"
local message="$3"

if ! grep -Fq "$pattern" "$file"; then
echo "not ok - $message" >&2
echo "missing pattern: $pattern" >&2
echo "file: $file" >&2
exit 1
fi
}

assert_contains "$groups_set" 'config_get "strategy_smart" "$section" "strategy_smart" ""' "Smart strategy is read from group UCI"
assert_contains "$groups_set" '[ "$strategy_smart" = "sticky-sessions" ] && {' "Smart strategy output is gated to sticky-sessions"
assert_contains "$groups_set" 'echo " strategy: $strategy_smart" >>$GROUP_FILE' "Smart strategy is written to generated YAML"

assert_contains "$groups_config" 's:option(ListValue, "strategy_smart", translate("Strategy Type"))' "LuCI exposes Smart strategy_smart"
assert_contains "$groups_config" 'Requires Smart core support for sticky-sessions strategy' "Smart group UI warns about core support"
assert_contains "$groups_config" 'o:depends("type", "smart")' "Smart strategy UI only depends on Smart groups"
assert_contains "$overwrite_config" 'Requires Smart core support for sticky-sessions strategy' "Global Smart strategy UI warns about core support"

assert_contains "$init_script" 'smart_strategy=$(uci_get_config "smart_strategy" || echo 0)' "Global Smart strategy is read from UCI"
assert_contains "$init_script" '"$auto_smart_switch" "$smart_collect" "$smart_collect_rate" "$smart_policy_priority" "$smart_enable_lgbm" "$smart_prefer_asn" "$smart_strategy"' "Global Smart strategy is passed to yml_rules_change"

assert_contains "$rules_change" "'\${14}' == 'sticky-sessions'" "Auto Smart strategy parameter is checked"
assert_contains "$rules_change" "group['strategy'] = '\${14}'" "Auto Smart strategy is written to Smart groups"
assert_contains "$rules_change" 'yml_other_set "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9" "${10}" "${11}" "${12}" "${13}" "${14}"' "Auto Smart strategy argument is forwarded"

ruby -ryaml <<'RUBY'
def convert_groups(groups, auto_smart_switch, smart_strategy)
groups = Marshal.load(Marshal.dump(groups))
if auto_smart_switch == "1" || smart_strategy == "sticky-sessions"
groups.each do |group|
if auto_smart_switch == "1" && ["url-test", "load-balance"].include?(group["type"])
group["type"] = "smart"
group["strategy"] = smart_strategy if smart_strategy == "sticky-sessions"
end
end
end
groups
end

source_groups = [
{"name" => "Auto", "type" => "url-test"},
{"name" => "Balance", "type" => "load-balance", "strategy" => "consistent-hashing"},
{"name" => "Existing Smart", "type" => "smart"}
]

default_groups = convert_groups(source_groups, "1", "0")
raise "default smart_strategy should not write strategy" if default_groups.any? { |group| group["strategy"] == "sticky-sessions" }

sticky_groups = convert_groups(source_groups, "1", "sticky-sessions")
converted = sticky_groups.select { |group| ["Auto", "Balance"].include?(group["name"]) }
raise "converted groups should all be smart" unless converted.all? { |group| group["type"] == "smart" }
raise "converted groups should receive sticky-sessions" unless converted.all? { |group| group["strategy"] == "sticky-sessions" }
existing = sticky_groups.find { |group| group["name"] == "Existing Smart" }
raise "existing smart group should use per-group strategy_smart, not auto switch strategy" if existing.key?("strategy")

imported_uci = {}
yaml_group = {"name" => "Manual Smart", "type" => "smart", "strategy" => "sticky-sessions"}
if yaml_group.key?("strategy") && yaml_group["type"] == "smart"
imported_uci["strategy_smart"] = yaml_group["strategy"].to_s
end
raise "Smart strategy should import as strategy_smart" unless imported_uci["strategy_smart"] == "sticky-sessions"

exported_group = {"name" => "Manual Smart", "type" => "smart"}
strategy_smart = imported_uci["strategy_smart"]
exported_group["strategy"] = strategy_smart if strategy_smart == "sticky-sessions"
raise "Smart strategy should export back to YAML" unless exported_group["strategy"] == "sticky-sessions"
RUBY

echo "ok - Smart sticky strategy wiring"