@@ -934,9 +934,11 @@ def config_state_change(spawn, from_state, sm):
934934 else :
935935 sm .update_cur_state (from_state )
936936
937+ # Flatten multi-line input into one-command-per-line list.
938+ flat_cmd_list = list (self .utils .flatten_splitlines_command (command ) if command else [])
939+
937940 self .result = ''
938- if command :
939- flat_cmd = self .utils .flatten_splitlines_command (command )
941+ if flat_cmd_list :
940942 dialog = self .dialog + self .service_dialog (handle = handle , service_dialog = reply )
941943 # Add all known states to detect state changes.
942944 for state in sm .states :
@@ -957,22 +959,35 @@ def config_state_change(spawn, from_state, sm):
957959 matched_retry_sleep = self .state_change_matched_retry_sleep
958960 ))
959961
960- banner_lines , command_lines , banner_delim = self .get_banner_lines (flat_cmd )
962+ # Use flattened command list
963+ pre_lines , banner_lines , post_lines , banner_delim = self .get_banner_lines (flat_cmd_list )
961964
962965 # Populate context for banner_text_handler only if banner was detected
963966 if banner_lines :
964967 self .connection .log .info ('Banner detected, configuring banners without state detection' )
965968
969+ for cmd in pre_lines :
970+ handle .spawn .sendline (cmd )
971+ self .update_hostname_if_needed ([cmd ])
972+ self .process_dialog_on_handle (handle , dialog , timeout )
973+
966974 # Send banner lines
967975 for line in banner_lines :
968976 handle .spawn .sendline (line )
969977 time .sleep (0.1 )
970978 handle .spawn .read_update_buffer ()
971979
980+ self .process_dialog_on_handle (handle , dialog , timeout )
972981
973- if bulk :
982+ post_cmds = chain (post_lines , [self .commit_cmd ]) if self .commit_cmd else post_lines
983+ for cmd in post_cmds :
984+ handle .spawn .sendline (cmd )
985+ self .update_hostname_if_needed ([cmd ])
986+ self .process_dialog_on_handle (handle , dialog , timeout )
987+
988+ elif bulk :
974989 indicator = handle .settings .BULK_CONFIG_END_INDICATOR
975- cmd_lst = list (chain (command_lines , [indicator ]))
990+ cmd_lst = list (chain (flat_cmd_list , [indicator ]))
976991 if bulk_chunk_lines == 0 :
977992 chunks = [cmd_lst ]
978993 else :
@@ -998,8 +1013,8 @@ def config_state_change(spawn, from_state, sm):
9981013 handle .spawn .sendline (self .commit_cmd )
9991014 self .process_dialog_on_handle (handle , dialog , timeout )
10001015 else :
1001- cmds = chain (command_lines , [self .commit_cmd ]) \
1002- if self .commit_cmd else command_lines
1016+ cmds = chain (flat_cmd_list , [self .commit_cmd ]) \
1017+ if self .commit_cmd else flat_cmd_list
10031018 for cmd in cmds :
10041019 handle .spawn .sendline (cmd )
10051020 self .update_hostname_if_needed ([cmd ])
@@ -1032,34 +1047,49 @@ def config_state_change(spawn, from_state, sm):
10321047
10331048
10341049 def get_banner_lines (self , config_lines ):
1035- """ Process lines related to the banner command
1050+ """ Process lines related to the banner command.
1051+ Handles detection and separation of banner configuration blocks from
1052+ regular configuration commands. Supports the first banner block only;
1053+ subsequent banners (if any) will be processed sequentially.
10361054 Args:
1037- config_lines (list): list of config lines
1055+ config_lines (list): Configuration command lines
10381056 Returns:
1039- tuple: (banner_lines, command_lines, banner_delim)
1057+ tuple: (pre_lines, banner_lines, post_lines, banner_delim)
1058+ - pre_lines: Commands before the banner block
1059+ - banner_lines: The banner initiation line and content
1060+ - post_lines: Commands after the banner block
1061+ - banner_delim: The delimiter character used for the banner
10401062 """
1041- banner_lines = []
1042- command_lines = []
1063+ pre_lines , banner_lines , post_lines = [], [], []
10431064 banner_delim = None
1065+ in_banner = False
1066+ banner_seen = False
10441067
10451068 for line in config_lines :
10461069
1047- match = re .match (r'^\s*banner\s+(login|motd|exec|incoming)\s+(\S)' , line )
1048- if match :
1049- banner_lines .append (line )
1050- banner_delim = match .group (2 )
1070+ if not in_banner and not banner_seen :
1071+ match = re .match (r'^\s*banner\s+(login|motd|exec|incoming)\s+(\S+)' , line )
1072+ if match :
1073+ banner_lines .append (line )
1074+ raw_delim = match .group (2 )
1075+ # Use '^C' token when present, else first character (e.g. '%')
1076+ banner_delim = '^C' if raw_delim .startswith ('^C' ) else raw_delim [0 ]
1077+ in_banner = True
1078+ banner_seen = True
1079+ continue
1080+ pre_lines .append (line )
10511081 continue
10521082
1053- if banner_delim :
1083+ if in_banner :
10541084 banner_lines .append (line )
10551085 # End of banner when delimiter repeats as a full line
10561086 if line .strip () == banner_delim :
1057- banner_delim = None
1087+ in_banner = False
10581088 continue
10591089
1060- command_lines .append (line )
1090+ post_lines .append (line )
10611091
1062- return banner_lines , command_lines , banner_delim
1092+ return pre_lines , banner_lines , post_lines , banner_delim
10631093
10641094 def process_dialog_on_handle (self , handle , dialog , timeout ):
10651095 try :
@@ -2175,7 +2205,7 @@ def call_service(self, # noqa: C901
21752205 if command and reload_command :
21762206 raise SubCommandFailure (
21772207 "Please use either 'command' or 'reload_command' parameter" )
2178- command = command or reload_command or self .command
2208+ command = command if command is not None else ( reload_command if reload_command is not None else self .command )
21792209
21802210 # TODO counter value must be moved to settings
21812211 counter = 0
@@ -2197,7 +2227,8 @@ def call_service(self, # noqa: C901
21972227 dialog += Dialog (custom_auth_stmt )
21982228
21992229 # Issue reload command
2200- con .active .spawn .sendline (command )
2230+ if command :
2231+ con .active .spawn .sendline (command )
22012232 try :
22022233 reload_output = dialog .process (con .active .spawn ,
22032234 context = context ,
@@ -2373,7 +2404,7 @@ def call_service(self, command=None, # noqa: C901
23732404 category = DeprecationWarning )
23742405
23752406 timeout = timeout or self .timeout
2376- command = command or self .command
2407+ command = command if command is not None else self .command
23772408 switchover_counter = con .settings .SWITCHOVER_COUNTER
23782409 con .log .debug ("+++ Issuing switchover on %s with "
23792410 "switchover_command %s and timeout is %s +++"
@@ -2403,7 +2434,8 @@ def call_service(self, command=None, # noqa: C901
24032434 context = con .standby .context
24042435
24052436 # Issue switchover command
2406- con .active .spawn .sendline (command )
2437+ if command :
2438+ con .active .spawn .sendline (command )
24072439 try :
24082440 dialog .process (con .active .spawn ,
24092441 timeout = timeout ,
0 commit comments