-
Notifications
You must be signed in to change notification settings - Fork 1.5k
/
Copy pathwrite_standby.py
executable file
·183 lines (152 loc) · 6.46 KB
/
write_standby.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
#!/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()