chore: add FreeSWITCH/ESL research (deploy + mock + docs)

This commit is contained in:
杨豪
2026-08-31 15:17:52 +08:00
parent 157160085e
commit e8ae47e3f7
10 changed files with 942 additions and 0 deletions
+18
View File
@@ -0,0 +1,18 @@
#!/bin/bash
# 端到端外呼验证: mock-agent(ESL+SIP UAS+RTP) → FreeSWITCH → mock-trunk → mock-provider(应答) → 双向 RTP 统计
# 用法: ./call.sh [被叫号码] [时长秒] [freeswitch节点 1|2]
set -euo pipefail
cd "$(dirname "$0")/.."
NUMBER="${1:-+15105550123}"
DURATION="${2:-15}"
NODE="${3:-1}"
NET="freeswitch-esl_fsnet"
NAME="mock-agent-$$"
docker run --rm --name "$NAME" --network "$NET" \
-v "$PWD/mock:/app/mock:ro" -w /app \
python:3.12-alpine python mock/mock-agent.py \
--esl-host "freeswitch${NODE}" --esl-port 8021 \
--sip-host "${NAME}:5062" \
--number "$NUMBER" --duration "$DURATION"
+53
View File
@@ -0,0 +1,53 @@
#!/bin/bash
# 运行状态检测: 容器 + ESL 命令 + sofia 网关状态 + 活动呼叫数
# 注: ESL ACL=rfc1918.auto 不含 loopback → fs_cli 必须 -H <eth0 IP> 走非 loopback 源地址;
# 且需显式 -P 8021 -p <密码> (fs_cli 默认 localhost+ClueCon)
set -uo pipefail
PASS="esl_mock_a95a037399153e9092b653fc"
ok=0; fail=0
pass() { echo "PASS $1"; ok=$((ok+1)); }
faill() { echo "FAIL $1"; fail=$((fail+1)); }
running() { [ -n "$(docker ps -q -f name=$1)" ]; }
fsc() { # fsc <容器> <fs_cli 参数...>
local ip
ip=$(docker exec "$1" hostname -i 2>/dev/null | awk '{print $1}')
docker exec "$1" fs_cli -H "$ip" -P 8021 -p "$PASS" "${@:2}" 2>/dev/null
}
echo "== 容器状态"
docker ps --filter "name=fs" --format "{{.Names}}\t{{.Status}}"
echo "== ESL (fs_cli status)"
for c in fs1 fs2; do
if running "$c"; then
out=$(fsc "$c" -x "status" | head -1)
if echo "$out" | grep -q "^UP"; then
pass "$c ESL status: ${out:0:60}"
else
faill "$c ESL status: ${out:-none}"
fi
else
echo "SKIP $c (未运行)"
fi
done
echo "== sofia 网关 (mock-trunk)"
for c in fs1 fs2; do
if running "$c"; then
state=$(fsc "$c" -x "sofia status gateway mock-trunk" | awk -F'[:\t ]+' '/^State/{print $2}')
case "$state" in
REGED|NOREG) pass "$c gateway mock-trunk State=$state" ;;
*) faill "$c gateway mock-trunk State=${state:-none}" ;;
esac
fi
done
echo "== 活动呼叫"
if running fs1; then
n=$(fsc fs1 -x "show calls count" | grep total | awk '{print $1}')
[ -n "$n" ] && pass "fs1 active calls=$n" || faill "fs1 active calls"
fi
echo
echo "result: ok=$ok fail=$fail"
[ $fail -eq 0 ]
+29
View File
@@ -0,0 +1,29 @@
#!/bin/bash
# 生成 etc-fs 运行配置: 镜像 vanilla 配置 + 本仓库覆盖/补丁
# 每次全量重建 (rm -rf → 拷 vanilla → 打补丁), 天然幂等, 不会叠加损坏 XML
# 产出: ../etc-fs/ (整目录挂载到容器 /etc/freeswitch, 见 docker-compose.yml)
set -euo pipefail
cd "$(dirname "$0")/.."
IMG="safarov/freeswitch:latest"
# 1. 从镜像导出干净 vanilla 配置
rm -rf etc-fs && mkdir -p etc-fs
docker run --rm --entrypoint sh -v "$PWD/etc-fs:/out" "$IMG" \
-c "cp -a /usr/share/freeswitch/conf/vanilla/. /out/"
# 2. ESL: 0.0.0.0:8021 + 自定义密码 + 内置 rfc1918.auto ACL
# (vanilla listen-ip=:: 在本容器 getaddrinfo 失败; ACL 详见文件内注释)
cp conf/event_socket.conf.xml etc-fs/autoload_configs/event_socket.conf.xml
# 3. Mock 运营商 trunk 网关 (无注册 + OPTIONS ping 探活)
cp conf/mock-trunk.xml etc-fs/sip_profiles/external/mock-trunk.xml
# 4. 去 stun: 依赖 (容器网内无 NAT; stun-set 在配置解析期做外网 STUN 查询,
# 不通则 Invalid ext-sip-ip → mod_sofia 加载失败)
sed -i '/cmd="stun-set"/d' etc-fs/vars.xml
sed -i '/<param name="ext-sip-ip"/d;/<param name="ext-rtp-ip"/d' etc-fs/sip_profiles/*.xml
# 5. 删除 mod_signalwire (镜像缺 CA 证书, Curl 77 每分钟刷错误日志)
sed -i '/<load module="mod_signalwire"\/>/d' etc-fs/autoload_configs/modules.conf.xml
echo "etc-fs rebuilt (freeswitch.xml: $(ls etc-fs/freeswitch.xml))"