validate-1m-linux.sh 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. #!/bin/bash
  2. # This test script joins Earth and pokes some stuff
  3. TEST_NETWORK=8056c2e21c000001
  4. RUN_LENGTH=60
  5. TEST_FINISHED=false
  6. ZTO_VER=$(git describe --tags $(git rev-list --tags --max-count=1))
  7. ZTO_COMMIT=$(git rev-parse HEAD)
  8. ZTO_COMMIT_SHORT=$(git rev-parse --short HEAD)
  9. TEST_DIR_PREFIX="$ZTO_VER-$ZTO_COMMIT_SHORT-test-results"
  10. echo "Performing test on: $ZTO_VER-$ZTO_COMMIT_SHORT"
  11. TEST_FILEPATH_PREFIX="$TEST_DIR_PREFIX/$ZTO_COMMIT_SHORT"
  12. mkdir $TEST_DIR_PREFIX
  13. ################################################################################
  14. # Multi-node connectivity and performance test #
  15. ################################################################################
  16. main() {
  17. echo -e "\nRunning test for $RUN_LENGTH seconds"
  18. NS1="ip netns exec ns1"
  19. NS2="ip netns exec ns2"
  20. ZT1="$NS1 ./zerotier-cli -p9996 -D$(pwd)/node1"
  21. # Specify custom port on one node to ensure that feature works
  22. ZT2="$NS2 ./zerotier-cli -p9997 -D$(pwd)/node2"
  23. echo -e "\nSetting up network namespaces..."
  24. echo "Setting up ns1"
  25. ip netns add ns1
  26. $NS1 ip link set dev lo up
  27. ip link add veth0 type veth peer name veth1
  28. ip link set veth1 netns ns1
  29. ip addr add 192.168.0.1/24 dev veth0
  30. ip link set dev veth0 up
  31. $NS1 ip addr add 192.168.0.2/24 dev veth1
  32. $NS1 ip link set dev veth1 up
  33. # Add default route
  34. $NS1 ip route add default via 192.168.0.1
  35. iptables -t nat -A POSTROUTING -s 192.168.0.0/255.255.255.0 \
  36. -o eth0 -j MASQUERADE
  37. iptables -A FORWARD -i eth0 -o veth0 -j ACCEPT
  38. iptables -A FORWARD -o eth0 -i veth0 -j ACCEPT
  39. echo "Setting up ns2"
  40. ip netns add ns2
  41. $NS2 ip link set dev lo up
  42. ip link add veth2 type veth peer name veth3
  43. ip link set veth3 netns ns2
  44. ip addr add 192.168.1.1/24 dev veth2
  45. ip link set dev veth2 up
  46. $NS2 ip addr add 192.168.1.2/24 dev veth3
  47. $NS2 ip link set dev veth3 up
  48. $NS2 ip route add default via 192.168.1.1
  49. iptables -t nat -A POSTROUTING -s 192.168.1.0/255.255.255.0 \
  50. -o eth0 -j MASQUERADE
  51. iptables -A FORWARD -i eth0 -o veth2 -j ACCEPT
  52. iptables -A FORWARD -o eth0 -i veth2 -j ACCEPT
  53. # Allow forwarding
  54. sysctl -w net.ipv4.ip_forward=1
  55. echo -e "\nPing from host to namespaces"
  56. ping -c 3 192.168.0.1
  57. ping -c 3 192.168.1.1
  58. echo -e "\nPing from namespace to host"
  59. $NS1 ping -c 3 192.168.0.1
  60. $NS1 ping -c 3 192.168.0.1
  61. $NS2 ping -c 3 192.168.0.2
  62. $NS2 ping -c 3 192.168.0.2
  63. echo -e "\nPing from ns1 to ns2"
  64. $NS1 ping -c 3 192.168.0.1
  65. echo -e "\nPing from ns2 to ns1"
  66. $NS2 ping -c 3 192.168.0.1
  67. ################################################################################
  68. # Memory Leak Check #
  69. ################################################################################
  70. FILENAME_MEMORY_LOG="$TEST_FILEPATH_PREFIX-memory.log"
  71. echo -e "\nStarting a ZeroTier instance in each namespace..."
  72. time_test_start=$(date +%s)
  73. # Spam the CLI as ZeroTier is starting
  74. spam_cli 100
  75. echo "Starting memory leak check"
  76. $NS1 sudo valgrind --demangle=yes --exit-on-first-error=yes \
  77. --error-exitcode=1 \
  78. --xml=yes \
  79. --xml-file=$FILENAME_MEMORY_LOG \
  80. --leak-check=full \
  81. ./zerotier-one node1 -p9996 -U >>node_1.log 2>&1 &
  82. # Second instance, not run in memory profiler
  83. $NS2 sudo ./zerotier-one node2 -U -p9997 >>node_2.log 2>&1 &
  84. ################################################################################
  85. # Online Check #
  86. ################################################################################
  87. echo "Waiting for ZeroTier to come online before attempting test..."
  88. MAX_WAIT_SECS="${MAX_WAIT_SECS:-120}"
  89. node1_online=false
  90. node2_online=false
  91. both_instances_online=false
  92. time_zt_node1_start=$(date +%s)
  93. time_zt_node2_start=$(date +%s)
  94. for ((s = 0; s <= MAX_WAIT_SECS; s++)); do
  95. node1_online="$($ZT1 -j info | jq '.online' 2>/dev/null)"
  96. node2_online="$($ZT2 -j info | jq '.online' 2>/dev/null)"
  97. echo "Checking for online status: try #$s, node1:$node1_online, node2:$node2_online"
  98. if [[ "$node1_online" == "true" ]]; then
  99. time_zt_node1_online=$(date +%s)
  100. fi
  101. if [[ "$node2_online" == "true" ]]; then
  102. time_zt_node2_online=$(date +%s)
  103. fi
  104. if [[ "$node2_online" == "true" && "$node1_online" == "true" ]]; then
  105. both_instances_online=true
  106. break
  107. fi
  108. sleep 1
  109. done
  110. echo -e "\n\nContents of ZeroTier home paths:"
  111. ls -lga node1
  112. tree node1
  113. ls -lga node2
  114. tree node2
  115. echo -e "\n\nRunning ZeroTier processes:"
  116. echo -e "\nNode 1:"
  117. $NS1 ps aux | grep zerotier-one
  118. echo -e "\nNode 2:"
  119. $NS2 ps aux | grep zerotier-one
  120. echo -e "\n\nStatus of each instance:"
  121. echo -e "\n\nNode 1:"
  122. $ZT1 status
  123. echo -e "\n\nNode 2:"
  124. $ZT2 status
  125. if [[ "$both_instances_online" != "true" ]]; then
  126. echo "One or more instances of ZeroTier failed to come online. Aborting test."
  127. exit 1
  128. fi
  129. echo -e "\nJoining networks"
  130. $ZT1 join $TEST_NETWORK
  131. $ZT2 join $TEST_NETWORK
  132. sleep 10
  133. node1_ip4=$($ZT1 get $TEST_NETWORK ip4)
  134. node2_ip4=$($ZT2 get $TEST_NETWORK ip4)
  135. echo "node1_ip4=$node1_ip4"
  136. echo "node2_ip4=$node2_ip4"
  137. echo -e "\nPinging each node"
  138. PING12_FILENAME="$TEST_FILEPATH_PREFIX-ping-1-to-2.txt"
  139. PING21_FILENAME="$TEST_FILEPATH_PREFIX-ping-2-to-1.txt"
  140. $NS1 ping -c 16 $node2_ip4 >$PING12_FILENAME
  141. $NS2 ping -c 16 $node1_ip4 >$PING21_FILENAME
  142. # Parse ping statistics
  143. ping_loss_percent_1_to_2="${ping_loss_percent_1_to_2:-100.0}"
  144. ping_loss_percent_2_to_1="${ping_loss_percent_2_to_1:-100.0}"
  145. ping_loss_percent_1_to_2=$(cat $PING12_FILENAME |
  146. grep "packet loss" | awk '{print $6}' | sed 's/%//')
  147. ping_loss_percent_2_to_1=$(cat $PING21_FILENAME |
  148. grep "packet loss" | awk '{print $6}' | sed 's/%//')
  149. # Normalize loss value
  150. ping_loss_percent_1_to_2=$(echo "scale=2; $ping_loss_percent_1_to_2/100.0" | bc)
  151. ping_loss_percent_2_to_1=$(echo "scale=2; $ping_loss_percent_2_to_1/100.0" | bc)
  152. ################################################################################
  153. # CLI Check #
  154. ################################################################################
  155. echo "Testing basic CLI functionality..."
  156. spam_cli 10
  157. $ZT1 join $TEST_NETWORK
  158. $ZT1 -h
  159. $ZT1 -v
  160. $ZT1 status
  161. $ZT1 info
  162. $ZT1 listnetworks
  163. $ZT1 peers
  164. $ZT1 listpeers
  165. $ZT1 -j status
  166. $ZT1 -j info
  167. $ZT1 -j listnetworks
  168. $ZT1 -j peers
  169. $ZT1 -j listpeers
  170. $ZT1 dump
  171. $ZT1 get $TEST_NETWORK allowDNS
  172. $ZT1 get $TEST_NETWORK allowDefault
  173. $ZT1 get $TEST_NETWORK allowGlobal
  174. $ZT1 get $TEST_NETWORK allowManaged
  175. $ZT1 get $TEST_NETWORK bridge
  176. $ZT1 get $TEST_NETWORK broadcastEnabled
  177. $ZT1 get $TEST_NETWORK dhcp
  178. $ZT1 get $TEST_NETWORK id
  179. $ZT1 get $TEST_NETWORK mac
  180. $ZT1 get $TEST_NETWORK mtu
  181. $ZT1 get $TEST_NETWORK name
  182. $ZT1 get $TEST_NETWORK netconfRevision
  183. $ZT1 get $TEST_NETWORK nwid
  184. $ZT1 get $TEST_NETWORK portDeviceName
  185. $ZT1 get $TEST_NETWORK portError
  186. $ZT1 get $TEST_NETWORK status
  187. $ZT1 get $TEST_NETWORK type
  188. # Test an invalid command
  189. $ZT1 get $TEST_NETWORK derpderp
  190. # TODO: Validate JSON
  191. ################################################################################
  192. # Performance Test #
  193. ################################################################################
  194. FILENAME_PERF_JSON="$TEST_FILEPATH_PREFIX-iperf.json"
  195. echo -e "\nBeginning performance test:"
  196. echo -e "\nStarting server:"
  197. echo "$NS1 iperf3 -s &"
  198. sleep 1
  199. echo -e "\nStarting client:"
  200. sleep 1
  201. echo "$NS2 iperf3 --json -c $node1_ip4 > $FILENAME_PERF_JSON"
  202. cat $FILENAME_PERF_JSON
  203. ################################################################################
  204. # Collect ZeroTier dump files #
  205. ################################################################################
  206. echo -e "\nCollecting ZeroTier dump files"
  207. node1_id=$($ZT1 -j status | jq -r .address)
  208. node2_id=$($ZT2 -j status | jq -r .address)
  209. $ZT1 dump
  210. mv zerotier_dump.txt "$TEST_FILEPATH_PREFIX-node-dump-$node1_id.txt"
  211. $ZT2 dump
  212. mv zerotier_dump.txt "$TEST_FILEPATH_PREFIX-node-dump-$node2_id.txt"
  213. ################################################################################
  214. # Let ZeroTier idle long enough for various timers #
  215. ################################################################################
  216. echo -e "\nIdling ZeroTier for $RUN_LENGTH seconds..."
  217. sleep $RUN_LENGTH
  218. echo -e "\nLeaving networks"
  219. $ZT1 leave $TEST_NETWORK
  220. $ZT2 leave $TEST_NETWORK
  221. sleep 5
  222. ################################################################################
  223. # Stop test #
  224. ################################################################################
  225. echo -e "\nStopping memory check..."
  226. sudo pkill -15 -f valgrind
  227. sleep 10
  228. time_test_end=$(date +%s)
  229. ################################################################################
  230. # Rename ZeroTier stdout/stderr logs #
  231. ################################################################################
  232. mv node_1.log "$TEST_FILEPATH_PREFIX-node-log-$node1_id.txt"
  233. mv node_2.log "$TEST_FILEPATH_PREFIX-node-log-$node2_id.txt"
  234. ################################################################################
  235. # Generate report #
  236. ################################################################################
  237. cat $FILENAME_MEMORY_LOG
  238. DEFINITELY_LOST=$(xmlstarlet sel -t -v '/valgrindoutput/error/xwhat' \
  239. $FILENAME_MEMORY_LOG | grep "definitely" | awk '{print $1;}')
  240. POSSIBLY_LOST=$(xmlstarlet sel -t -v '/valgrindoutput/error/xwhat' \
  241. $FILENAME_MEMORY_LOG | grep "possibly" | awk '{print $1;}')
  242. ################################################################################
  243. # Generate coverage report artifact and summary #
  244. ################################################################################
  245. FILENAME_COVERAGE_JSON="$TEST_FILEPATH_PREFIX-coverage.json"
  246. FILENAME_COVERAGE_HTML="$TEST_FILEPATH_PREFIX-coverage.html"
  247. echo -e "\nGenerating coverage test report..."
  248. gcovr -r . --exclude ext --json-summary $FILENAME_COVERAGE_JSON \
  249. --html >$FILENAME_COVERAGE_HTML
  250. cat $FILENAME_COVERAGE_JSON
  251. COVERAGE_LINE_COVERED=$(cat $FILENAME_COVERAGE_JSON | jq .line_covered)
  252. COVERAGE_LINE_TOTAL=$(cat $FILENAME_COVERAGE_JSON | jq .line_total)
  253. COVERAGE_LINE_PERCENT=$(cat $FILENAME_COVERAGE_JSON | jq .line_percent)
  254. COVERAGE_LINE_COVERED="${COVERAGE_LINE_COVERED:-0}"
  255. COVERAGE_LINE_TOTAL="${COVERAGE_LINE_TOTAL:-0}"
  256. COVERAGE_LINE_PERCENT="${COVERAGE_LINE_PERCENT:-0}"
  257. ################################################################################
  258. # Default values #
  259. ################################################################################
  260. DEFINITELY_LOST="${DEFINITELY_LOST:-0}"
  261. POSSIBLY_LOST="${POSSIBLY_LOST:-0}"
  262. ################################################################################
  263. # Summarize and emit json for trend reporting #
  264. ################################################################################
  265. FILENAME_SUMMARY="$TEST_FILEPATH_PREFIX-summary.json"
  266. time_length_test=$((time_test_end - time_test_start))
  267. time_length_zt_node1_online=$((time_zt_node1_online - time_zt_start))
  268. time_length_zt_node2_online=$((time_zt_node2_online - time_zt_start))
  269. #time_length_zt_join=$((time_zt_join_end-time_zt_join_start))
  270. #time_length_zt_leave=$((time_zt_leave_end-time_zt_leave_start))
  271. #time_length_zt_can_still_ping=$((time_zt_can_still_ping-time_zt_leave_start))
  272. summary=$(
  273. cat <<EOF
  274. {
  275. "version":"$ZTO_VER",
  276. "commit":"$ZTO_COMMIT",
  277. "arch_m":"$(uname -m)",
  278. "arch_a":"$(uname -a)",
  279. "time_length_test":$time_length_test,
  280. "time_length_zt_node1_online":$time_length_zt_node1_online,
  281. "time_length_zt_node2_online":$time_length_zt_node2_online,
  282. "num_possible_bytes_lost": $POSSIBLY_LOST,
  283. "num_definite_bytes_lost": $DEFINITELY_LOST,
  284. "num_incorrect_settings": $POSSIBLY_LOST,
  285. "num_bad_formattings": $POSSIBLY_LOST,
  286. "percent_coverage_branches": $POSSIBLY_LOST,
  287. "coverage_lines_covered": $COVERAGE_LINE_COVERED,
  288. "coverage_lines_total": $COVERAGE_LINE_TOTAL,
  289. "coverage_lines_percent": $COVERAGE_LINE_PERCENT,
  290. "ping_loss_percent_1_to_2": $ping_loss_percent_1_to_2,
  291. "ping_loss_percent_2_to_1": $ping_loss_percent_2_to_1,
  292. "mean_latency_ping_random": $POSSIBLY_LOST,
  293. "mean_latency_ping_netns": $POSSIBLY_LOST,
  294. "mean_pdv_random": $POSSIBLY_LOST,
  295. "mean_pdv_netns": $POSSIBLY_LOST,
  296. "mean_perf_netns": $POSSIBLY_LOST
  297. }
  298. EOF
  299. )
  300. echo $summary >$FILENAME_SUMMARY
  301. cat $FILENAME_SUMMARY
  302. }
  303. ################################################################################
  304. # CLI Check #
  305. ################################################################################
  306. spam_cli() {
  307. echo "Spamming CLI..."
  308. # Rapidly spam the CLI with joins/leaves
  309. MAX_TRIES="${1:-10}"
  310. for ((s = 0; s <= MAX_TRIES; s++)); do
  311. $ZT1 status
  312. $ZT2 status
  313. sleep 0.1
  314. done
  315. SPAM_TRIES=128
  316. for ((s = 0; s <= SPAM_TRIES; s++)); do
  317. $ZT1 join $TEST_NETWORK
  318. done
  319. for ((s = 0; s <= SPAM_TRIES; s++)); do
  320. $ZT1 leave $TEST_NETWORK
  321. done
  322. for ((s = 0; s <= SPAM_TRIES; s++)); do
  323. $ZT1 leave $TEST_NETWORK
  324. $ZT1 join $TEST_NETWORK
  325. done
  326. }
  327. main "$@"