2021-04-27 18:44:13 -05:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
2022-08-09 16:21:29 -05:00
|
|
|
import argparse
|
2021-04-27 18:44:13 -05:00
|
|
|
import time
|
|
|
|
|
2021-05-06 13:36:13 -05:00
|
|
|
from sonic_py_common import logger as log
|
2022-06-29 23:34:02 -05:00
|
|
|
from swsscommon.swsscommon import ConfigDBConnector, DBConnector, FieldValuePairs, ProducerStateTable, SonicV2Connector, Table
|
|
|
|
from swsscommon.swsscommon import APPL_DB, STATE_DB
|
2021-04-27 18:44:13 -05:00
|
|
|
|
2021-05-06 13:36:13 -05:00
|
|
|
logger = log.Logger('write_standby')
|
2021-04-27 18:44:13 -05:00
|
|
|
|
|
|
|
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
|
|
|
|
"""
|
|
|
|
|
2022-09-02 15:50:42 -05:00
|
|
|
def __init__(self, activeactive, activestandby, shutdown_module):
|
2021-04-27 18:44:13 -05:00
|
|
|
self.config_db_connector = None
|
|
|
|
self.appl_db_connector = None
|
2022-06-29 23:34:02 -05:00
|
|
|
self.state_db_connector = None
|
2021-04-27 18:44:13 -05:00
|
|
|
self.asic_db_connector = None
|
2022-08-09 16:21:29 -05:00
|
|
|
self.default_active_active_state = activeactive
|
|
|
|
self.default_active_standby_state = activestandby
|
2022-09-02 15:50:42 -05:00
|
|
|
self.shutdown_module = shutdown_module
|
|
|
|
self.is_shutdwon = (self.shutdown_module != None)
|
2021-04-27 18:44:13 -05:00
|
|
|
|
|
|
|
@property
|
|
|
|
def config_db(self):
|
|
|
|
"""
|
|
|
|
Returns config DB connector.
|
|
|
|
Initializes the connector during the first call
|
|
|
|
"""
|
|
|
|
if self.config_db_connector is None:
|
2023-04-06 15:54:56 -05:00
|
|
|
self.config_db_connector = ConfigDBConnector(use_unix_socket_path=True)
|
2021-04-27 18:44:13 -05:00
|
|
|
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
|
2022-06-29 23:34:02 -05:00
|
|
|
|
|
|
|
@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
|
2021-04-27 18:44:13 -05:00
|
|
|
|
|
|
|
@property
|
|
|
|
def asic_db(self):
|
|
|
|
"""
|
|
|
|
Returns the ASIC DB connector.
|
|
|
|
Initializes the connector during the first call
|
|
|
|
"""
|
|
|
|
if self.asic_db_connector is None:
|
2023-04-06 15:54:56 -05:00
|
|
|
self.asic_db_connector = SonicV2Connector(use_unix_socket_path=True)
|
2021-04-27 18:44:13 -05:00
|
|
|
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]
|
|
|
|
|
2021-06-03 19:24:54 -05:00
|
|
|
@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()
|
|
|
|
|
2022-06-29 23:34:02 -05:00
|
|
|
@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')
|
|
|
|
|
2022-09-02 15:50:42 -05:00
|
|
|
if status and value == 'true':
|
|
|
|
return True
|
|
|
|
|
|
|
|
if self.shutdown_module:
|
|
|
|
(status, value) = tbl.hget(self.shutdown_module, 'enable')
|
|
|
|
if status and value == 'true':
|
|
|
|
return True
|
|
|
|
|
|
|
|
return False
|
2022-06-29 23:34:02 -05:00
|
|
|
|
2022-08-09 16:21:29 -05:00
|
|
|
def get_all_mux_intfs_modes(self):
|
2021-04-27 18:44:13 -05:00
|
|
|
"""
|
2022-08-09 16:21:29 -05:00
|
|
|
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.
|
2021-04-27 18:44:13 -05:00
|
|
|
"""
|
2022-08-09 16:21:29 -05:00
|
|
|
intf_modes = {}
|
2021-06-18 12:19:09 -05:00
|
|
|
all_intfs = self.config_db.get_table('MUX_CABLE')
|
2022-08-09 16:21:29 -05:00
|
|
|
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
|
2021-04-27 18:44:13 -05:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2023-04-06 22:30:58 -05:00
|
|
|
def wait_for_tunnel(self, interval=1, timeout=90):
|
2021-04-27 18:44:13 -05:00
|
|
|
"""
|
|
|
|
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
|
|
|
|
"""
|
2021-05-06 13:36:13 -05:00
|
|
|
logger.log_info("Waiting for tunnel {} with timeout {} seconds".format(self.tunnel_name, timeout))
|
2021-04-27 18:44:13 -05:00
|
|
|
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
|
|
|
|
"""
|
2021-06-03 19:24:54 -05:00
|
|
|
if not self.is_dualtor:
|
|
|
|
# If not running on a dual ToR system, take no action
|
|
|
|
return
|
2022-06-29 23:34:02 -05:00
|
|
|
|
2022-09-02 15:50:42 -05:00
|
|
|
if self.is_warmrestart and self.is_shutdwon:
|
2022-06-29 23:34:02 -05:00
|
|
|
# If in warmrestart context, take no action
|
|
|
|
logger.log_warning("Skip setting mux state due to ongoing warmrestart.")
|
|
|
|
return
|
|
|
|
|
2022-08-09 16:21:29 -05:00
|
|
|
modes = self.get_all_mux_intfs_modes()
|
2021-04-27 18:44:13 -05:00
|
|
|
if self.wait_for_tunnel():
|
2022-08-09 16:21:29 -05:00
|
|
|
logger.log_warning("Applying state to interfaces {}".format(modes))
|
2021-04-27 18:44:13 -05:00
|
|
|
producer_state_table = ProducerStateTable(self.appl_db, 'MUX_CABLE_TABLE')
|
|
|
|
|
2022-08-09 16:21:29 -05:00
|
|
|
for intf, state in modes.items():
|
|
|
|
fvs = create_fvs(state=state)
|
2021-04-27 18:44:13 -05:00
|
|
|
producer_state_table.set(intf, fvs)
|
|
|
|
else:
|
2021-05-06 13:36:13 -05:00
|
|
|
logger.log_error("Timed out waiting for tunnel {}, mux state will not be written".format(self.tunnel_name))
|
2021-04-27 18:44:13 -05:00
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
2022-08-09 16:21:29 -05:00
|
|
|
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')
|
2022-09-02 15:50:42 -05:00
|
|
|
parser.add_argument('--shutdown', help='write mux state after shutdown other services, supported: mux, bgp',
|
|
|
|
type=str, required=False, choices=['mux', 'bgp'], default=None)
|
2022-08-09 16:21:29 -05:00
|
|
|
args = parser.parse_args()
|
2022-08-31 15:10:22 -05:00
|
|
|
active_active_state = args.active_active
|
|
|
|
active_standby_state = args.active_standby
|
2022-09-02 15:50:42 -05:00
|
|
|
if args.shutdown in ['mux', 'bgp']:
|
2022-08-31 15:10:22 -05:00
|
|
|
active_active_state = "standby"
|
2022-09-02 15:50:42 -05:00
|
|
|
mux_writer = MuxStateWriter(activeactive=active_active_state, activestandby=active_standby_state, shutdown_module=args.shutdown)
|
2021-04-27 18:44:13 -05:00
|
|
|
mux_writer.apply_mux_config()
|