parent
13a62666d9
commit
91f611157a
@ -235,7 +235,6 @@ then
|
|||||||
sudo LANG=C chroot $FILESYSTEM_ROOT apt-get -y install kubelet=${KUBERNETES_VERSION}-00
|
sudo LANG=C chroot $FILESYSTEM_ROOT apt-get -y install kubelet=${KUBERNETES_VERSION}-00
|
||||||
sudo LANG=C chroot $FILESYSTEM_ROOT apt-get -y install kubectl=${KUBERNETES_VERSION}-00
|
sudo LANG=C chroot $FILESYSTEM_ROOT apt-get -y install kubectl=${KUBERNETES_VERSION}-00
|
||||||
sudo LANG=C chroot $FILESYSTEM_ROOT apt-get -y install kubeadm=${KUBERNETES_VERSION}-00
|
sudo LANG=C chroot $FILESYSTEM_ROOT apt-get -y install kubeadm=${KUBERNETES_VERSION}-00
|
||||||
# kubeadm package auto install kubelet & kubectl
|
|
||||||
else
|
else
|
||||||
echo '[INFO] Skipping Install kubernetes'
|
echo '[INFO] Skipping Install kubernetes'
|
||||||
fi
|
fi
|
||||||
|
@ -414,6 +414,10 @@ sudo https_proxy=$https_proxy LANG=C chroot $FILESYSTEM_ROOT pip3 install azure-
|
|||||||
sudo https_proxy=$https_proxy LANG=C chroot $FILESYSTEM_ROOT pip3 install watchdog==0.10.3
|
sudo https_proxy=$https_proxy LANG=C chroot $FILESYSTEM_ROOT pip3 install watchdog==0.10.3
|
||||||
|
|
||||||
{% if include_kubernetes == "y" %}
|
{% if include_kubernetes == "y" %}
|
||||||
|
# Point to kubelet to /etc/resolv.conf
|
||||||
|
#
|
||||||
|
echo 'KUBELET_EXTRA_ARGS="--resolv-conf=/etc/resolv.conf"' | sudo tee -a $FILESYSTEM_ROOT/etc/default/kubelet
|
||||||
|
|
||||||
# Copy Flannel conf file into sonic-templates
|
# Copy Flannel conf file into sonic-templates
|
||||||
#
|
#
|
||||||
sudo cp $BUILD_TEMPLATES/kube_cni.10-flannel.conflist $FILESYSTEM_ROOT_USR_SHARE_SONIC_TEMPLATES/
|
sudo cp $BUILD_TEMPLATES/kube_cni.10-flannel.conflist $FILESYSTEM_ROOT_USR_SHARE_SONIC_TEMPLATES/
|
||||||
@ -434,6 +438,25 @@ sudo cp ${files_path}/container_startup.py ${FILESYSTEM_ROOT_USR_SHARE_SONIC_SCR
|
|||||||
sudo chmod a+x ${FILESYSTEM_ROOT_USR_SHARE_SONIC_SCRIPTS}/container_startup.py
|
sudo chmod a+x ${FILESYSTEM_ROOT_USR_SHARE_SONIC_SCRIPTS}/container_startup.py
|
||||||
|
|
||||||
# Config file used by container mgmt scripts/service
|
# Config file used by container mgmt scripts/service
|
||||||
|
fl="${files_path}/remote_ctr.config.json"
|
||||||
|
use_k8s_as_http_proxy=$(python3 -c 'import json
|
||||||
|
with open("'${fl}'", "r") as s:
|
||||||
|
d=json.load(s);print(d.get("use_k8s_as_http_proxy", ""))
|
||||||
|
')
|
||||||
|
if [ "${use_k8s_as_http_proxy}" == "y" ]; then
|
||||||
|
# create proxy files for docker using private IP which will
|
||||||
|
# be later directed to k8s master upon config
|
||||||
|
PROXY_INFO="${kube_docker_proxy}"
|
||||||
|
cat <<EOT | sudo tee $FILESYSTEM_ROOT/etc/systemd/system/docker.service.d/http_proxy.conf > /dev/null
|
||||||
|
[Service]
|
||||||
|
Environment="HTTP_PROXY=${PROXY_INFO}"
|
||||||
|
EOT
|
||||||
|
cat <<EOT | sudo tee $FILESYSTEM_ROOT/etc/systemd/system/docker.service.d/https_proxy.conf > /dev/null
|
||||||
|
[Service]
|
||||||
|
Environment="HTTPS_PROXY=${PROXY_INFO}"
|
||||||
|
EOT
|
||||||
|
fi
|
||||||
|
|
||||||
sudo cp ${files_path}/remote_ctr.config.json ${FILESYSTEM_ROOT_ETC_SONIC}/
|
sudo cp ${files_path}/remote_ctr.config.json ${FILESYSTEM_ROOT_ETC_SONIC}/
|
||||||
|
|
||||||
# Remote container management service files
|
# Remote container management service files
|
||||||
|
12
rules/config
12
rules/config
@ -142,18 +142,24 @@ INCLUDE_NAT = y
|
|||||||
# TELEMETRY_WRITABLE - Enable write/config operations via the gNMI interface.
|
# TELEMETRY_WRITABLE - Enable write/config operations via the gNMI interface.
|
||||||
# Uncomment to enable:
|
# Uncomment to enable:
|
||||||
# TELEMETRY_WRITABLE = y
|
# TELEMETRY_WRITABLE = y
|
||||||
|
|
||||||
|
# INCLUDE_MACSEC - build docker-macsec for macsec support
|
||||||
|
INCLUDE_MACSEC = y
|
||||||
|
|
||||||
# INCLUDE_KUBERNETES - if set to y kubernetes packages are installed to be able to
|
# INCLUDE_KUBERNETES - if set to y kubernetes packages are installed to be able to
|
||||||
# run as worker node in kubernetes cluster.
|
# run as worker node in kubernetes cluster.
|
||||||
INCLUDE_KUBERNETES = n
|
INCLUDE_KUBERNETES = n
|
||||||
|
|
||||||
|
KUBE_DOCKER_PROXY = http://172.16.1.1:3128/
|
||||||
|
|
||||||
# KUBERNETES_VERSION - Set to the required version.
|
# KUBERNETES_VERSION - Set to the required version.
|
||||||
# K8s_GCR_IO_PAUSE_VERSION - Version of k8s universal pause container image
|
# K8s_GCR_IO_PAUSE_VERSION - Version of k8s universal pause container image
|
||||||
# These are Used *only* when INCLUDE_KUBERNETES=y
|
# These are Used *only* when INCLUDE_KUBERNETES=y
|
||||||
# NOTE: As a worker node it has to run version compatible to kubernetes master.
|
# NOTE: As a worker node it has to run version compatible to kubernetes master.
|
||||||
#
|
#
|
||||||
KUBERNETES_VERSION = 1.18.6
|
KUBERNETES_VERSION = 1.21.1
|
||||||
KUBERNETES_CNI_VERSION = 0.8.6
|
KUBERNETES_CNI_VERSION = 0.8.7
|
||||||
K8s_GCR_IO_PAUSE_VERSION = 3.2
|
K8s_GCR_IO_PAUSE_VERSION = 3.4.1
|
||||||
|
|
||||||
# SONIC_ENABLE_IMAGE_SIGNATURE - enable image signature
|
# SONIC_ENABLE_IMAGE_SIGNATURE - enable image signature
|
||||||
# To not use the auto-generated self-signed certificate, the required files to sign the image as below:
|
# To not use the auto-generated self-signed certificate, the required files to sign the image as below:
|
||||||
|
1
slave.mk
1
slave.mk
@ -903,6 +903,7 @@ $(addprefix $(TARGET_PATH)/, $(SONIC_INSTALLERS)) : $(TARGET_PATH)/% : \
|
|||||||
export shutdown_bgp_on_start="$(SHUTDOWN_BGP_ON_START)"
|
export shutdown_bgp_on_start="$(SHUTDOWN_BGP_ON_START)"
|
||||||
export default_buffer_model="$(SONIC_BUFFER_MODEL)"
|
export default_buffer_model="$(SONIC_BUFFER_MODEL)"
|
||||||
export include_kubernetes="$(INCLUDE_KUBERNETES)"
|
export include_kubernetes="$(INCLUDE_KUBERNETES)"
|
||||||
|
export kube_docker_proxy="$(KUBE_DOCKER_PROXY)"
|
||||||
export enable_pfcwd_on_start="$(ENABLE_PFCWD_ON_START)"
|
export enable_pfcwd_on_start="$(ENABLE_PFCWD_ON_START)"
|
||||||
export installer_debs="$(addprefix $(IMAGE_DISTRO_DEBS_PATH)/,$($*_INSTALLS))"
|
export installer_debs="$(addprefix $(IMAGE_DISTRO_DEBS_PATH)/,$($*_INSTALLS))"
|
||||||
export lazy_installer_debs="$(foreach deb, $($*_LAZY_INSTALLS),$(foreach device, $($(deb)_PLATFORM),$(addprefix $(device)@, $(IMAGE_DISTRO_DEBS_PATH)/$(deb))))"
|
export lazy_installer_debs="$(foreach deb, $($*_LAZY_INSTALLS),$(foreach device, $($(deb)_PLATFORM),$(addprefix $(device)@, $(IMAGE_DISTRO_DEBS_PATH)/$(deb))))"
|
||||||
|
135
src/sonic-ctrmgrd/ctrmgr/ctrmgr_iptables.py
Normal file
135
src/sonic-ctrmgrd/ctrmgr/ctrmgr_iptables.py
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import ipaddress
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import socket
|
||||||
|
import subprocess
|
||||||
|
import syslog
|
||||||
|
|
||||||
|
UNIT_TESTING = 0
|
||||||
|
|
||||||
|
# NOTE:
|
||||||
|
# Unable to use python-iptables as that does not create rules per ip-tables default
|
||||||
|
# which is nf_tables. So rules added via iptc package will not be listed under
|
||||||
|
# "sudo iptables -t nat -L -n". But available in kernel. To list, we need to
|
||||||
|
# use legacy mode as "sudo iptables-legacy -t nat -L -n".
|
||||||
|
# As we can't use two modes and using non-default could make any debugging effort
|
||||||
|
# very tough.
|
||||||
|
|
||||||
|
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
DST_FILE = "/etc/systemd/system/docker.service.d/http_proxy.conf"
|
||||||
|
DST_IP = None
|
||||||
|
DST_PORT = None
|
||||||
|
SQUID_PORT = "3128"
|
||||||
|
|
||||||
|
def _get_ip(ip_str):
|
||||||
|
ret = ""
|
||||||
|
if ip_str:
|
||||||
|
try:
|
||||||
|
ipaddress.ip_address(ip_str)
|
||||||
|
ret = ip_str
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if not ret:
|
||||||
|
try:
|
||||||
|
ret = socket.gethostbyname(ip_str)
|
||||||
|
except (OSError, socket.error):
|
||||||
|
pass
|
||||||
|
if not ret:
|
||||||
|
syslog.syslog(syslog.LOG_ERR, "{} is neither IP nor resolves to IP".
|
||||||
|
format(ip_str))
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
def _get_dst_info():
|
||||||
|
global DST_IP, DST_PORT
|
||||||
|
DST_IP = None
|
||||||
|
DST_PORT = None
|
||||||
|
print("DST_FILE={}".format(DST_FILE))
|
||||||
|
if os.path.exists(DST_FILE):
|
||||||
|
with open(DST_FILE, "r") as s:
|
||||||
|
for line in s.readlines():
|
||||||
|
url_match = re.search('^Environment=.HTTP_PROXY=(.+?)"', line)
|
||||||
|
if url_match:
|
||||||
|
url = urlparse(url_match.group(1))
|
||||||
|
DST_IP = _get_ip(url.hostname)
|
||||||
|
DST_PORT = url.port
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
print("{} not available".format(DST_FILE))
|
||||||
|
print("DST_IP={}".format(DST_IP))
|
||||||
|
|
||||||
|
|
||||||
|
def _is_rule_match(rule):
|
||||||
|
expect = "DNAT tcp -- 0.0.0.0/0 {} tcp dpt:{} to:".format(
|
||||||
|
DST_IP, DST_PORT)
|
||||||
|
|
||||||
|
# Remove duplicate spaces
|
||||||
|
rule = " ".join(rule.split()).strip()
|
||||||
|
|
||||||
|
if rule.startswith(expect):
|
||||||
|
return rule[len(expect):]
|
||||||
|
else:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def check_proc(proc):
|
||||||
|
if proc.returncode:
|
||||||
|
syslog.syslog(syslog.LOG_ERR, "Failed to run: cmd: {}".format(proc.args))
|
||||||
|
syslog.syslog(syslog.LOG_ERR, "Failed to run: stdout: {}".format(proc.stdout))
|
||||||
|
syslog.syslog(syslog.LOG_ERR, "Failed to run: stderr: {}".format(proc.stderr))
|
||||||
|
if not UNIT_TESTING:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
|
||||||
|
def iptable_proxy_rule_upd(ip_str, port = SQUID_PORT):
|
||||||
|
_get_dst_info()
|
||||||
|
if not DST_IP:
|
||||||
|
# There is no proxy in use. Bail out.
|
||||||
|
return ""
|
||||||
|
|
||||||
|
destination = ""
|
||||||
|
if ip_str:
|
||||||
|
upd_ip = _get_ip(ip_str)
|
||||||
|
if not upd_ip:
|
||||||
|
return ""
|
||||||
|
destination = "{}:{}".format(upd_ip, port)
|
||||||
|
|
||||||
|
found = False
|
||||||
|
num = 0
|
||||||
|
|
||||||
|
while True:
|
||||||
|
num += 1
|
||||||
|
|
||||||
|
cmd = "sudo iptables -t nat -n -L OUTPUT {}".format(num)
|
||||||
|
proc = subprocess.run(cmd, shell=True, capture_output=True)
|
||||||
|
check_proc(proc)
|
||||||
|
|
||||||
|
if not proc.stdout:
|
||||||
|
# No more rule
|
||||||
|
break
|
||||||
|
|
||||||
|
rule_dest = _is_rule_match(proc.stdout.decode("utf-8").strip())
|
||||||
|
if rule_dest:
|
||||||
|
if not found and destination and (rule_dest == destination):
|
||||||
|
found = True
|
||||||
|
else:
|
||||||
|
# Duplicate or different IP - delete it
|
||||||
|
cmd = "sudo iptables -t nat -D OUTPUT {}".format(num)
|
||||||
|
proc = subprocess.run(cmd, shell=True, capture_output=True)
|
||||||
|
check_proc(proc)
|
||||||
|
# Decrement number to accommodate deleted rule
|
||||||
|
num -= 1
|
||||||
|
|
||||||
|
if destination and not found:
|
||||||
|
cmd = "sudo iptables -t nat -A OUTPUT -p tcp -d {} --dport {} -j DNAT --to-destination {}".format(
|
||||||
|
DST_IP, DST_PORT, destination)
|
||||||
|
proc = subprocess.run(cmd, shell=True, capture_output=True)
|
||||||
|
|
||||||
|
check_proc(proc)
|
||||||
|
|
||||||
|
return destination
|
@ -8,6 +8,7 @@ import sys
|
|||||||
import syslog
|
import syslog
|
||||||
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
from ctrmgr.ctrmgr_iptables import iptable_proxy_rule_upd
|
||||||
|
|
||||||
from swsscommon import swsscommon
|
from swsscommon import swsscommon
|
||||||
from sonic_py_common import device_info
|
from sonic_py_common import device_info
|
||||||
@ -87,11 +88,13 @@ dflt_st_feat= {
|
|||||||
JOIN_LATENCY = "join_latency_on_boot_seconds"
|
JOIN_LATENCY = "join_latency_on_boot_seconds"
|
||||||
JOIN_RETRY = "retry_join_interval_seconds"
|
JOIN_RETRY = "retry_join_interval_seconds"
|
||||||
LABEL_RETRY = "retry_labels_update_seconds"
|
LABEL_RETRY = "retry_labels_update_seconds"
|
||||||
|
USE_K8S_PROXY = "use_k8s_as_http_proxy"
|
||||||
|
|
||||||
remote_ctr_config = {
|
remote_ctr_config = {
|
||||||
JOIN_LATENCY: 10,
|
JOIN_LATENCY: 10,
|
||||||
JOIN_RETRY: 10,
|
JOIN_RETRY: 10,
|
||||||
LABEL_RETRY: 2
|
LABEL_RETRY: 2,
|
||||||
|
USE_K8S_PROXY: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
def log_debug(m):
|
def log_debug(m):
|
||||||
@ -309,6 +312,9 @@ class RemoteServerHandler:
|
|||||||
|
|
||||||
self.start_time = datetime.datetime.now()
|
self.start_time = datetime.datetime.now()
|
||||||
|
|
||||||
|
if remote_ctr_config[USE_K8S_PROXY] == "y":
|
||||||
|
iptable_proxy_rule_upd(self.cfg_server[CFG_SER_IP])
|
||||||
|
|
||||||
if not self.st_server[ST_FEAT_UPDATE_TS]:
|
if not self.st_server[ST_FEAT_UPDATE_TS]:
|
||||||
# This is upon system start. Sleep 10m before join
|
# This is upon system start. Sleep 10m before join
|
||||||
self.start_time += datetime.timedelta(
|
self.start_time += datetime.timedelta(
|
||||||
@ -336,6 +342,9 @@ class RemoteServerHandler:
|
|||||||
log_debug("Received config update: {}".format(str(data)))
|
log_debug("Received config update: {}".format(str(data)))
|
||||||
self.cfg_server = cfg_data
|
self.cfg_server = cfg_data
|
||||||
|
|
||||||
|
if remote_ctr_config[USE_K8S_PROXY] == "y":
|
||||||
|
iptable_proxy_rule_upd(self.cfg_server[CFG_SER_IP])
|
||||||
|
|
||||||
if self.pending:
|
if self.pending:
|
||||||
tnow = datetime.datetime.now()
|
tnow = datetime.datetime.now()
|
||||||
if tnow < self.start_time:
|
if tnow < self.start_time:
|
||||||
@ -359,7 +368,7 @@ class RemoteServerHandler:
|
|||||||
|
|
||||||
ip = self.cfg_server[CFG_SER_IP]
|
ip = self.cfg_server[CFG_SER_IP]
|
||||||
disable = self.cfg_server[CFG_SER_DISABLE] != "false"
|
disable = self.cfg_server[CFG_SER_DISABLE] != "false"
|
||||||
|
|
||||||
pre_state = dict(self.st_server)
|
pre_state = dict(self.st_server)
|
||||||
log_debug("server: handle_update: disable={} ip={}".format(disable, ip))
|
log_debug("server: handle_update: disable={} ip={}".format(disable, ip))
|
||||||
if disable or not ip:
|
if disable or not ip:
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=Container Manager watcher daemon
|
Description=Container Manager watcher daemon
|
||||||
Requires=updategraph.service
|
Requires=caclmgrd.service
|
||||||
After=updategraph.service
|
After=caclmgrd.service
|
||||||
|
BindsTo=sonic.target
|
||||||
|
After=sonic.target
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
Type=simple
|
||||||
@ -11,4 +12,4 @@ Restart=always
|
|||||||
RestartSec=30
|
RestartSec=30
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=sonic.target
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
"join_latency_on_boot_seconds": 300,
|
"join_latency_on_boot_seconds": 300,
|
||||||
"retry_join_interval_seconds": 30,
|
"retry_join_interval_seconds": 30,
|
||||||
"retry_labels_update_seconds": 5,
|
"retry_labels_update_seconds": 5,
|
||||||
"revert_to_local_on_wait_seconds": 60
|
"revert_to_local_on_wait_seconds": 60,
|
||||||
|
"use_k8s_as_http_proxy": "y"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
178
src/sonic-ctrmgrd/tests/ctrmgr_iptables_test.py
Executable file
178
src/sonic-ctrmgrd/tests/ctrmgr_iptables_test.py
Executable file
@ -0,0 +1,178 @@
|
|||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from . import common_test
|
||||||
|
|
||||||
|
sys.path.append("ctrmgr")
|
||||||
|
import ctrmgr_iptables
|
||||||
|
|
||||||
|
|
||||||
|
PROXY_FILE="http_proxy.conf"
|
||||||
|
|
||||||
|
test_data = {
|
||||||
|
"1": {
|
||||||
|
"ip": "10.10.20.20",
|
||||||
|
"port": "3128",
|
||||||
|
"pre_rules": [
|
||||||
|
"DNAT tcp -- 20.20.0.0/0 172.16.1.1 tcp dpt:8080 to:100.127.20.21:8080",
|
||||||
|
"DNAT tcp -- 0.0.0.0/0 172.16.1.1 tcp dpt:3128 to:11.11.11.11:8080",
|
||||||
|
"DNAT tcp -- 0.0.0.0/0 172.16.1.1 tcp dpt:3128 to:11.11.11.11:8080",
|
||||||
|
"DNAT tcp -- 0.0.0.0/0 172.16.1.1 tcp dpt:3128 to:11.11.11.11:8088",
|
||||||
|
"DNAT tcp -- 0.0.0.0/0 172.16.1.1 tcp dpt:3129 to:11.11.11.11:8088"
|
||||||
|
],
|
||||||
|
"post_rules": [
|
||||||
|
"DNAT tcp -- 20.20.0.0/0 172.16.1.1 tcp dpt:8080 to:100.127.20.21:8080",
|
||||||
|
"DNAT tcp -- 0.0.0.0/0 172.16.1.1 tcp dpt:3129 to:11.11.11.11:8088",
|
||||||
|
"DNAT tcp -- 0.0.0.0/0 172.16.1.1 tcp dpt:3128 to:10.10.20.20:3128"
|
||||||
|
],
|
||||||
|
"ret": "10.10.20.20:3128"
|
||||||
|
},
|
||||||
|
"2": {
|
||||||
|
"ip": "",
|
||||||
|
"port": "",
|
||||||
|
"pre_rules": [
|
||||||
|
"DNAT tcp -- 20.20.0.0/0 172.16.1.1 tcp dpt:8080 to:100.127.20.21:8080",
|
||||||
|
"DNAT tcp -- 0.0.0.0/0 172.16.1.1 tcp dpt:3128 to:11.11.11.11:8080",
|
||||||
|
"DNAT tcp -- 0.0.0.0/0 172.16.1.1 tcp dpt:3128 to:11.11.11.11:8080",
|
||||||
|
"DNAT tcp -- 0.0.0.0/0 172.16.1.1 tcp dpt:3128 to:11.11.11.11:8088"
|
||||||
|
],
|
||||||
|
"post_rules": [
|
||||||
|
"DNAT tcp -- 20.20.0.0/0 172.16.1.1 tcp dpt:8080 to:100.127.20.21:8080"
|
||||||
|
],
|
||||||
|
"ret": ""
|
||||||
|
},
|
||||||
|
"3": {
|
||||||
|
"ip": "www.google.com",
|
||||||
|
"port": "3128",
|
||||||
|
"pre_rules": [
|
||||||
|
"DNAT tcp -- 20.20.0.0/0 172.16.1.1 tcp dpt:8080 to:100.127.20.21:8080",
|
||||||
|
"DNAT tcp -- 0.0.0.0/0 172.16.1.1 tcp dpt:3128 to:11.11.11.11:8080"
|
||||||
|
],
|
||||||
|
"post_rules": [
|
||||||
|
"DNAT tcp -- 20.20.0.0/0 172.16.1.1 tcp dpt:8080 to:100.127.20.21:8080",
|
||||||
|
"DNAT tcp -- 0.0.0.0/0 172.16.1.1 tcp dpt:3128 to:.*3128"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"4": {
|
||||||
|
"ip": "www.google.comx",
|
||||||
|
"port": "3128",
|
||||||
|
"pre_rules": [
|
||||||
|
"DNAT tcp -- 20.20.0.0/0 172.16.1.1 tcp dpt:8080 to:100.127.20.21:8080",
|
||||||
|
"DNAT tcp -- 0.0.0.0/0 172.16.1.1 tcp dpt:3128 to:11.11.11.11:8080"
|
||||||
|
],
|
||||||
|
"post_rules": [
|
||||||
|
"DNAT tcp -- 20.20.0.0/0 172.16.1.1 tcp dpt:8080 to:100.127.20.21:8080",
|
||||||
|
"DNAT tcp -- 0.0.0.0/0 172.16.1.1 tcp dpt:3128 to:11.11.11.11:8080"
|
||||||
|
],
|
||||||
|
"ret": ""
|
||||||
|
},
|
||||||
|
"5": {
|
||||||
|
"ip": "www.google.comx",
|
||||||
|
"port": "3128",
|
||||||
|
"conf_file": "no_proxy.conf",
|
||||||
|
"pre_rules": [
|
||||||
|
"DNAT tcp -- 20.20.0.0/0 172.16.1.1 tcp dpt:8080 to:100.127.20.21:8080",
|
||||||
|
"DNAT tcp -- 0.0.0.0/0 172.16.1.1 tcp dpt:3128 to:11.11.11.11:8080"
|
||||||
|
],
|
||||||
|
"post_rules": [
|
||||||
|
"DNAT tcp -- 20.20.0.0/0 172.16.1.1 tcp dpt:8080 to:100.127.20.21:8080",
|
||||||
|
"DNAT tcp -- 0.0.0.0/0 172.16.1.1 tcp dpt:3128 to:11.11.11.11:8080"
|
||||||
|
],
|
||||||
|
"ret": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
current_tc = None
|
||||||
|
current_rules = None
|
||||||
|
|
||||||
|
class proc:
|
||||||
|
returncode = 0
|
||||||
|
stdout = None
|
||||||
|
stderr = None
|
||||||
|
|
||||||
|
def __init__(self, ret, stdout, stderr):
|
||||||
|
self.returncode = ret
|
||||||
|
self.stdout = bytearray(stdout, 'utf-8')
|
||||||
|
self.stderr = bytearray(stderr, 'utf-8')
|
||||||
|
print("out={} err={}".format(stdout, stderr))
|
||||||
|
|
||||||
|
|
||||||
|
def mock_subproc_run(cmd, shell, capture_output):
|
||||||
|
cmd_prefix = "sudo iptables -t nat "
|
||||||
|
list_cmd = "{}-n -L OUTPUT ".format(cmd_prefix)
|
||||||
|
del_cmd = "{}-D OUTPUT ".format(cmd_prefix)
|
||||||
|
ins_cmd = "{}-A OUTPUT -p tcp -d ".format(cmd_prefix)
|
||||||
|
|
||||||
|
assert shell
|
||||||
|
|
||||||
|
print("cmd={}".format(cmd))
|
||||||
|
if cmd.startswith(list_cmd):
|
||||||
|
num = int(cmd[len(list_cmd):])
|
||||||
|
out = current_rules[num] if len(current_rules) > num else ""
|
||||||
|
return proc(0, out, "")
|
||||||
|
|
||||||
|
if cmd.startswith(del_cmd):
|
||||||
|
num = int(cmd[len(del_cmd):])
|
||||||
|
if num >= len(current_rules):
|
||||||
|
print("delete num={} is greater than len={}".format(num, len(current_rules)))
|
||||||
|
print("current_rules = {}".format(current_rules))
|
||||||
|
assert False
|
||||||
|
del current_rules[num]
|
||||||
|
return proc(0, "", "")
|
||||||
|
|
||||||
|
if cmd.startswith(ins_cmd):
|
||||||
|
l = cmd.split()
|
||||||
|
assert len(l) == 16
|
||||||
|
rule = "DNAT tcp -- 0.0.0.0/0 {} tcp dpt:{} to:{}".format(l[9], l[11], l[-1])
|
||||||
|
current_rules.append(rule)
|
||||||
|
return proc(0, "", "")
|
||||||
|
|
||||||
|
print("unknown cmd: {}".format(cmd))
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def match_rules(pattern_list, str_list):
|
||||||
|
if len(pattern_list) != len(str_list):
|
||||||
|
print("pattern len {} != given {}".format(
|
||||||
|
len(pattern_list), len(str_list)))
|
||||||
|
return False
|
||||||
|
|
||||||
|
for i in range(len(pattern_list)):
|
||||||
|
if not re.match(pattern_list[i], str_list[i]):
|
||||||
|
print("{}: {} != {}".format(i, pattern_list[i], str_list[i]))
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class TestIPTableUpdate(object):
|
||||||
|
|
||||||
|
@patch("ctrmgr_iptables.subprocess.run")
|
||||||
|
def test_table(self, mock_proc):
|
||||||
|
global current_rules, current_tc
|
||||||
|
|
||||||
|
mock_proc.side_effect = mock_subproc_run
|
||||||
|
for i, tc in test_data.items():
|
||||||
|
print("----- Test: {} Start ------------------".format(i))
|
||||||
|
current_tc = tc
|
||||||
|
current_rules = tc["pre_rules"].copy()
|
||||||
|
|
||||||
|
ctrmgr_iptables.DST_IP = ""
|
||||||
|
ctrmgr_iptables.DST_PORT = ""
|
||||||
|
ctrmgr_iptables.DST_FILE = os.path.join(
|
||||||
|
os.path.dirname(os.path.realpath(__file__)),
|
||||||
|
tc.get("conf_file", PROXY_FILE))
|
||||||
|
ret = ctrmgr_iptables.iptable_proxy_rule_upd(tc["ip"], tc["port"])
|
||||||
|
if "ret" in tc:
|
||||||
|
assert ret == tc["ret"]
|
||||||
|
if not match_rules(tc["post_rules"], current_rules):
|
||||||
|
print("current_rules={}".format(current_rules))
|
||||||
|
print("post_rules={}".format(tc["post_rules"]))
|
||||||
|
assert False
|
||||||
|
print("----- Test: {} End ------------------".format(i))
|
||||||
|
|
||||||
|
|
@ -8,6 +8,7 @@ from . import common_test
|
|||||||
|
|
||||||
sys.path.append("ctrmgr")
|
sys.path.append("ctrmgr")
|
||||||
import ctrmgrd
|
import ctrmgrd
|
||||||
|
import ctrmgr.ctrmgr_iptables
|
||||||
|
|
||||||
|
|
||||||
# ctrmgrd test cases
|
# ctrmgrd test cases
|
||||||
@ -387,6 +388,11 @@ class TestContainerStartup(object):
|
|||||||
ctrmgrd.UNIT_TESTING = 1
|
ctrmgrd.UNIT_TESTING = 1
|
||||||
ctrmgrd.SONIC_CTR_CONFIG = (
|
ctrmgrd.SONIC_CTR_CONFIG = (
|
||||||
common_test.create_remote_ctr_config_json())
|
common_test.create_remote_ctr_config_json())
|
||||||
|
ctrmgr.ctrmgr_iptables.UNIT_TESTING = 1
|
||||||
|
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
ctrmgr.ctrmgr_iptables.UNIT_TESTING = 0
|
||||||
|
|
||||||
|
|
||||||
@patch("ctrmgrd.swsscommon.DBConnector")
|
@patch("ctrmgrd.swsscommon.DBConnector")
|
||||||
@ -421,6 +427,7 @@ class TestContainerStartup(object):
|
|||||||
ret = common_test.check_kube_actions()
|
ret = common_test.check_kube_actions()
|
||||||
assert ret == 0
|
assert ret == 0
|
||||||
common_test.mock_selector.SLEEP_SECS = 0
|
common_test.mock_selector.SLEEP_SECS = 0
|
||||||
|
self.clear()
|
||||||
|
|
||||||
|
|
||||||
@patch("ctrmgrd.swsscommon.DBConnector")
|
@patch("ctrmgrd.swsscommon.DBConnector")
|
||||||
@ -450,6 +457,7 @@ class TestContainerStartup(object):
|
|||||||
|
|
||||||
ret = common_test.check_tables_returned()
|
ret = common_test.check_tables_returned()
|
||||||
assert ret == 0
|
assert ret == 0
|
||||||
|
self.clear()
|
||||||
|
|
||||||
|
|
||||||
@patch("ctrmgrd.swsscommon.DBConnector")
|
@patch("ctrmgrd.swsscommon.DBConnector")
|
||||||
@ -486,3 +494,4 @@ class TestContainerStartup(object):
|
|||||||
|
|
||||||
ret = common_test.check_kube_actions()
|
ret = common_test.check_kube_actions()
|
||||||
assert ret == 0
|
assert ret == 0
|
||||||
|
self.clear()
|
||||||
|
2
src/sonic-ctrmgrd/tests/http_proxy.conf
Normal file
2
src/sonic-ctrmgrd/tests/http_proxy.conf
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[Service]
|
||||||
|
Environment="HTTP_PROXY=http://172.16.1.1:3128/"
|
2
src/sonic-ctrmgrd/tests/no_proxy.conf
Normal file
2
src/sonic-ctrmgrd/tests/no_proxy.conf
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[Service]
|
||||||
|
Environment="NO_PROXY=10.10.10.10"
|
Reference in New Issue
Block a user