Skip to content

Commit 512cfac

Browse files
authored
Merge branch 'sonic-net:master' into master
2 parents 2303bb4 + e93494c commit 512cfac

18 files changed

+298
-32
lines changed

data/debian/sonic-host-services-data.aaastatsd.service

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[Unit]
22
Description=AAA Statistics Collection daemon
33
Requires=hostcfgd.service
4-
After=hostcfgd.service updategraph.service
4+
After=hostcfgd.service config-setup.service
55
BindsTo=sonic.target
66
After=sonic.target
77

data/debian/sonic-host-services-data.caclmgrd.service

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[Unit]
22
Description=Control Plane ACL configuration daemon
3-
Requires=updategraph.service
4-
After=updategraph.service
3+
Requires=config-setup.service
4+
After=config-setup.service
55
BindsTo=sonic.target
66
After=sonic.target
77

data/debian/sonic-host-services-data.determine-reboot-cause.service

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[Unit]
22
Description=Reboot cause determination service
3-
Requires=rc-local.service database.service
4-
After=rc-local.service database.service
3+
Requires=rc-local.service
4+
After=rc-local.service
55

66
[Service]
77
Type=oneshot

data/debian/sonic-host-services-data.featured.service

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[Unit]
22
Description=Feature configuration daemon
3-
Requires=updategraph.service
4-
After=updategraph.service
3+
Requires=config-setup.service
4+
After=config-setup.service
55
BindsTo=sonic.target
66
After=sonic.target
77

data/debian/sonic-host-services-data.hostcfgd.service

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[Unit]
22
Description=Host config enforcer daemon
3-
Requires=updategraph.service
4-
After=updategraph.service
3+
Requires=config-setup.service
4+
After=config-setup.service
55
BindsTo=sonic.target
66
After=sonic.target
77

data/debian/sonic-host-services-data.procdockerstatsd.service

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[Unit]
22
Description=Process and docker CPU/memory utilization data export daemon
3-
Requires=database.service updategraph.service
4-
After=database.service updategraph.service
3+
Requires=database.service config-setup.service
4+
After=database.service config-setup.service
55
BindsTo=sonic.target
66
After=sonic.target
77

data/debian/sonic-host-services-data.sonic-hostservice.service

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@ RestartSec=10
1212
TimeoutStopSec=3
1313

1414
[Install]
15-
WantedBy=mgmt-framework.service telemetry.service
15+
WantedBy=mgmt-framework.service telemetry.service gnmi.service
1616

scripts/caclmgrd

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -806,6 +806,12 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
806806
self.log_info(" " + ' '.join(cmd))
807807

808808
self.run_commands(iptables_cmds)
809+
if self.bfdAllowed:
810+
self.allow_bfd_protocol(namespace)
811+
if self.VxlanAllowed:
812+
fvs = swsscommon.FieldValuePairs([("src_ip", self.VxlanSrcIP)])
813+
self.allow_vxlan_port(namespace, fvs)
814+
809815

810816
self.update_control_plane_nat_acls(namespace, service_to_source_ip_map, config_db_connector)
811817

scripts/hostcfgd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1804,6 +1804,7 @@ class HostConfigDaemon:
18041804
def callback(table, key, data):
18051805
if data is None:
18061806
op = "DEL"
1807+
data = {}
18071808
else:
18081809
op = "SET"
18091810
return func(key, op, data)

scripts/procdockerstatsd

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ Daemon which periodically gathers process and docker statistics and pushes the d
55
'''
66

77
import os
8+
import psutil
89
import re
910
import subprocess
1011
import sys
1112
import time
12-
from datetime import datetime
13+
from datetime import datetime, timedelta
1314

1415
from sonic_py_common import daemon_base
1516
from swsscommon import swsscommon
@@ -136,18 +137,30 @@ class ProcDockerStats(daemon_base.DaemonBase):
136137
return True
137138

138139
def update_processstats_command(self):
139-
cmd0 = ["ps", "-eo", "uid,pid,ppid,%mem,%cpu,stime,tty,time,cmd", "--sort", "-%cpu"]
140-
cmd1 = ["head", "-1024"]
141-
exitcode, data = getstatusoutput_noshell_pipe(cmd0, cmd1)
142-
if any(exitcode):
143-
cmd = ' | '.join([' '.join(cmd0), ' '.join(cmd1)])
144-
self.log_error("Error running command '{}'".format(cmd))
145-
data = None
146-
processdata = self.format_process_cmd_output(data)
147-
value = ""
140+
processdata = []
141+
for process in psutil.process_iter(['pid', 'ppid', 'memory_percent', 'cpu_percent', 'create_time', 'cmdline']):
142+
try:
143+
uid = process.uids()[0]
144+
pid = process.pid
145+
ppid = process.ppid()
146+
mem = process.memory_percent()
147+
cpu = process.cpu_percent()
148+
stime = process.create_time()
149+
stime_formatted = datetime.utcfromtimestamp(stime).strftime("%b%d")
150+
tty = process.terminal()
151+
ttime = process.cpu_times()
152+
time_formatted = str(timedelta(seconds=int(ttime.user + ttime.system)))
153+
cmd = ' '.join(process.cmdline())
154+
155+
row = {'PID': pid, 'UID': uid, 'PPID': ppid, '%CPU': cpu, '%MEM': mem, 'STIME': stime_formatted, 'TT': tty, 'TIME': time_formatted, 'CMD': cmd}
156+
processdata.append(row)
157+
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
158+
pass
159+
148160
# wipe out all data before updating with new values
149161
self.state_db.delete_all_by_pattern('STATE_DB', 'PROCESS_STATS|*')
150-
for row in processdata[0:]:
162+
163+
for row in processdata:
151164
cid = row.get('PID')
152165
if cid:
153166
value = 'PROCESS_STATS|{}'.format(cid)
@@ -157,7 +170,7 @@ class ProcDockerStats(daemon_base.DaemonBase):
157170
self.update_state_db(value, 'PPID', ppid)
158171
cpu = row.get('%CPU')
159172
self.update_state_db(value, '%CPU', str(cpu))
160-
mem = row.get('%MEM')
173+
mem = round(row.get('%MEM'), 1)
161174
self.update_state_db(value, '%MEM', str(mem))
162175
stime = row.get('STIME')
163176
self.update_state_db(value, 'STIME', stime)

setup.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
'systemd-python',
4848
'Jinja2>=2.10',
4949
'PyGObject',
50+
'psutil'
5051
] + sonic_dependencies,
5152
setup_requires = [
5253
'pytest-runner',
@@ -57,7 +58,8 @@
5758
'pytest',
5859
'pyfakefs',
5960
'sonic-py-common',
60-
'deepdiff==6.2.2'
61+
'deepdiff==6.2.2',
62+
'psutil'
6163
],
6264
extras_require = {
6365
"testing": [

tests/caclmgrd/caclmgrd_bfd_test.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import os
22
import sys
3-
import swsscommon
3+
from swsscommon import swsscommon
44

55
from parameterized import parameterized
66
from sonic_py_common.general import load_module_from_source
@@ -18,7 +18,7 @@ class TestCaclmgrdBfd(TestCase):
1818
Test caclmgrd bfd
1919
"""
2020
def setUp(self):
21-
swsscommon.swsscommon.ConfigDBConnector = MockConfigDb
21+
swsscommon.ConfigDBConnector = MockConfigDb
2222
test_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
2323
modules_path = os.path.dirname(test_path)
2424
scripts_path = os.path.join(modules_path, "scripts")
@@ -48,4 +48,9 @@ def test_caclmgrd_bfd(self, test_name, test_data, fs):
4848
caclmgrd_daemon = self.caclmgrd.ControlPlaneAclManager("caclmgrd")
4949
caclmgrd_daemon.allow_bfd_protocol('')
5050
mocked_subprocess.Popen.assert_has_calls(test_data["expected_subprocess_calls"], any_order=True)
51+
caclmgrd_daemon.bfdAllowed = True
52+
mocked_subprocess.Popen.reset_mock()
53+
caclmgrd_daemon.num_changes[''] = 1
54+
caclmgrd_daemon.check_and_update_control_plane_acls('', 1)
55+
mocked_subprocess.Popen.assert_has_calls(test_data["expected_subprocess_calls"], any_order=True)
5156

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import os
2+
import sys
3+
4+
from swsscommon import swsscommon
5+
from parameterized import parameterized
6+
from sonic_py_common.general import load_module_from_source
7+
from unittest import TestCase, mock
8+
from pyfakefs.fake_filesystem_unittest import patchfs
9+
10+
from .test_default_rule_vectors import CACLMGRD_DEFAULT_RULE_TEST_VECTOR
11+
from tests.common.mock_configdb import MockConfigDb
12+
13+
DBCONFIG_PATH = '/var/run/redis/sonic-db/database_config.json'
14+
15+
16+
class TestCaclmgrdDefaultRule(TestCase):
17+
"""
18+
Test caclmgrd default deny rule
19+
1. Default deny rule NOT EXISTS when there is no CACL rule.
20+
2. Default deny rule EXISTS when there is at least one CACL rules.
21+
"""
22+
def setUp(self):
23+
swsscommon.ConfigDBConnector = MockConfigDb
24+
test_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
25+
modules_path = os.path.dirname(test_path)
26+
scripts_path = os.path.join(modules_path, "scripts")
27+
sys.path.insert(0, modules_path)
28+
caclmgrd_path = os.path.join(scripts_path, 'caclmgrd')
29+
self.caclmgrd = load_module_from_source('caclmgrd', caclmgrd_path)
30+
self.maxDiff = None
31+
32+
self.default_deny_rule_v4 = ('iptables', '-A', 'INPUT', '-j', 'DROP')
33+
self.default_deny_rule_v6 = ('ip6tables', '-A', 'INPUT', '-j', 'DROP')
34+
35+
@parameterized.expand(CACLMGRD_DEFAULT_RULE_TEST_VECTOR)
36+
@patchfs
37+
def test_caclmgrd_default_rule(self, test_name, test_data, fs):
38+
if not os.path.exists(DBCONFIG_PATH):
39+
fs.create_file(DBCONFIG_PATH) # fake database_config.json
40+
41+
MockConfigDb.set_config_db(test_data["config_db"])
42+
self.caclmgrd.ControlPlaneAclManager.get_namespace_mgmt_ip = mock.MagicMock()
43+
self.caclmgrd.ControlPlaneAclManager.get_namespace_mgmt_ipv6 = mock.MagicMock()
44+
self.caclmgrd.ControlPlaneAclManager.generate_block_ip2me_traffic_iptables_commands = mock.MagicMock(return_value=[])
45+
self.caclmgrd.ControlPlaneAclManager.get_chain_list = mock.MagicMock(return_value=["INPUT", "FORWARD", "OUTPUT"])
46+
self.caclmgrd.ControlPlaneAclManager.get_chassis_midplane_interface_ip = mock.MagicMock(return_value='')
47+
caclmgrd_daemon = self.caclmgrd.ControlPlaneAclManager("caclmgrd")
48+
49+
iptables_rules_ret, _ = caclmgrd_daemon.get_acl_rules_and_translate_to_iptables_commands('', MockConfigDb())
50+
iptables_rules_ret = [tuple(i) for i in iptables_rules_ret]
51+
if test_data['default_deny']:
52+
self.assertIn(self.default_deny_rule_v4, iptables_rules_ret)
53+
self.assertIn(self.default_deny_rule_v6, iptables_rules_ret)
54+
else:
55+
self.assertNotIn(self.default_deny_rule_v4, iptables_rules_ret)
56+
self.assertNotIn(self.default_deny_rule_v6, iptables_rules_ret)

tests/caclmgrd/caclmgrd_vxlan_test.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,9 @@ def test_caclmgrd_vxlan(self, test_name, test_data, fs):
5555
mocked_subprocess.Popen.assert_has_calls(test_data["expected_add_subprocess_calls"], any_order=True)
5656
caclmgrd_daemon.block_vxlan_port('')
5757
mocked_subprocess.Popen.assert_has_calls(test_data["expected_del_subprocess_calls"], any_order=True)
58+
caclmgrd_daemon.allow_vxlan_port('', data)
59+
mocked_subprocess.Popen.reset_mock()
60+
caclmgrd_daemon.num_changes[''] = 1
61+
caclmgrd_daemon.check_and_update_control_plane_acls('', 1)
62+
mocked_subprocess.Popen.assert_has_calls(test_data["expected_add_subprocess_calls"], any_order=True)
5863

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
from unittest.mock import call
2+
import subprocess
3+
4+
"""
5+
caclmgrd default rule test vector
6+
"""
7+
CACLMGRD_DEFAULT_RULE_TEST_VECTOR = [
8+
(
9+
"No CACL rules -> Default deny rule NOT EXISTS",
10+
{
11+
"config_db": {
12+
"ACL_RULE": {},
13+
"ACL_TABLE": {
14+
"SSH_ONLY": {
15+
"policy_desc": "SSH_ONLY",
16+
"services": [
17+
"SSH"
18+
],
19+
"stage": "ingress",
20+
"type": "CTRLPLANE"
21+
}
22+
},
23+
"DEVICE_METADATA": {
24+
"localhost": {
25+
}
26+
},
27+
"FEATURE": {},
28+
},
29+
"return": [],
30+
"default_deny": False,
31+
}
32+
),
33+
(
34+
"At least one CACL rules -> Default deny rule EXISTS",
35+
{
36+
"config_db": {
37+
"ACL_RULE": {
38+
"SSH_ONLY|RULE_22": {
39+
"PACKET_ACTION": "ACCEPT",
40+
"PRIORITY": "9978",
41+
"SRC_IPV6": "2001::23/128"
42+
},
43+
},
44+
"ACL_TABLE": {
45+
"SSH_ONLY": {
46+
"policy_desc": "SSH_ONLY",
47+
"services": [
48+
"SSH"
49+
],
50+
"stage": "ingress",
51+
"type": "CTRLPLANE"
52+
}
53+
},
54+
"DEVICE_METADATA": {
55+
"localhost": {
56+
}
57+
},
58+
"FEATURE": {},
59+
},
60+
"return": [
61+
['ip6tables', '-A', 'INPUT', '-p', 'tcp', '-s', '2001::23/128', '--dport', '22', '-j', 'ACCEPT'],
62+
['iptables', '-A', 'INPUT', '-j', 'DROP'],
63+
['ip6tables', '-A', 'INPUT', '-j', 'DROP'],
64+
],
65+
"default_deny": True,
66+
}
67+
),
68+
]

tests/common/mock_configdb.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,17 @@ def set_entry(self, key, field, data):
5151

5252
def get_table(self, table_name):
5353
data = {}
54-
for k, v in MockConfigDb.CONFIG_DB[table_name].items():
55-
data[self.deserialize_key(k)] = v
54+
if table_name in MockConfigDb.CONFIG_DB:
55+
for k, v in MockConfigDb.CONFIG_DB[table_name].items():
56+
data[self.deserialize_key(k)] = v
5657
return data
5758

5859
def subscribe(self, table_name, callback):
5960
self.handlers[table_name] = callback
6061

62+
def publish(self, table_name, key, op, data):
63+
self.handlers[table_name](key, op, data)
64+
6165
def listen(self, init_data_handler=None):
6266
for e in MockConfigDb.event_queue:
6367
self.handlers[e[0]](e[0], e[1], self.get_entry(e[0], e[1]))

tests/hostcfgd/hostcfgd_tacacs_test.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,3 +192,43 @@ def test_hostcfgd_sshd_not_empty(self, test_name, test_data):
192192
]
193193
mocked_syslog.assert_has_calls(expected)
194194

195+
@parameterized.expand(HOSTCFGD_TEST_TACACS_VECTOR)
196+
def test_hostcfgd_delete_config_table(self, test_name, test_data):
197+
"""
198+
Test hostcfd delete config table check
199+
200+
Args:
201+
test_name(str): test name
202+
test_data(dict): test data which contains initial Config Db tables, and expected results
203+
204+
Returns:
205+
None
206+
"""
207+
config_name = "config_db_local"
208+
op_path = output_path + "/" + test_name + "_" + config_name
209+
sop_path = sample_output_path + "/" + test_name + "_" + config_name
210+
host_config_daemon = self.mock_hostcfgd(test_data, config_name, op_path, sop_path)
211+
host_config_daemon.register_callbacks()
212+
213+
# render with delete config table
214+
original_syslog = hostcfgd.syslog
215+
with mock.patch('hostcfgd.syslog.syslog') as mocked_syslog:
216+
mocked_syslog.LOG_INFO = original_syslog.LOG_INFO
217+
mocked_syslog.LOG_ERR = original_syslog.LOG_ERR
218+
219+
# simulate subscribe callback
220+
try:
221+
host_config_daemon.__dict__['config_db'].publish('AAA', 'authorization', 'DEL', None)
222+
except TypeError as e:
223+
assert False
224+
225+
# check sys log
226+
expected = [
227+
mock.call(mocked_syslog.LOG_INFO, "file size check pass: {} size is (2139) bytes".format(hostcfgd.ETC_PAMD_SSHD)),
228+
mock.call(mocked_syslog.LOG_INFO, "file size check pass: {} size is (4951) bytes".format(hostcfgd.ETC_PAMD_LOGIN)),
229+
mock.call(mocked_syslog.LOG_INFO, "Found audisp-tacplus PID: "),
230+
mock.call(mocked_syslog.LOG_INFO, "cmd - ['service', 'aaastatsd', 'stop']"),
231+
mock.call(mocked_syslog.LOG_ERR, "['service', 'aaastatsd', 'stop'] - failed: return code - 1, output:\nNone"),
232+
mock.call(mocked_syslog.LOG_INFO, "AAA Update: key: DEL, op: DEL, data: {}")
233+
]
234+
mocked_syslog.assert_has_calls(expected)

0 commit comments

Comments
 (0)