sonic-buildimage/files/scripts/write_standby.py
Ying Xie 4ab83170a5 [write_standby] update write_standby.py script (#11650)
Why I did it
The initial value has to be present for the state machines to work. In active-standby dual-tor scenario, or any hardware mux scenario, the value will be updtaed eventually with a delay.

However, in active-active dual-tor scenario, there is no other mechanism to initialize the value and get state machines started.
So this script will have to write something at start up time.

For active-active dualtor, 'active' is a more preferred initial value, the state machine will switch the state to standby soon if
link prober found link not in good state.

How I did it
Update the script to always provide initial values.

How to verify it
Tested on active-active dual-tor testbed.

Signed-off-by: Ying Xie ying.xie@microsoft.com
2022-09-01 23:57:23 +00:00

184 lines
6.5 KiB
Python
Executable File

#!/usr/bin/env python3
import argparse
import time
from sonic_py_common import logger as log
from swsscommon.swsscommon import ConfigDBConnector, DBConnector, FieldValuePairs, ProducerStateTable, SonicV2Connector, Table
from swsscommon.swsscommon import APPL_DB, STATE_DB
logger = log.Logger('write_standby')
REDIS_SOCK_PATH = '/var/run/redis/redis.sock'
def create_fvs(**kwargs):
return FieldValuePairs(list(kwargs.items()))
class MuxStateWriter(object):
"""
Class used to write standby mux state to APP DB
"""
def __init__(self, activeactive, activestandby):
self.config_db_connector = None
self.appl_db_connector = None
self.state_db_connector = None
self.asic_db_connector = None
self.default_active_active_state = activeactive
self.default_active_standby_state = activestandby
@property
def config_db(self):
"""
Returns config DB connector.
Initializes the connector during the first call
"""
if self.config_db_connector is None:
self.config_db_connector = ConfigDBConnector()
self.config_db_connector.connect()
return self.config_db_connector
@property
def appl_db(self):
"""
Returns the app DB connector.
Initializes the connector during the first call
"""
if self.appl_db_connector is None:
self.appl_db_connector = DBConnector(APPL_DB, REDIS_SOCK_PATH, True)
return self.appl_db_connector
@property
def state_db(self):
"""
Returns the state DB connector.
Intializes the connector during the first call
"""
if self.state_db_connector is None:
self.state_db_connector = DBConnector(STATE_DB, REDIS_SOCK_PATH, True)
return self.state_db_connector
@property
def asic_db(self):
"""
Returns the ASIC DB connector.
Initializes the connector during the first call
"""
if self.asic_db_connector is None:
self.asic_db_connector = SonicV2Connector()
self.asic_db_connector.connect('ASIC_DB')
return self.asic_db_connector
@property
def tunnel_name(self):
"""
Returns the name of the IP-in-IP tunnel used for Dual ToR devices
"""
return self.config_db.get_keys('TUNNEL')[0]
@property
def is_dualtor(self):
"""
Checks if script is running on a dual ToR system
"""
localhost_key = self.config_db.get_keys('DEVICE_METADATA')[0]
metadata = self.config_db.get_entry('DEVICE_METADATA', localhost_key)
return 'subtype' in metadata and 'dualtor' in metadata['subtype'].lower()
@property
def is_warmrestart(self):
"""
Checks if a warmrestart is going on
"""
tbl = Table(self.state_db, 'WARM_RESTART_ENABLE_TABLE')
(status, value) = tbl.hget('system', 'enable')
return status and value == 'true'
def get_all_mux_intfs_modes(self):
"""
Returns a list of all mux cable interfaces, with suggested modes
Setting mux initial modes is crucial to kick off the statemachines,
have to set the modes for all mux/gRPC ports.
"""
intf_modes = {}
all_intfs = self.config_db.get_table('MUX_CABLE')
for intf, status in all_intfs.items():
state = status['state'].lower()
if state in ['active', 'standby']:
intf_modes[intf] = state
elif state in ['auto', 'manual']:
if ('soc_ipv4' in status or 'soc_ipv6' in status or
('cable_type' in status and status['cable_type'] == 'active-active')):
intf_modes[intf] = self.default_active_active_state
else:
intf_modes[intf] = self.default_active_standby_state
return intf_modes
def tunnel_exists(self):
"""
Checks if the IP-in-IP tunnel has been written to ASIC DB
"""
tunnel_key_pattern = 'ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL:*'
return len(self.asic_db.keys('ASIC_DB', tunnel_key_pattern)) > 0
def wait_for_tunnel(self, interval=1, timeout=60):
"""
Waits until the IP-in-IP tunnel has been created
Returns:
(bool) True if the tunnel has been created
False if the timeout period is exceeded
"""
logger.log_info("Waiting for tunnel {} with timeout {} seconds".format(self.tunnel_name, timeout))
start = time.time()
curr_time = time.time()
while not self.tunnel_exists() and curr_time - start < timeout:
time.sleep(interval)
curr_time = time.time()
# If we timed out, return False else return True
return curr_time - start < timeout
def apply_mux_config(self):
"""
Writes standby mux state to APP DB for all mux interfaces
"""
if not self.is_dualtor:
# If not running on a dual ToR system, take no action
return
if self.is_warmrestart:
# If in warmrestart context, take no action
logger.log_warning("Skip setting mux state due to ongoing warmrestart.")
return
modes = self.get_all_mux_intfs_modes()
if self.wait_for_tunnel():
logger.log_warning("Applying state to interfaces {}".format(modes))
producer_state_table = ProducerStateTable(self.appl_db, 'MUX_CABLE_TABLE')
for intf, state in modes.items():
fvs = create_fvs(state=state)
producer_state_table.set(intf, fvs)
else:
logger.log_error("Timed out waiting for tunnel {}, mux state will not be written".format(self.tunnel_name))
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Write initial mux state')
parser.add_argument('-a', '--active_active',
help='state: intial state for "auto" and/or "manual" config in active-active mode, default "active"',
type=str, required=False, default='active')
parser.add_argument('-s', '--active_standby',
help='state: intial state for "auto" and/or "manual" config in active-standby mode, default "standby"',
type=str, required=False, default='standby')
args = parser.parse_args()
mux_writer = MuxStateWriter(activeactive=args.active_active, activestandby=args.active_standby)
mux_writer.apply_mux_config()