@@ -38,6 +38,31 @@ def deep_update(dst, src):
3838 return dst
3939
4040
41+ class AclAction :
42+ """ namespace for ACL action keys """
43+
44+ PACKET = "PACKET_ACTION"
45+ REDIRECT = "REDIRECT_ACTION"
46+ MIRROR = "MIRROR_ACTION"
47+ MIRROR_INGRESS = "MIRROR_INGRESS_ACTION"
48+ MIRROR_EGRESS = "MIRROR_EGRESS_ACTION"
49+
50+
51+ class PacketAction :
52+ """ namespace for ACL packet actions """
53+
54+ DROP = "DROP"
55+ FORWARD = "FORWARD"
56+ ACCEPT = "ACCEPT"
57+
58+
59+ class Stage :
60+ """ namespace for ACL stages """
61+
62+ INGRESS = "INGRESS"
63+ EGRESS = "EGRESS"
64+
65+
4166class AclLoaderException (Exception ):
4267 pass
4368
@@ -52,6 +77,9 @@ class AclLoader(object):
5277 STATE_MIRROR_SESSION_TABLE = "MIRROR_SESSION_TABLE"
5378 POLICER = "POLICER"
5479 SESSION_PREFIX = "everflow"
80+ SWITCH_CAPABILITY_TABLE = "SWITCH_CAPABILITY"
81+ ACL_ACTIONS_CAPABILITY_FIELD = "ACL_ACTIONS"
82+ ACL_ACTION_CAPABILITY_FIELD = "ACL_ACTION"
5583
5684 min_priority = 1
5785 max_priority = 10000
@@ -81,6 +109,7 @@ class AclLoader(object):
81109 def __init__ (self ):
82110 self .yang_acl = None
83111 self .requested_session = None
112+ self .mirror_stage = None
84113 self .current_table = None
85114 self .tables_db_info = {}
86115 self .rules_db_info = {}
@@ -179,6 +208,14 @@ def set_session_name(self, session_name):
179208
180209 self .requested_session = session_name
181210
211+ def set_mirror_stage (self , stage ):
212+ """
213+ Set mirror stage to be used in ACL mirror rule action
214+ :param session_name: stage 'ingress'/'egress'
215+ :return:
216+ """
217+ self .mirror_stage = stage .upper ()
218+
182219 def set_max_priority (self , priority ):
183220 """
184221 Set rules max priority
@@ -232,25 +269,72 @@ def convert_action(self, table_name, rule_idx, rule):
232269
233270 if rule .actions .config .forwarding_action == "ACCEPT" :
234271 if self .is_table_control_plane (table_name ):
235- rule_props ["PACKET_ACTION" ] = " ACCEPT"
272+ rule_props [AclAction . PACKET ] = PacketAction . ACCEPT
236273 elif self .is_table_mirror (table_name ):
237274 session_name = self .get_session_name ()
238275 if not session_name :
239276 raise AclLoaderException ("Mirroring session does not exist" )
240277
241- rule_props ["MIRROR_ACTION" ] = session_name
278+ if self .mirror_stage == Stage .INGRESS :
279+ mirror_action = AclAction .MIRROR_INGRESS
280+ elif self .mirror_stage == Stage .EGRESS :
281+ mirror_action = AclAction .MIRROR_EGRESS
282+ else :
283+ raise AclLoaderException ("Invalid mirror stage passed {}" .format (self .mirror_stage ))
284+
285+ rule_props [mirror_action ] = session_name
242286 else :
243- rule_props ["PACKET_ACTION" ] = " FORWARD"
287+ rule_props [AclAction . PACKET ] = PacketAction . FORWARD
244288 elif rule .actions .config .forwarding_action == "DROP" :
245- rule_props ["PACKET_ACTION" ] = " DROP"
289+ rule_props [AclAction . PACKET ] = PacketAction . DROP
246290 elif rule .actions .config .forwarding_action == "REJECT" :
247- rule_props ["PACKET_ACTION" ] = " DROP"
291+ rule_props [AclAction . PACKET ] = PacketAction . DROP
248292 else :
249- raise AclLoaderException ("Unknown rule action %s in table %s, rule %d" % (
293+ raise AclLoaderException ("Unknown rule action {} in table {}, rule {}" .format (
294+ rule .actions .config .forwarding_action , table_name , rule_idx ))
295+
296+ if not self .validate_actions (table_name , rule_props ):
297+ raise AclLoaderException ("Rule action {} is not supported in table {}, rule {}" .format (
250298 rule .actions .config .forwarding_action , table_name , rule_idx ))
251299
252300 return rule_props
253301
302+ def validate_actions (self , table_name , action_props ):
303+ if self .is_table_control_plane (table_name ):
304+ return True
305+
306+ action_count = len (action_props )
307+
308+ if table_name not in self .tables_db_info :
309+ raise AclLoaderException ("Table {} does not exist" .format (table_name ))
310+
311+ stage = self .tables_db_info [table_name ].get ("stage" , Stage .INGRESS )
312+ capability = self .statedb .get_all (self .statedb .STATE_DB , "{}|switch" .format (self .SWITCH_CAPABILITY_TABLE ))
313+ for action_key in dict (action_props ):
314+ key = "{}|{}" .format (self .ACL_ACTIONS_CAPABILITY_FIELD , stage .upper ())
315+ if key not in capability :
316+ del action_props [action_key ]
317+ continue
318+
319+ values = capability [key ].split ("," )
320+ if action_key .upper () not in values :
321+ del action_props [action_key ]
322+ continue
323+
324+ if action_key == AclAction .PACKET :
325+ # Check if action_value is supported
326+ action_value = action_props [action_key ]
327+ key = "{}|{}" .format (self .ACL_ACTION_CAPABILITY_FIELD , action_key .upper ())
328+ if key not in capability :
329+ del action_props [action_key ]
330+ continue
331+
332+ if action_value not in capability [key ]:
333+ del action_props [action_key ]
334+ continue
335+
336+ return action_count == len (action_props )
337+
254338 def convert_l2 (self , table_name , rule_idx , rule ):
255339 rule_props = {}
256340
@@ -510,30 +594,32 @@ def show_table(self, table_name):
510594 :param table_name: Optional. ACL table name. Filter tables by specified name.
511595 :return:
512596 """
513- header = ("Name" , "Type" , "Binding" , "Description" )
597+ header = ("Name" , "Type" , "Binding" , "Description" , "Stage" )
514598
515599 data = []
516600 for key , val in self .get_tables_db_info ().iteritems ():
517601 if table_name and key != table_name :
518602 continue
519603
604+ stage = val .get ("stage" , Stage .INGRESS ).lower ()
605+
520606 if val ["type" ] == AclLoader .ACL_TABLE_TYPE_CTRLPLANE :
521607 services = natsorted (val ["services" ])
522- data .append ([key , val ["type" ], services [0 ], val ["policy_desc" ]])
608+ data .append ([key , val ["type" ], services [0 ], val ["policy_desc" ], stage ])
523609
524610 if len (services ) > 1 :
525611 for service in services [1 :]:
526- data .append (["" , "" , service , "" ])
612+ data .append (["" , "" , service , "" , "" ])
527613 else :
528614 if not val ["ports" ]:
529- data .append ([key , val ["type" ], "" , val ["policy_desc" ]])
615+ data .append ([key , val ["type" ], "" , val ["policy_desc" ], stage ])
530616 else :
531617 ports = natsorted (val ["ports" ])
532- data .append ([key , val ["type" ], ports [0 ], val ["policy_desc" ]])
618+ data .append ([key , val ["type" ], ports [0 ], val ["policy_desc" ], stage ])
533619
534620 if len (ports ) > 1 :
535621 for port in ports [1 :]:
536- data .append (["" , "" , port , "" ])
622+ data .append (["" , "" , port , "" , "" ])
537623
538624 print (tabulate .tabulate (data , headers = header , tablefmt = "simple" , missingval = "" ))
539625
@@ -586,7 +672,33 @@ def show_rule(self, table_name, rule_id):
586672 """
587673 header = ("Table" , "Rule" , "Priority" , "Action" , "Match" )
588674
589- ignore_list = ["PRIORITY" , "PACKET_ACTION" , "MIRROR_ACTION" ]
675+ def pop_priority (val ):
676+ priority = val .pop ("PRIORITY" )
677+ return priority
678+
679+ def pop_action (val ):
680+ action = ""
681+
682+ for key in dict (val ):
683+ key = key .upper ()
684+ if key == AclAction .PACKET :
685+ action = val .pop (key )
686+ elif key == AclAction .REDIRECT :
687+ action = "REDIRECT: {}" .format (val .pop (key ))
688+ elif key in (AclAction .MIRROR , AclAction .MIRROR_INGRESS ):
689+ action = "MIRROR INGRESS: {}" .format (val .pop (key ))
690+ elif key == AclAction .MIRROR_EGRESS :
691+ action = "MIRROR EGRESS: {}" .format (val .pop (key ))
692+ else :
693+ continue
694+
695+ return action
696+
697+ def pop_matches (val ):
698+ matches = list (sorted (["%s: %s" % (k , val [k ]) for k in val ]))
699+ if len (matches ) == 0 :
700+ matches .append ("N/A" )
701+ return matches
590702
591703 raw_data = []
592704 for (tname , rid ), val in self .get_rules_db_info ().iteritems ():
@@ -597,22 +709,9 @@ def show_rule(self, table_name, rule_id):
597709 if rule_id and rule_id != rid :
598710 continue
599711
600- priority = val ["PRIORITY" ]
601-
602- action = ""
603- if "PACKET_ACTION" in val :
604- action = val ["PACKET_ACTION" ]
605- elif "MIRROR_ACTION" in val :
606- action = "MIRROR: %s" % val ["MIRROR_ACTION" ]
607- else :
608- continue
609-
610- matches = ["%s: %s" % (k , v ) for k , v in val .iteritems () if k not in ignore_list ]
611-
612- matches .sort ()
613-
614- if len (matches ) == 0 :
615- matches .append ("N/A" )
712+ priority = pop_priority (val )
713+ action = pop_action (val )
714+ matches = pop_matches (val )
616715
617716 rule_data = [[tname , rid , priority , action , matches [0 ]]]
618717 if len (matches ) > 1 :
@@ -718,9 +817,10 @@ def update(ctx):
718817@click .argument ('filename' , type = click .Path (exists = True ))
719818@click .option ('--table_name' , type = click .STRING , required = False )
720819@click .option ('--session_name' , type = click .STRING , required = False )
820+ @click .option ('--mirror_stage' , type = click .Choice (["ingress" , "egress" ]), default = "ingress" )
721821@click .option ('--max_priority' , type = click .INT , required = False )
722822@click .pass_context
723- def full (ctx , filename , table_name , session_name , max_priority ):
823+ def full (ctx , filename , table_name , session_name , mirror_stage , max_priority ):
724824 """
725825 Full update of ACL rules configuration.
726826 If a table_name is provided, the operation will be restricted in the specified table.
@@ -733,6 +833,8 @@ def full(ctx, filename, table_name, session_name, max_priority):
733833 if session_name :
734834 acl_loader .set_session_name (session_name )
735835
836+ acl_loader .set_mirror_stage (mirror_stage )
837+
736838 if max_priority :
737839 acl_loader .set_max_priority (max_priority )
738840
@@ -743,9 +845,10 @@ def full(ctx, filename, table_name, session_name, max_priority):
743845@update .command ()
744846@click .argument ('filename' , type = click .Path (exists = True ))
745847@click .option ('--session_name' , type = click .STRING , required = False )
848+ @click .option ('--mirror_stage' , type = click .Choice (["ingress" , "egress" ]), default = "ingress" )
746849@click .option ('--max_priority' , type = click .INT , required = False )
747850@click .pass_context
748- def incremental (ctx , filename , session_name , max_priority ):
851+ def incremental (ctx , filename , session_name , mirror_stage , max_priority ):
749852 """
750853 Incremental update of ACL rule configuration.
751854 """
@@ -754,6 +857,8 @@ def incremental(ctx, filename, session_name, max_priority):
754857 if session_name :
755858 acl_loader .set_session_name (session_name )
756859
860+ acl_loader .set_mirror_stage (mirror_stage )
861+
757862 if max_priority :
758863 acl_loader .set_max_priority (max_priority )
759864
0 commit comments