Skip to content
Closed
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
150 changes: 146 additions & 4 deletions src/sonic-host-services/scripts/hostcfgd
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import signal
import jinja2
from sonic_py_common import device_info
from swsscommon.swsscommon import ConfigDBConnector, DBConnector, Table
from swsscommon import swsscommon

# FILE
PAM_AUTH_CONF = "/etc/pam.d/common-auth-sonic"
Expand Down Expand Up @@ -1046,6 +1047,125 @@ class PamLimitsCfg(object):
"modify pam_limits config file failed with exception: {}"
.format(e))

class DeviceMetaCfg(object):
"""
DeviceMetaCfg Config Daemon
Handles changes in DEVICE_METADATA table.
1) Handle hostname change
"""

def __init__(self):
self.hostname = ''

def hostname_update(self, data):
"""
Apply hostname handler.

Args:
data: Read table's key's data.
"""
syslog.syslog(syslog.LOG_DEBUG, 'DeviceMetaCfg: hostname update')
new_hostname = data.get('hostname')

# Restart hostname-config service when hostname was changed.
# Empty not allowed
if new_hostname and new_hostname != self.hostname:
syslog.syslog(syslog.LOG_INFO, 'DeviceMetaCfg: Set new hostname: {}'
.format(new_hostname))
self.hostname = new_hostname
try:
run_cmd('sudo service hostname-config restart', True, True)
except Exception as e:
syslog.syslog(syslog.LOG_ERR, 'DeviceMetaCfg: Failed to set new'
' hostname: {}'.format(e))
return

run_cmd('sudo monit reload')


class MgmtIfaceCfg(object):
"""
MgmtIfaceCfg Config Daemon
Handles changes in MGMT_INTERFACE, MGMT_VRF_CONFIG tables.
1) Handle change of interface ip
2) Handle change of management VRF state
"""

def __init__(self):
self.iface_config_data = {}
self.mgmt_vrf_enabled = ''

def update_mgmt_iface(self, iface, key, data):
"""Handle update management interface config
"""
syslog.syslog(syslog.LOG_DEBUG, 'MgmtIfaceCfg: mgmt iface update')

# Restart management interface service when config was changed
if data != self.iface_config_data.get(key):
cfg = {key: data}
syslog.syslog(syslog.LOG_INFO, f'MgmtIfaceCfg: Set new interface '
f'config {cfg} for {iface}')
try:
run_cmd('sudo systemctl restart interfaces-config', True, True)
run_cmd('sudo systemctl restart ntp-config', True, True)
except Exception:
syslog.syslog(syslog.LOG_ERR, f'Failed to restart management '
'interface services')
return

self.iface_config_data[key] = data

def update_mgmt_vrf(self, data):
"""Handle update management VRF state
"""
syslog.syslog(syslog.LOG_DEBUG, 'MgmtIfaceCfg: mgmt vrf state update')

# Restart mgmt vrf services when mgmt vrf config was changed.
# Empty not allowed.
enabled = data.get('mgmtVrfEnabled', '')
if not enabled or enabled == self.mgmt_vrf_enabled:
return

syslog.syslog(syslog.LOG_INFO, f'Set mgmt vrf state {enabled}')

# Restart related vrfs services
try:
run_cmd('service ntp stop', True, True)
run_cmd('systemctl restart interfaces-config', True, True)
run_cmd('service ntp start', True, True)
except Exception:
syslog.syslog(syslog.LOG_ERR, f'Failed to restart management vrf '
'services')
return

# Remove mgmt if route
if enabled == 'true':
"""
The regular expression for grep in below cmd is to match eth0 line
in /proc/net/route, sample file:
$ cat /proc/net/route
Iface Destination Gateway Flags RefCnt Use
eth0 00000000 01803B0A 0003 0 0
#################### Line break here ####################
Metric Mask MTU Window IRTT
202 00000000 0 0 0
"""
try:
run_cmd(r"""cat /proc/net/route | grep -E \"eth0\s+"""
r"""00000000\s+[0-9A-Z]+\s+[0-9]+\s+[0-9]+\s+[0-9]+"""
r"""\s+202\" | wc -l""",
True, True)
except Exception:
syslog.syslog(syslog.LOG_ERR, 'MgmtIfaceCfg: Could not delete '
'eth0 route')
return

run_cmd("ip -4 route del default dev eth0 metric 202", False)

# Update cache
self.mgmt_vrf_enabled = enabled


class HostConfigDaemon:
def __init__(self):
# Just a sanity check to verify if the CONFIG_DB has been initialized
Expand Down Expand Up @@ -1084,6 +1204,12 @@ class HostConfigDaemon:
self.pamLimitsCfg = PamLimitsCfg(self.config_db)
self.pamLimitsCfg.update_config_file()

# Initialize DeviceMetaCfg
self.devmetacfg = DeviceMetaCfg()

# Initialize MgmtIfaceCfg
self.mgmtifacecfg = MgmtIfaceCfg()

def load(self, init_data):
features = init_data['FEATURE']
aaa = init_data['AAA']
Expand All @@ -1102,10 +1228,10 @@ class HostConfigDaemon:
self.ntpcfg.load(ntp_global, ntp_server)
self.kdumpCfg.load(kdump)

dev_meta = self.config_db.get_table('DEVICE_METADATA')
if 'localhost' in dev_meta:
if 'hostname' in dev_meta['localhost']:
self.hostname_cache = dev_meta['localhost']['hostname']
# Get hostname
self.hostname_cache = self.config_db.get_table(
swsscommon.CFG_DEVICE_METADATA_TABLE_NAME
).get('localhost', {}).get('hostname', '')

# Update AAA with the hostname
self.aaacfg.hostname_update(self.hostname_cache)
Expand Down Expand Up @@ -1154,6 +1280,10 @@ class HostConfigDaemon:
mgmt_intf_name = self.__get_intf_name(key)
self.aaacfg.handle_radius_source_intf_ip_chg(mgmt_intf_name)
self.aaacfg.handle_radius_nas_ip_chg(mgmt_intf_name)
self.mgmtifacecfg.update_mgmt_iface(mgmt_intf_name, key, data)

def mgmt_vrf_handler(self, key, op, data):
self.mgmtifacecfg.update_mgmt_vrf(data)

def lpbk_handler(self, key, op, data):
key = ConfigDBConnector.deserialize_key(key)
Expand Down Expand Up @@ -1193,6 +1323,10 @@ class HostConfigDaemon:
syslog.syslog(syslog.LOG_INFO, 'Kdump handler...')
self.kdumpCfg.kdump_update(key, data)

def device_metadata_handler(self, key, op, data):
syslog.syslog(syslog.LOG_INFO, 'DeviceMeta handler...')
self.devmetacfg.hostname_update(data)

def wait_till_system_init_done(self):
# No need to print the output in the log file so using the "--quiet"
# flag
Expand Down Expand Up @@ -1231,6 +1365,14 @@ class HostConfigDaemon:
self.config_db.subscribe('PORTCHANNEL_INTERFACE', make_callback(self.portchannel_intf_handler))
self.config_db.subscribe('INTERFACE', make_callback(self.phy_intf_handler))

# Handle DEVICE_MEATADATA changes
self.config_db.subscribe(swsscommon.CFG_DEVICE_METADATA_TABLE_NAME,
make_callback(self.device_metadata_handler))

# Handle MGMT_VRF_CONFIG changes
self.config_db.subscribe(swsscommon.CFG_MGMT_VRF_CONFIG_TABLE_NAME,
make_callback(self.mgmt_vrf_handler))

syslog.syslog(syslog.LOG_INFO,
"Waiting for systemctl to finish initialization")
self.wait_till_system_init_done()
Expand Down
66 changes: 66 additions & 0 deletions src/sonic-host-services/tests/hostcfgd/hostcfgd_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -307,3 +307,69 @@ def test_kdump_event(self):
call('sonic-kdump-config --num_dumps 3', shell=True),
call('sonic-kdump-config --memory 0M-2G:256M,2G-4G:320M,4G-8G:384M,8G-:448M', shell=True)]
mocked_subprocess.check_call.assert_has_calls(expected, any_order=True)

def test_devicemeta_event(self):
"""
Test handling DEVICE_METADATA events.
1) Hostname reload
"""
MockConfigDb.set_config_db(HOSTCFG_DAEMON_CFG_DB)
MockConfigDb.event_queue = [(swsscommon.CFG_DEVICE_METADATA_TABLE_NAME,
'localhost')]
daemon = hostcfgd.HostConfigDaemon()
daemon.register_callbacks()
with mock.patch('hostcfgd.subprocess') as mocked_subprocess:
popen_mock = mock.Mock()
attrs = {'communicate.return_value': ('output', 'error')}
popen_mock.configure_mock(**attrs)
mocked_subprocess.Popen.return_value = popen_mock

try:
daemon.start()
except TimeoutError:
pass

expected = [
call('sudo service hostname-config restart', shell=True),
call('sudo monit reload', shell=True)
]
mocked_subprocess.check_call.assert_has_calls(expected,
any_order=True)

def test_mgmtiface_event(self):
"""
Test handling mgmt events.
1) Management interface setup
2) Management vrf setup
"""
MockConfigDb.set_config_db(HOSTCFG_DAEMON_CFG_DB)
MockConfigDb.event_queue = [
(swsscommon.CFG_MGMT_INTERFACE_TABLE_NAME, 'eth0|1.2.3.4/24'),
(swsscommon.CFG_MGMT_VRF_CONFIG_TABLE_NAME, 'vrf_global')
]
daemon = hostcfgd.HostConfigDaemon()
daemon.register_callbacks()
with mock.patch('hostcfgd.subprocess') as mocked_subprocess:
popen_mock = mock.Mock()
attrs = {'communicate.return_value': ('output', 'error')}
popen_mock.configure_mock(**attrs)
mocked_subprocess.Popen.return_value = popen_mock

try:
daemon.start()
except TimeoutError:
pass

expected = [
call('sudo systemctl restart interfaces-config', shell=True),
call('sudo systemctl restart ntp-config', shell=True),
call('service ntp stop', shell=True),
call('systemctl restart interfaces-config', shell=True),
call('service ntp start', shell=True),
call('cat /proc/net/route | grep -E \\"eth0\\s+00000000'
'\\s+[0-9A-Z]+\\s+[0-9]+\\s+[0-9]+\\s+[0-9]+\\s+202\\" | '
'wc -l', shell=True),
call('ip -4 route del default dev eth0 metric 202', shell=True)
]
mocked_subprocess.check_call.assert_has_calls(expected,
any_order=True)
19 changes: 14 additions & 5 deletions src/sonic-host-services/tests/hostcfgd/test_vectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"enabled": "false",
"num_dumps": "3",
"memory": "0M-2G:256M,2G-4G:320M,4G-8G:384M,8G-:448M"
}
}
},
"FEATURE": {
"dhcp_relay": {
Expand Down Expand Up @@ -115,7 +115,7 @@
"enabled": "false",
"num_dumps": "3",
"memory": "0M-2G:256M,2G-4G:320M,4G-8G:384M,8G-:448M"
}
}
},
"FEATURE": {
"dhcp_relay": {
Expand Down Expand Up @@ -229,7 +229,7 @@
"enabled": "false",
"num_dumps": "3",
"memory": "0M-2G:256M,2G-4G:320M,4G-8G:384M,8G-:448M"
}
}
},
"FEATURE": {
"dhcp_relay": {
Expand Down Expand Up @@ -322,7 +322,7 @@
"enabled": "false",
"num_dumps": "3",
"memory": "0M-2G:256M,2G-4G:320M,4G-8G:384M,8G-:448M"
}
}
},
"FEATURE": {
"dhcp_relay": {
Expand Down Expand Up @@ -419,7 +419,7 @@
"enabled": "false",
"num_dumps": "3",
"memory": "0M-2G:256M,2G-4G:320M,4G-8G:384M,8G-:448M"
}
}
},
"FEATURE": {
"dhcp_relay": {
Expand Down Expand Up @@ -548,6 +548,15 @@
"localhost": {
"subtype": "DualToR",
"type": "ToRRouter",
"hostname": "SomeNewHostname"
}
},
"MGMT_INTERFACE": {
"eth0|1.2.3.4/24": {}
},
"MGMT_VRF_CONFIG": {
"vrf_global": {
'mgmtVrfEnabled': 'true'
}
}
}