1
+ import re
2
+ import jinja2
3
+
1
4
from .manager import Manager
2
5
from .log import log_err , log_debug , log_notice
3
- import re
4
6
from swsscommon import swsscommon
5
7
6
8
class DeviceGlobalCfgMgr (Manager ):
7
9
"""This class responds to change in device-specific state"""
8
10
11
+ TSA_DEFAULTS = "false"
12
+ WCMP_DEFAULTS = "false"
13
+ IDF_DEFAULTS = "unisolated"
14
+
9
15
def __init__ (self , common_objs , db , table ):
10
16
"""
11
17
Initialize the object
@@ -19,6 +25,7 @@ def __init__(self, common_objs, db, table):
19
25
self .constants = common_objs ['constants' ]
20
26
self .tsa_template = common_objs ['tf' ].from_file ("bgpd/tsa/bgpd.tsa.isolate.conf.j2" )
21
27
self .tsb_template = common_objs ['tf' ].from_file ("bgpd/tsa/bgpd.tsa.unisolate.conf.j2" )
28
+ self .wcmp_template = common_objs ['tf' ].from_file ("bgpd/wcmp/bgpd.wcmp.conf.j2" )
22
29
self .idf_isolate_template = common_objs ['tf' ].from_file ("bgpd/idf_isolate/idf_isolate.conf.j2" )
23
30
self .idf_unisolate_template = common_objs ['tf' ].from_file ("bgpd/idf_isolate/idf_unisolate.conf.j2" )
24
31
self .directory .subscribe ([("CONFIG_DB" , swsscommon .CFG_DEVICE_METADATA_TABLE_NAME , "localhost/switch_type" ),], self .on_switch_type_change )
@@ -29,49 +36,130 @@ def __init__(self, common_objs, db, table):
29
36
table ,
30
37
)
31
38
39
+ # By default TSA feature is disabled
40
+ if not self .directory .path_exist (self .db_name , self .table_name , "tsa_enabled" ):
41
+ self .directory .put (self .db_name , self .table_name , "tsa_enabled" , self .TSA_DEFAULTS )
42
+ # By default W-ECMP feature is disabled
43
+ if not self .directory .path_exist (self .db_name , self .table_name , "wcmp_enabled" ):
44
+ self .directory .put (self .db_name , self .table_name , "wcmp_enabled" , self .WCMP_DEFAULTS )
45
+ # By default IDF feature is unisolated
46
+ if not self .directory .path_exist (self .db_name , self .table_name , "idf_isolation_state" ):
47
+ self .directory .put (self .db_name , self .table_name , "idf_isolation_state" , self .IDF_DEFAULTS )
48
+
32
49
def on_switch_type_change (self ):
33
50
log_debug ("DeviceGlobalCfgMgr:: Switch type update handler" )
34
51
if self .directory .path_exist ("CONFIG_DB" , swsscommon .CFG_DEVICE_METADATA_TABLE_NAME , "localhost/switch_type" ):
35
52
self .switch_type = self .directory .get_slot ("CONFIG_DB" , swsscommon .CFG_DEVICE_METADATA_TABLE_NAME )["localhost" ]["switch_type" ]
36
53
log_debug ("DeviceGlobalCfgMgr:: Switch type: %s" % self .switch_type )
37
54
38
55
def set_handler (self , key , data ):
56
+ """ Handle device TSA/W-ECMP state change """
39
57
log_debug ("DeviceGlobalCfgMgr:: set handler" )
58
+
40
59
if self .switch_type :
41
60
log_debug ("DeviceGlobalCfgMgr:: Switch type: %s" % self .switch_type )
42
- """ Handle device tsa_enabled state change """
43
61
if not data :
44
62
log_err ("DeviceGlobalCfgMgr:: data is None" )
45
63
return False
46
64
47
- tsa_status = "false"
48
- idf_isolation_state = "unisolated"
65
+ # TSA configuration
66
+ self .configure_tsa (data )
67
+ # W-ECMP configuration
68
+ self .configure_wcmp (data )
69
+ # IDF configuration
70
+ self .configure_idf (data )
49
71
50
- if self .directory .path_exist ("CONFIG_DB" , swsscommon .CFG_BGP_DEVICE_GLOBAL_TABLE_NAME , "tsa_enabled" ):
51
- tsa_status = self .directory .get_slot ("CONFIG_DB" , swsscommon .CFG_BGP_DEVICE_GLOBAL_TABLE_NAME )["tsa_enabled" ]
52
- if self .directory .path_exist ("CONFIG_DB" , swsscommon .CFG_BGP_DEVICE_GLOBAL_TABLE_NAME , "idf_isolation_state" ):
53
- idf_isolation_state = self .directory .get_slot ("CONFIG_DB" , swsscommon .CFG_BGP_DEVICE_GLOBAL_TABLE_NAME )["idf_isolation_state" ]
54
-
55
- if "tsa_enabled" in data :
56
- self .directory .put (self .db_name , self .table_name , "tsa_enabled" , data ["tsa_enabled" ])
57
- if tsa_status != data ["tsa_enabled" ]:
58
- self .cfg_mgr .commit ()
59
- self .cfg_mgr .update ()
60
- self .isolate_unisolate_device (data ["tsa_enabled" ])
61
-
62
-
63
- if "idf_isolation_state" in data :
64
- self .directory .put (self .db_name , self .table_name , "idf_isolation_state" , data ["idf_isolation_state" ])
65
- if idf_isolation_state != data ["idf_isolation_state" ]:
66
- if self .switch_type and self .switch_type != "SpineRouter" :
67
- log_debug ("DeviceGlobalCfgMgr:: Skipping IDF isolation configuration on Switch type: %s" % self .switch_type )
68
- return True
69
- self .downstream_isolate_unisolate (data ["idf_isolation_state" ])
70
-
71
72
return True
72
73
73
74
def del_handler (self , key ):
74
75
log_debug ("DeviceGlobalCfgMgr:: del handler" )
76
+
77
+ # TSA configuration
78
+ self .configure_tsa ()
79
+ # W-ECMP configuration
80
+ self .configure_wcmp ()
81
+ # IDF configuration
82
+ self .configure_idf ()
83
+
84
+ return True
85
+
86
+ def is_update_required (self , key , value ):
87
+ if self .directory .path_exist (self .db_name , self .table_name , key ):
88
+ return value != self .directory .get (self .db_name , self .table_name , key )
89
+ return True
90
+
91
+ def configure_tsa (self , data = None ):
92
+ """ Configure TSA feature"""
93
+
94
+ state = self .TSA_DEFAULTS
95
+
96
+ if data is not None :
97
+ if "tsa_enabled" in data :
98
+ state = data ["tsa_enabled" ]
99
+
100
+ if self .is_update_required ("tsa_enabled" , state ):
101
+ self .cfg_mgr .commit ()
102
+ self .cfg_mgr .update ()
103
+ if self .isolate_unisolate_device (state ):
104
+ self .directory .put (self .db_name , self .table_name , "tsa_enabled" , state )
105
+ else :
106
+ log_notice ("DeviceGlobalCfgMgr:: TSA configuration is up-to-date" )
107
+
108
+ def configure_wcmp (self , data = None ):
109
+ """ Configure W-ECMP feature"""
110
+
111
+ state = self .WCMP_DEFAULTS
112
+
113
+ if data is not None :
114
+ if "wcmp_enabled" in data :
115
+ state = data ["wcmp_enabled" ]
116
+
117
+ if self .is_update_required ("wcmp_enabled" , state ):
118
+ if self .set_wcmp (state ):
119
+ self .directory .put (self .db_name , self .table_name , "wcmp_enabled" , state )
120
+ else :
121
+ log_notice ("DeviceGlobalCfgMgr:: W-ECMP configuration is up-to-date" )
122
+
123
+ def configure_idf (self , data = None ):
124
+ """ Configure IDF feature"""
125
+
126
+ state = self .IDF_DEFAULTS
127
+
128
+ if data is not None :
129
+ if "idf_isolation_state" in data :
130
+ state = data ["idf_isolation_state" ]
131
+
132
+ if self .is_update_required ("idf_isolation_state" , state ):
133
+ if self .downstream_isolate_unisolate (state ):
134
+ self .directory .put (self .db_name , self .table_name , "idf_isolation_state" , state )
135
+ else :
136
+ log_notice ("DeviceGlobalCfgMgr:: IDF configuration is up-to-date" )
137
+
138
+ def set_wcmp (self , status ):
139
+ """ API to set/unset W-ECMP """
140
+
141
+ if status not in ["true" , "false" ]:
142
+ log_err ("W-ECMP: invalid value({}) is provided" .format (status ))
143
+ return False
144
+
145
+ if status == "true" :
146
+ log_notice ("DeviceGlobalCfgMgr:: Enabling W-ECMP..." )
147
+ else :
148
+ log_notice ("DeviceGlobalCfgMgr:: Disabling W-ECMP..." )
149
+
150
+ cmd = "\n "
151
+
152
+ try :
153
+ cmd += self .wcmp_template .render (wcmp_enabled = status )
154
+ except jinja2 .TemplateError as e :
155
+ msg = "W-ECMP: error in template rendering"
156
+ log_err ("%s: %s" % (msg , str (e )))
157
+ return False
158
+
159
+ self .cfg_mgr .push (cmd )
160
+
161
+ log_debug ("DeviceGlobalCfgMgr::Done" )
162
+
75
163
return True
76
164
77
165
def check_state_and_get_tsa_routemaps (self , cfg ):
@@ -87,6 +175,11 @@ def check_state_and_get_tsa_routemaps(self, cfg):
87
175
88
176
def isolate_unisolate_device (self , tsa_status ):
89
177
""" API to get TSA/TSB route-maps and apply configuration"""
178
+
179
+ if tsa_status not in ["true" , "false" ]:
180
+ log_err ("TSA: invalid value({}) is provided" .format (tsa_status ))
181
+ return False
182
+
90
183
cmd = "\n "
91
184
if tsa_status == "true" :
92
185
log_notice ("DeviceGlobalCfgMgr:: Device isolated. Executing TSA" )
@@ -98,6 +191,8 @@ def isolate_unisolate_device(self, tsa_status):
98
191
self .cfg_mgr .push (cmd )
99
192
log_debug ("DeviceGlobalCfgMgr::Done" )
100
193
194
+ return True
195
+
101
196
def get_ts_routemaps (self , cmds , ts_template ):
102
197
if not cmds :
103
198
return ""
@@ -134,6 +229,16 @@ def __extract_out_route_map_names(self, cmds):
134
229
return route_map_names
135
230
136
231
def downstream_isolate_unisolate (self , idf_isolation_state ):
232
+ """ API to apply IDF configuration """
233
+
234
+ if idf_isolation_state not in ["unisolated" , "isolated_withdraw_all" , "isolated_no_export" ]:
235
+ log_err ("IDF: invalid value({}) is provided" .format (idf_isolation_state ))
236
+ return False
237
+
238
+ if self .switch_type and self .switch_type != "SpineRouter" :
239
+ log_debug ("DeviceGlobalCfgMgr:: Skipping IDF isolation configuration on Switch type: %s" % self .switch_type )
240
+ return True
241
+
137
242
cmd = "\n "
138
243
if idf_isolation_state == "unisolated" :
139
244
cmd += self .idf_unisolate_template .render (constants = self .constants )
@@ -145,12 +250,16 @@ def downstream_isolate_unisolate(self, idf_isolation_state):
145
250
self .cfg_mgr .push (cmd )
146
251
log_debug ("DeviceGlobalCfgMgr::Done" )
147
252
253
+ return True
254
+
148
255
def check_state_and_get_idf_isolation_routemaps (self ):
149
256
""" API to get TSA route-maps if device is isolated"""
257
+
150
258
cmd = ""
151
259
if self .directory .path_exist ("CONFIG_DB" , swsscommon .CFG_BGP_DEVICE_GLOBAL_TABLE_NAME , "idf_isolation_state" ):
152
260
idf_isolation_state = self .directory .get_slot ("CONFIG_DB" , swsscommon .CFG_BGP_DEVICE_GLOBAL_TABLE_NAME )["idf_isolation_state" ]
153
- if idf_isolation_state != "unisolated" :
261
+ if idf_isolation_state != "unisolated" :
154
262
log_notice ("DeviceGlobalCfgMgr:: IDF is isolated. Applying required route-maps" )
155
- cmd = self .idf_isolate_template .render (isolation_status = idf_isolation_state , constants = self .constants )
156
- return cmd
263
+ cmd = self .idf_isolate_template .render (isolation_status = idf_isolation_state , constants = self .constants )
264
+
265
+ return cmd
0 commit comments