blob: 01242a1e05c8acd94533bd91042e9f1ed378e996 [file] [log] [blame]
adminbae64d82013-08-01 10:50:15 -07001#!/usr/bin/env python
kelvin8ec71442015-01-15 16:57:00 -08002"""
adminbae64d82013-08-01 10:50:15 -07003Created on 24-Oct-2012
Jeremy Ronquillo4d5f1d02017-10-13 20:23:57 +00004Copyright 2012 Open Networking Foundation (ONF)
kelvin8ec71442015-01-15 16:57:00 -08005
Jeremy Songsterae01bba2016-07-11 15:39:17 -07006Please refer questions to either the onos test mailing list at <onos-test@onosproject.org>,
7the System Testing Plans and Results wiki page at <https://wiki.onosproject.org/x/voMg>,
8or the System Testing Guide page at <https://wiki.onosproject.org/x/WYQg>
adminbae64d82013-08-01 10:50:15 -07009
10 TestON is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation, either version 2 of the License, or
Jeremy Ronquillo82705492017-10-18 14:19:55 -070013 (at your option) any later version.
adminbae64d82013-08-01 10:50:15 -070014
15 TestON is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
kelvin8ec71442015-01-15 16:57:00 -080021 along with TestON. If not, see <http://www.gnu.org/licenses/>.
kelvin8ec71442015-01-15 16:57:00 -080022"""
adminbae64d82013-08-01 10:50:15 -070023import pexpect
kelvin8ec71442015-01-15 16:57:00 -080024import re
Jon Hall06fd0df2021-01-25 15:50:06 -080025import os
adminbae64d82013-08-01 10:50:15 -070026
27from drivers.component import Component
kelvin8ec71442015-01-15 16:57:00 -080028
29
30class CLI( Component ):
31
32 """
adminbae64d82013-08-01 10:50:15 -070033 This will define common functions for CLI included.
kelvin8ec71442015-01-15 16:57:00 -080034 """
35 def __init__( self ):
Devin Limdc78e202017-06-09 18:30:07 -070036 super( CLI, self ).__init__()
Jon Hall3c0114c2020-08-11 15:07:42 -070037 self.inDocker = False
Jon Halla16b4db2021-10-20 14:11:59 -070038 self.portForwardList = None
Jon Hallca319892017-06-15 15:25:22 -070039
Jeremy Ronquillo82705492017-10-18 14:19:55 -070040 def checkPrompt( self ):
Devin Limdc78e202017-06-09 18:30:07 -070041 for key in self.options:
Jeremy Ronquillo82705492017-10-18 14:19:55 -070042 if key == "prompt" and self.options[ 'prompt' ] is not None:
43 self.prompt = self.options[ 'prompt' ]
Devin Limdc78e202017-06-09 18:30:07 -070044 break
kelvin8ec71442015-01-15 16:57:00 -080045
46 def connect( self, **connectargs ):
47 """
adminbae64d82013-08-01 10:50:15 -070048 Connection will establish to the remote host using ssh.
49 It will take user_name ,ip_address and password as arguments<br>
kelvin8ec71442015-01-15 16:57:00 -080050 and will return the handle.
51 """
Jon Hall06fd0df2021-01-25 15:50:06 -080052 self.shell = "/bin/bash -l"
adminbae64d82013-08-01 10:50:15 -070053 for key in connectargs:
kelvin8ec71442015-01-15 16:57:00 -080054 vars( self )[ key ] = connectargs[ key ]
Devin Limdc78e202017-06-09 18:30:07 -070055 self.checkPrompt()
adminbae64d82013-08-01 10:50:15 -070056
kelvin8ec71442015-01-15 16:57:00 -080057 connect_result = super( CLI, self ).connect()
adminbae64d82013-08-01 10:50:15 -070058 ssh_newkey = 'Are you sure you want to continue connecting'
kelvin8ec71442015-01-15 16:57:00 -080059 refused = "ssh: connect to host " + \
60 self.ip_address + " port 22: Connection refused"
Jon Halla16b4db2021-10-20 14:11:59 -070061 ssh_options = "-t -X -A -o ServerAliveInterval=50 -o ServerAliveCountMax=1000 -o TCPKeepAlive=yes"
Jon Hall06fd0df2021-01-25 15:50:06 -080062 ssh_destination = self.user_name + "@" + self.ip_address
63 envVars = { "TERM": "vt100" }
64 # TODO: Add option to specify which shell/command to use
65 jump_host = main.componentDictionary[ self.name ].get( 'jump_host' )
adminbae64d82013-08-01 10:50:15 -070066 if self.port:
Jon Hall06fd0df2021-01-25 15:50:06 -080067 ssh_option += " -p " + self.port
68 if jump_host:
69 jump_host = main.componentDictionary.get( jump_host )
70 ssh_options += " -J %s@%s" % ( jump_host.get( 'user' ), jump_host.get( 'host' ) )
71 ssh_auth = os.getenv('SSH_AUTH_SOCK')
72 if ssh_auth:
73 envVars[ 'SSH_AUTH_SOCK' ] = ssh_auth
74 self.handle = pexpect.spawn(
75 "ssh %s %s %s" % ( ssh_options, ssh_destination, self.shell ),
76 env=envVars,
77 maxread=1000000,
78 timeout=60 )
adminbae64d82013-08-01 10:50:15 -070079
Jon Hall73057ee2016-08-23 09:57:26 -070080 # set tty window size
81 self.handle.setwinsize( 24, 250 )
82
adminbae64d82013-08-01 10:50:15 -070083 self.handle.logfile = self.logfile_handler
kelvin8ec71442015-01-15 16:57:00 -080084 i = 5
85 while i == 5:
Jon Hall4173b242017-09-12 17:04:38 -070086 i = self.handle.expect( [ ssh_newkey,
87 'password:|Password:',
88 pexpect.EOF,
89 pexpect.TIMEOUT,
90 refused,
91 'teston>',
Jon Halld9066132018-03-01 14:52:53 -080092 'Permission denied, please try again.',
Jon Hall4173b242017-09-12 17:04:38 -070093 self.prompt ],
94 120 )
acsmars32de0bc2015-06-30 09:57:12 -070095 if i == 0: # Accept key, then expect either a password prompt or access
Jon Hall3c0114c2020-08-11 15:07:42 -070096 main.log.info( self.name + ": ssh key confirmation received, send yes" )
kelvin8ec71442015-01-15 16:57:00 -080097 self.handle.sendline( 'yes' )
acsmars32de0bc2015-06-30 09:57:12 -070098 i = 5 # Run the loop again
acsmars07f9d392015-07-15 10:30:58 -070099 continue
100 if i == 1: # Password required
Jon Hall63604932015-02-26 17:09:50 -0800101 if self.pwd:
102 main.log.info(
Jon Hall4173b242017-09-12 17:04:38 -0700103 "ssh connection asked for password, gave password" )
Jon Hall63604932015-02-26 17:09:50 -0800104 else:
Jon Hall3c0114c2020-08-11 15:07:42 -0700105 main.log.info( self.name + ": Server asked for password, but none was "
acsmars07f9d392015-07-15 10:30:58 -0700106 "given in the .topo file. Trying "
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700107 "no password." )
acsmars07f9d392015-07-15 10:30:58 -0700108 self.pwd = ""
109 self.handle.sendline( self.pwd )
110 j = self.handle.expect( [
acsmars07f9d392015-07-15 10:30:58 -0700111 'password:|Password:',
Jon Halld9066132018-03-01 14:52:53 -0800112 'Permission denied, please try again.',
113 self.prompt,
acsmars07f9d392015-07-15 10:30:58 -0700114 pexpect.EOF,
115 pexpect.TIMEOUT ],
116 120 )
Jon Halld9066132018-03-01 14:52:53 -0800117 if j != 2:
Jon Hall3c0114c2020-08-11 15:07:42 -0700118 main.log.error( self.name + ": Incorrect Password" )
acsmars07f9d392015-07-15 10:30:58 -0700119 return main.FALSE
kelvin8ec71442015-01-15 16:57:00 -0800120 elif i == 2:
Jon Hall3c0114c2020-08-11 15:07:42 -0700121 main.log.error( self.name + ": Connection timeout" )
Jon Hall06fd0df2021-01-25 15:50:06 -0800122 main.log.debug( self.handle.before )
kelvin8ec71442015-01-15 16:57:00 -0800123 return main.FALSE
124 elif i == 3: # timeout
125 main.log.error(
126 "No route to the Host " +
127 self.user_name +
128 "@" +
129 self.ip_address )
Jon Hall06fd0df2021-01-25 15:50:06 -0800130 main.log.debug( self.handle.before )
kelvin8ec71442015-01-15 16:57:00 -0800131 return main.FALSE
132 elif i == 4:
133 main.log.error(
134 "ssh: connect to host " +
135 self.ip_address +
136 " port 22: Connection refused" )
Jon Hall06fd0df2021-01-25 15:50:06 -0800137 main.log.debug( self.handle.before )
kelvin8ec71442015-01-15 16:57:00 -0800138 return main.FALSE
Jon Halld9066132018-03-01 14:52:53 -0800139 elif i == 6: # Incorrect Password
Jon Hall3c0114c2020-08-11 15:07:42 -0700140 main.log.error( self.name + ": Incorrect Password" )
Jon Hall06fd0df2021-01-25 15:50:06 -0800141 main.log.debug( self.handle.before )
Jon Halld9066132018-03-01 14:52:53 -0800142 return main.FALSE
143 elif i == 7: # Prompt
Jon Hall3c0114c2020-08-11 15:07:42 -0700144 main.log.info( self.name + ": Password not required logged in" )
adminbae64d82013-08-01 10:50:15 -0700145
kelvin8ec71442015-01-15 16:57:00 -0800146 self.handle.sendline( "" )
Devin Limdc78e202017-06-09 18:30:07 -0700147 self.handle.expect( self.prompt )
Jeremy Ronquillo0f2008a2017-06-23 15:32:51 -0700148 self.handle.sendline( "cd" )
149 self.handle.expect( self.prompt )
adminbae64d82013-08-01 10:50:15 -0700150 return self.handle
151
kelvin8ec71442015-01-15 16:57:00 -0800152 def disconnect( self ):
Jon Hall06fd0df2021-01-25 15:50:06 -0800153 result = self.preDisconnect()
kelvin8ec71442015-01-15 16:57:00 -0800154 result = super( CLI, self ).disconnect( self )
adminbae64d82013-08-01 10:50:15 -0700155 result = main.TRUE
Jon Hall3c0114c2020-08-11 15:07:42 -0700156
157 def Prompt( self ):
158 """
159 Returns the prompt to expect depending on what program we are in
160 """
161 return self.prompt if not self.inDocker else self.dockerPrompt
kelvin8ec71442015-01-15 16:57:00 -0800162
163 def execute( self, **execparams ):
164 """
adminbae64d82013-08-01 10:50:15 -0700165 It facilitates the command line execution of a given command. It has arguments as :
166 cmd => represents command to be executed,
167 prompt => represents expect command prompt or output,
168 timeout => timeout for command execution,
169 more => to provide a key press if it is on.
You Wang7d14d642019-01-23 15:10:08 -0800170 logCmd => log the command executed if True
adminbae64d82013-08-01 10:50:15 -0700171
172 It will return output of command exection.
kelvin8ec71442015-01-15 16:57:00 -0800173 """
174 result = super( CLI, self ).execute( self )
adminaef00552014-05-08 09:18:36 -0700175 defaultPrompt = '.*[$>\#]'
Jon Hall3b489db2015-10-05 14:38:37 -0700176 args = utilities.parse_args( [ "CMD",
177 "TIMEOUT",
178 "PROMPT",
You Wang7d14d642019-01-23 15:10:08 -0800179 "MORE",
180 "LOGCMD" ],
Jon Hall3b489db2015-10-05 14:38:37 -0700181 **execparams )
kelvin8ec71442015-01-15 16:57:00 -0800182
183 expectPrompt = args[ "PROMPT" ] if args[ "PROMPT" ] else defaultPrompt
adminbae64d82013-08-01 10:50:15 -0700184 self.LASTRSP = ""
kelvin8ec71442015-01-15 16:57:00 -0800185 timeoutVar = args[ "TIMEOUT" ] if args[ "TIMEOUT" ] else 10
adminbae64d82013-08-01 10:50:15 -0700186 cmd = ''
kelvin8ec71442015-01-15 16:57:00 -0800187 if args[ "CMD" ]:
188 cmd = args[ "CMD" ]
189 else:
adminbae64d82013-08-01 10:50:15 -0700190 return 0
kelvin8ec71442015-01-15 16:57:00 -0800191 if args[ "MORE" ] is None:
192 args[ "MORE" ] = " "
193 self.handle.sendline( cmd )
adminbae64d82013-08-01 10:50:15 -0700194 self.lastCommand = cmd
Jon Hall3b489db2015-10-05 14:38:37 -0700195 index = self.handle.expect( [ expectPrompt,
196 "--More--",
197 'Command not found.',
198 pexpect.TIMEOUT,
199 "^:$" ],
200 timeout=timeoutVar )
adminbae64d82013-08-01 10:50:15 -0700201 if index == 0:
kelvin8ec71442015-01-15 16:57:00 -0800202 self.LASTRSP = self.LASTRSP + \
203 self.handle.before + self.handle.after
You Wang7d14d642019-01-23 15:10:08 -0800204 if not args[ "LOGCMD" ] is False:
Jon Hall3c0114c2020-08-11 15:07:42 -0700205 main.log.info( self.name + ": Executed :" + str( cmd ) +
You Wang7d14d642019-01-23 15:10:08 -0800206 " \t\t Expected Prompt '" + str( expectPrompt ) +
207 "' Found" )
adminbae64d82013-08-01 10:50:15 -0700208 elif index == 1:
209 self.LASTRSP = self.LASTRSP + self.handle.before
kelvin8ec71442015-01-15 16:57:00 -0800210 self.handle.send( args[ "MORE" ] )
211 main.log.info(
212 "Found More screen to go , Sending a key to proceed" )
213 indexMore = self.handle.expect(
214 [ "--More--", expectPrompt ], timeout=timeoutVar )
adminbae64d82013-08-01 10:50:15 -0700215 while indexMore == 0:
kelvin8ec71442015-01-15 16:57:00 -0800216 main.log.info(
217 "Found anoother More screen to go , Sending a key to proceed" )
218 self.handle.send( args[ "MORE" ] )
219 indexMore = self.handle.expect(
220 [ "--More--", expectPrompt ], timeout=timeoutVar )
adminbae64d82013-08-01 10:50:15 -0700221 self.LASTRSP = self.LASTRSP + self.handle.before
kelvin8ec71442015-01-15 16:57:00 -0800222 elif index == 2:
Jon Hall3c0114c2020-08-11 15:07:42 -0700223 main.log.error( self.name + ": Command not found" )
adminbae64d82013-08-01 10:50:15 -0700224 self.LASTRSP = self.LASTRSP + self.handle.before
kelvin8ec71442015-01-15 16:57:00 -0800225 elif index == 3:
Jon Hall3c0114c2020-08-11 15:07:42 -0700226 main.log.error( self.name + ": Expected Prompt not found, Time Out!!" )
kelvin8ec71442015-01-15 16:57:00 -0800227 main.log.error( expectPrompt )
Jon Hall3b489db2015-10-05 14:38:37 -0700228 self.LASTRSP = self.LASTRSP + self.handle.before
229 return self.LASTRSP
adminbae64d82013-08-01 10:50:15 -0700230 elif index == 4:
231 self.LASTRSP = self.LASTRSP + self.handle.before
kelvin8ec71442015-01-15 16:57:00 -0800232 # self.handle.send( args[ "MORE" ] )
233 self.handle.sendcontrol( "D" )
234 main.log.info(
Jon Hall3b489db2015-10-05 14:38:37 -0700235 "Found More screen to go, Sending a key to proceed" )
kelvin8ec71442015-01-15 16:57:00 -0800236 indexMore = self.handle.expect(
237 [ "^:$", expectPrompt ], timeout=timeoutVar )
adminbae64d82013-08-01 10:50:15 -0700238 while indexMore == 0:
kelvin8ec71442015-01-15 16:57:00 -0800239 main.log.info(
Jon Hall3b489db2015-10-05 14:38:37 -0700240 "Found another More screen to go, Sending a key to proceed" )
kelvin8ec71442015-01-15 16:57:00 -0800241 self.handle.sendcontrol( "D" )
242 indexMore = self.handle.expect(
243 [ "^:$", expectPrompt ], timeout=timeoutVar )
adminbae64d82013-08-01 10:50:15 -0700244 self.LASTRSP = self.LASTRSP + self.handle.before
kelvin8ec71442015-01-15 16:57:00 -0800245 main.last_response = self.remove_contol_chars( self.LASTRSP )
adminbae64d82013-08-01 10:50:15 -0700246 return self.LASTRSP
kelvin8ec71442015-01-15 16:57:00 -0800247
248 def remove_contol_chars( self, response ):
249 # RE_XML_ILLEGAL = '([\u0000-\u0008\u000b-\u000c\u000e-\u001f\ufffe-\uffff])|([%s-%s][^%s-%s])|([^%s-%s][%s-%s])|([%s-%s]$)|(^[%s-%s])'%( unichr( 0xd800 ),unichr( 0xdbff ),unichr( 0xdc00 ),unichr( 0xdfff ),unichr( 0xd800 ),unichr( 0xdbff ),unichr( 0xdc00 ),unichr( 0xdfff ),unichr( 0xd800 ),unichr( 0xdbff ),unichr( 0xdc00 ),unichr( 0xdfff ) )
250 # response = re.sub( RE_XML_ILLEGAL, "\n", response )
251 response = re.sub( r"[\x01-\x1F\x7F]", "", response )
252 # response = re.sub( r"\[\d+\;1H", "\n", response )
253 response = re.sub( r"\[\d+\;\d+H", "", response )
adminbae64d82013-08-01 10:50:15 -0700254 return response
adminbae64d82013-08-01 10:50:15 -0700255
kelvin8ec71442015-01-15 16:57:00 -0800256 def runAsSudoUser( self, handle, pwd, default ):
257
258 i = handle.expect( [ ".ssword:*", default, pexpect.EOF ] )
259 if i == 0:
260 handle.sendline( pwd )
Jon Hall5ec6b1b2015-09-17 18:20:14 -0700261 handle.sendline( "\n" )
kelvin8ec71442015-01-15 16:57:00 -0800262
263 if i == 1:
264 handle.expect( default )
265
266 if i == 2:
Jon Hall3c0114c2020-08-11 15:07:42 -0700267 main.log.error( self.name + ": Unable to run as Sudo user" )
kelvin8ec71442015-01-15 16:57:00 -0800268
adminbae64d82013-08-01 10:50:15 -0700269 return handle
adminbae64d82013-08-01 10:50:15 -0700270
kelvin8ec71442015-01-15 16:57:00 -0800271 def onfail( self ):
272 if 'onfail' in main.componentDictionary[ self.name ]:
273 commandList = main.componentDictionary[
274 self.name ][ 'onfail' ].split( "," )
275 for command in commandList:
276 response = self.execute(
277 cmd=command,
278 prompt="(.*)",
279 timeout=120 )
adminbae64d82013-08-01 10:50:15 -0700280
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700281 def secureCopy( self, userName, ipAddress, filePath, dstPath, pwd="",
Jon Hall22a3bcf2021-07-23 11:40:11 -0700282 direction="from", options="", timeout=120 ):
kelvin8ec71442015-01-15 16:57:00 -0800283 """
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700284 Definition:
285 Execute scp command in linux to copy to/from a remote host
286 Required:
287 str userName - User name of the remote host
288 str ipAddress - IP address of the remote host
289 str filePath - File path including the file it self
290 str dstPath - Destination path
291 Optional:
292 str pwd - Password of the host
293 str direction - Direction of the scp, default to "from" which means
294 copy "from" the remote machine to local machine,
295 while "to" means copy "to" the remote machine from
296 local machine
kelvin8ec71442015-01-15 16:57:00 -0800297 """
Jon Hall669bc862021-03-09 12:24:44 -0800298 returnVal = main.FALSE
adminbae64d82013-08-01 10:50:15 -0700299 ssh_newkey = 'Are you sure you want to continue connecting'
kelvin8ec71442015-01-15 16:57:00 -0800300 refused = "ssh: connect to host " + \
Jon Hall547e0582015-09-21 17:35:40 -0700301 ipAddress + " port 22: Connection refused"
Jon Hall39570262020-11-17 12:18:19 -0800302 cmd = "scp %s " % options
Jon Hall669bc862021-03-09 12:24:44 -0800303 try:
304 self.handle.sendline( "" )
305 self.handle.expect( self.prompt, timeout=5 )
306 except pexpect.TIMEOUT:
307 main.log.error( "%s: Component not ready for input" % self.name )
308 main.log.debug( "%s: %s%s" % ( self.name, self.handle.before, str( self.handle.after ) ) )
309 self.handle.send( "\x03" ) # CTRL-C
310 self.handle.expect( self.prompt, timeout=5 )
acsmars07f9d392015-07-15 10:30:58 -0700311
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700312 if direction == "from":
Jon Hall39570262020-11-17 12:18:19 -0800313 cmd = cmd + str( userName ) + '@' + str( ipAddress ) + ':' + \
Jon Hall547e0582015-09-21 17:35:40 -0700314 str( filePath ) + ' ' + str( dstPath )
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700315 elif direction == "to":
Jon Hall39570262020-11-17 12:18:19 -0800316 cmd = cmd + str( filePath ) + ' ' + str( userName ) + \
Jon Hall547e0582015-09-21 17:35:40 -0700317 '@' + str( ipAddress ) + ':' + str( dstPath )
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700318 else:
319 main.log.debug( "Wrong direction using secure copy command!" )
320 return main.FALSE
kelvin8ec71442015-01-15 16:57:00 -0800321
Jon Hall3c0114c2020-08-11 15:07:42 -0700322 main.log.info( self.name + ": Sending: " + cmd )
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700323 self.handle.sendline( cmd )
Jon Hall547e0582015-09-21 17:35:40 -0700324 i = 0
Jon Hall66ce22f2021-06-30 14:57:40 -0700325 hit = False
Jon Hall669bc862021-03-09 12:24:44 -0800326 while i <= 6 :
Jon Hall547e0582015-09-21 17:35:40 -0700327 i = self.handle.expect( [
328 ssh_newkey,
329 'password:',
330 "100%",
331 refused,
332 "No such file or directory",
Jon Hall53c5e662016-04-13 16:06:56 -0700333 "Permission denied",
Devin Limdc78e202017-06-09 18:30:07 -0700334 self.prompt,
Jon Hall547e0582015-09-21 17:35:40 -0700335 pexpect.EOF,
336 pexpect.TIMEOUT ],
Jon Hall669bc862021-03-09 12:24:44 -0800337 timeout=timeout )
Jon Hall547e0582015-09-21 17:35:40 -0700338 if i == 0: # ask for ssh key confirmation
Jon Hall66ce22f2021-06-30 14:57:40 -0700339 hit = True
Jon Hall3c0114c2020-08-11 15:07:42 -0700340 main.log.info( self.name + ": ssh key confirmation received, sending yes" )
Jon Hall547e0582015-09-21 17:35:40 -0700341 self.handle.sendline( 'yes' )
342 elif i == 1: # Asked for ssh password
Jon Hall66ce22f2021-06-30 14:57:40 -0700343 hit = True
Jon Hall669bc862021-03-09 12:24:44 -0800344 timeout = 120
Jon Hall3c0114c2020-08-11 15:07:42 -0700345 main.log.info( self.name + ": ssh connection asked for password, gave password" )
Jon Hall547e0582015-09-21 17:35:40 -0700346 self.handle.sendline( pwd )
347 elif i == 2: # File finished transfering
Jon Hall66ce22f2021-06-30 14:57:40 -0700348 hit = True
Jon Hall3c0114c2020-08-11 15:07:42 -0700349 main.log.info( self.name + ": Secure copy successful" )
Jon Hall669bc862021-03-09 12:24:44 -0800350 timeout = 10
Jon Hall547e0582015-09-21 17:35:40 -0700351 returnVal = main.TRUE
352 elif i == 3: # Connection refused
Jon Hall66ce22f2021-06-30 14:57:40 -0700353 hit = True
Jon Hall547e0582015-09-21 17:35:40 -0700354 main.log.error(
355 "ssh: connect to host " +
356 ipAddress +
357 " port 22: Connection refused" )
358 returnVal = main.FALSE
359 elif i == 4: # File Not found
Jon Hall66ce22f2021-06-30 14:57:40 -0700360 hit = True
Jon Hall3c0114c2020-08-11 15:07:42 -0700361 main.log.error( self.name + ": No such file found" )
Jon Hall39570262020-11-17 12:18:19 -0800362 main.log.debug( self.handle.before + self.handle.after )
Jon Hall547e0582015-09-21 17:35:40 -0700363 returnVal = main.FALSE
Jon Hall53c5e662016-04-13 16:06:56 -0700364 elif i == 5: # Permission denied
Jon Hall66ce22f2021-06-30 14:57:40 -0700365 hit = True
Jon Hall3c0114c2020-08-11 15:07:42 -0700366 main.log.error( self.name + ": Permission denied. Check folder permissions" )
Jon Hall39570262020-11-17 12:18:19 -0800367 main.log.debug( self.handle.before + self.handle.after )
Jon Hall53c5e662016-04-13 16:06:56 -0700368 returnVal = main.FALSE
369 elif i == 6: # prompt returned
Jon Hall66ce22f2021-06-30 14:57:40 -0700370 hit = True
Jon Hall669bc862021-03-09 12:24:44 -0800371 timeout = 10
372 main.log.debug( "%s: %s%s" % ( self.name, repr( self.handle.before ), repr( self.handle.after ) ) )
Jon Hall53c5e662016-04-13 16:06:56 -0700373 elif i == 7: # EOF
Jon Hall66ce22f2021-06-30 14:57:40 -0700374 hit = True
Jon Hall3c0114c2020-08-11 15:07:42 -0700375 main.log.error( self.name + ": Pexpect.EOF found!!!" )
Devin Lim44075962017-08-11 10:56:37 -0700376 main.cleanAndExit()
Jon Hall53c5e662016-04-13 16:06:56 -0700377 elif i == 8: # timeout
Jon Hall66ce22f2021-06-30 14:57:40 -0700378 if not hit:
Jon Hall669bc862021-03-09 12:24:44 -0800379 main.log.error(
380 "No route to the Host " +
381 userName +
382 "@" +
383 ipAddress )
384 return returnVal
385 self.handle.expect( [ self.prompt, pexpect.TIMEOUT ], timeout=5 )
386 main.log.debug( "%s: %s%s" % ( self.name, repr( self.handle.before ), repr( self.handle.after ) ) )
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700387 return returnVal
388
Jon Hall22a3bcf2021-07-23 11:40:11 -0700389 def scp( self, remoteHost, filePath, dstPath, direction="from", options="", timeout=120 ):
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700390 """
391 Definition:
392 Execute scp command in linux to copy to/from a remote host
393 Required:
394 * remoteHost - Test ON component to be parsed
395 str filePath - File path including the file it self
396 str dstPath - Destination path
397 Optional:
398 str direction - Direction of the scp, default to "from" which means
399 copy "from" the remote machine to local machine,
400 while "to" means copy "to" the remote machine from
401 local machine
402 """
Jon Hall06fd0df2021-01-25 15:50:06 -0800403 jump_host = main.componentDictionary[ remoteHost.name ].get( 'jump_host' )
404 if jump_host:
405 jump_host = main.componentDictionary.get( jump_host )
Jon Hall669bc862021-03-09 12:24:44 -0800406 options += " -o 'ProxyJump %s@%s' " % ( jump_host.get( 'user' ), jump_host.get( 'host' ) )
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700407 return self.secureCopy( remoteHost.user_name,
408 remoteHost.ip_address,
409 filePath,
410 dstPath,
411 pwd=remoteHost.pwd,
Jon Hall39570262020-11-17 12:18:19 -0800412 direction=direction,
Jon Hall22a3bcf2021-07-23 11:40:11 -0700413 options=options,
414 timeout=timeout )
Devin Lim142b5342017-07-20 15:22:39 -0700415
416 def sshToNode( self, ipAddress, uName="sdn", pwd="rocks" ):
417 ssh_newkey = 'Are you sure you want to continue connecting'
418 refused = "ssh: connect to host " + ipAddress + " port 22: Connection refused"
419 handle = pexpect.spawn( 'ssh -X ' +
420 uName +
421 '@' +
422 ipAddress,
Jon Hall6c9e2da2018-11-06 12:01:23 -0800423 env={ "TERM": "vt100" },
Devin Lim142b5342017-07-20 15:22:39 -0700424 maxread=1000000,
425 timeout=60 )
426
427 # set tty window size
428 handle.setwinsize( 24, 250 )
429
430 i = 5
431 while i == 5:
Jon Hall4173b242017-09-12 17:04:38 -0700432 i = handle.expect( [ ssh_newkey,
433 'password:|Password:',
434 pexpect.EOF,
435 pexpect.TIMEOUT,
436 refused,
437 'teston>',
438 self.prompt ],
439 120 )
Devin Lim142b5342017-07-20 15:22:39 -0700440 if i == 0: # Accept key, then expect either a password prompt or access
Jon Hall3c0114c2020-08-11 15:07:42 -0700441 main.log.info( self.name + ": ssh key confirmation received, send yes" )
Devin Lim142b5342017-07-20 15:22:39 -0700442 handle.sendline( 'yes' )
443 i = 5 # Run the loop again
444 continue
445 if i == 1: # Password required
446 if pwd:
447 main.log.info(
Jon Hall4173b242017-09-12 17:04:38 -0700448 "ssh connection asked for password, gave password" )
Devin Lim142b5342017-07-20 15:22:39 -0700449 else:
Jon Hall3c0114c2020-08-11 15:07:42 -0700450 main.log.info( self.name + ": Server asked for password, but none was "
Devin Lim142b5342017-07-20 15:22:39 -0700451 "given in the .topo file. Trying "
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700452 "no password." )
Devin Lim142b5342017-07-20 15:22:39 -0700453 pwd = ""
454 handle.sendline( pwd )
455 j = handle.expect( [ self.prompt,
456 'password:|Password:',
457 pexpect.EOF,
458 pexpect.TIMEOUT ],
459 120 )
460 if j != 0:
Jon Hall3c0114c2020-08-11 15:07:42 -0700461 main.log.error( self.name + ": Incorrect Password" )
Devin Lim44075962017-08-11 10:56:37 -0700462 main.cleanAndExit()
Devin Lim142b5342017-07-20 15:22:39 -0700463 elif i == 2:
Jon Hall3c0114c2020-08-11 15:07:42 -0700464 main.log.error( self.name + ": Connection timeout" )
Devin Lim44075962017-08-11 10:56:37 -0700465 main.cleanAndExit()
Devin Lim142b5342017-07-20 15:22:39 -0700466 elif i == 3: # timeout
467 main.log.error(
468 "No route to the Host " +
469 uName +
470 "@" +
471 ipAddress )
Devin Lim44075962017-08-11 10:56:37 -0700472 main.cleanAndExit()
Devin Lim142b5342017-07-20 15:22:39 -0700473 elif i == 4:
474 main.log.error(
475 "ssh: connect to host " +
476 ipAddress +
477 " port 22: Connection refused" )
Devin Lim44075962017-08-11 10:56:37 -0700478 main.cleanAndExit()
Devin Lim142b5342017-07-20 15:22:39 -0700479 elif i == 6:
Jon Hall3c0114c2020-08-11 15:07:42 -0700480 main.log.info( self.name + ": Password not required logged in" )
Devin Lim142b5342017-07-20 15:22:39 -0700481
482 handle.sendline( "" )
483 handle.expect( self.prompt )
484 handle.sendline( "cd" )
485 handle.expect( self.prompt )
486
Jon Hall3c0114c2020-08-11 15:07:42 -0700487 main.log.info( self.name + ": Successfully ssh to " + ipAddress + "." )
Devin Lim142b5342017-07-20 15:22:39 -0700488 return handle
489
490 def exitFromSsh( self, handle, ipAddress ):
Devin Lim142b5342017-07-20 15:22:39 -0700491 try:
Jon Hall4f360bc2017-09-07 10:19:52 -0700492 handle.sendline( "logout" )
Devin Lim142b5342017-07-20 15:22:39 -0700493 handle.expect( "closed." )
Jon Hall3c0114c2020-08-11 15:07:42 -0700494 main.log.info( self.name + ": Successfully closed ssh connection from " + ipAddress )
Devin Lim142b5342017-07-20 15:22:39 -0700495 except pexpect.EOF:
Jon Hall3c0114c2020-08-11 15:07:42 -0700496 main.log.error( self.name + ": Failed to close the connection from " + ipAddress )
Jon Hall4f360bc2017-09-07 10:19:52 -0700497 try:
498 # check that this component handle still works
499 self.handle.sendline( "" )
500 self.handle.expect( self.prompt )
501 except pexpect.EOF:
502 main.log.error( self.handle.before )
Jon Hall3c0114c2020-08-11 15:07:42 -0700503 main.log.error( self.name + ": EOF after closing ssh connection" )
Jon Hall4173b242017-09-12 17:04:38 -0700504
505 def folderSize( self, path, size='10', unit='M', ignoreRoot=True ):
506 """
507 Run `du -h` on the folder path and verifies the folder(s) size is
508 less than the given size. Note that if multiple subdirectories are
509 present, the result will be the OR of all the individual subdirectories.
510
511 Arguments:
512 path - A string containing the path supplied to the du command
513 size - The number portion of the file size that the results will be compared to
514 unit - The unit portion of the file size that the results will be compared to
515 ignoreRoot - If True, will ignore the "root" of the path supplied to du. I.E. will ignore `.`
516
517 Returns True if the folder(s) size(s) are less than SIZE UNITS, else returns False
518 """
519 sizeRe = r'(?P<number>\d+\.*\d*)(?P<unit>\D)'
520 unitsList = [ 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y' ]
521 try:
522 # make sure we convert units if size is too big
523 size = float( size )
524 if size >= 1000:
525 size = size / 1000
526 unit = unitsList[ unitsList.index( unit + 1 ) ]
527 cmdStr = "du -h " + path
528 self.handle.sendline( cmdStr )
529 self.handle.expect( self.prompt )
530 output = self.handle.before
531 assert "cannot access" not in output
532 assert "command not found" not in output
533 main.log.debug( output )
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700534 lines = [ line for line in output.split( '\r\n' ) ]
Jon Hall4173b242017-09-12 17:04:38 -0700535 retValue = True
536 if ignoreRoot:
537 lastIndex = -2
538 else:
539 lastIndex = -1
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700540 for line in lines[ 1:lastIndex ]:
Jon Hall4173b242017-09-12 17:04:38 -0700541 parsed = line.split()
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700542 sizeMatch = parsed[ 0 ]
543 folder = parsed[ 1 ]
Jon Hall4173b242017-09-12 17:04:38 -0700544 match = re.search( sizeRe, sizeMatch )
545 num = match.group( 'number' )
546 unitMatch = match.group( 'unit' )
547 if unitsList.index( unitMatch ) < unitsList.index( unit ):
548 retValue &= True
549 elif unitsList.index( unitMatch ) == unitsList.index( unit ):
550 if float( num ) < float( size ):
551 retValue &= True
552 else:
553 retValue &= False
554 elif unitsList.index( unitMatch ) > unitsList.index( unit ):
555 retValue &= False
556 return retValue
557 except AssertionError:
558 main.log.error( self.name + ": Could not execute command: " + output )
559 return False
Jon Hall43060f62020-06-23 13:13:33 -0700560 except ValueError as e:
561 main.log.error( self.name + ": Error parsing output: " + output )
562 main.log.error( e )
563 return False
Jon Hall4173b242017-09-12 17:04:38 -0700564 except pexpect.TIMEOUT:
565 main.log.exception( self.name + ": TIMEOUT exception found" )
566 main.log.error( self.name + ": " + self.handle.before )
567 return False
568 except pexpect.EOF:
569 main.log.error( self.name + ": EOF exception found" )
570 main.log.error( self.name + ": " + self.handle.before )
571 main.cleanAndExit()
Jon Hall0e240372018-05-02 11:21:57 -0700572
Jon Hall22a3bcf2021-07-23 11:40:11 -0700573 def fileSize( self, path, inBytes=True ):
574 """
575 Run `du` on the file path and returns the file size
576
577 Arguments:
578 path - A string containing the path supplied to the du command
579 Optional Arguments:
580 inBytes - Display size in bytes, defaults to true
581
582 Returns the size of the file as an int
583 """
584 sizeRe = r'(?P<number>\d+\.*\d*)(?P<unit>\D)'
585 try:
586 cmdStr = "du %s %s" % ( "-b" if inBytes else "", path )
587 self.handle.sendline( cmdStr )
588 self.handle.expect( self.prompt )
589 output = self.handle.before
590 assert "cannot access" not in output
591 assert "command not found" not in output
592 assert "No such file or directory" not in output
593 main.log.debug( output )
594 lines = [ line for line in output.split( '\r\n' ) ]
595 return int( lines[1].split()[0] )
596 except AssertionError:
597 main.log.error( self.name + ": Could not execute command: " + output )
598 return False
599 except ValueError as e:
600 main.log.error( self.name + ": Error parsing output: " + output )
601 main.log.error( e )
602 return False
603 except pexpect.TIMEOUT:
604 main.log.exception( self.name + ": TIMEOUT exception found" )
605 main.log.error( self.name + ": " + self.handle.before )
606 return False
607 except pexpect.EOF:
608 main.log.error( self.name + ": EOF exception found" )
609 main.log.error( self.name + ": " + self.handle.before )
610 main.cleanAndExit()
611
Jon Hall0e240372018-05-02 11:21:57 -0700612 def setEnv( self, variable, value=None ):
613 """
614 Sets the environment variable to the given value for the current shell session.
615 If value is None, will unset the variable.
616
617 Required Arguments:
618 variable - The name of the environment variable to set.
619
620 Optional Arguments:
621 value - The value to set the variable to. ( Defaults to None, which unsets the variable )
622
623 Returns True if no errors are detected else returns False
624 """
625 try:
626 if value:
627 cmd = "export {}={}".format( variable, value )
628 else:
629 cmd = "unset {}".format( variable )
630 self.handle.sendline( cmd )
631 self.handle.expect( self.prompt )
Jon Hall3c0114c2020-08-11 15:07:42 -0700632 output = self.handle.before
633 main.log.debug( output )
Jon Hall0e240372018-05-02 11:21:57 -0700634 return True
635 except AssertionError:
636 main.log.error( self.name + ": Could not execute command: " + output )
637 return False
638 except pexpect.TIMEOUT:
639 main.log.exception( self.name + ": TIMEOUT exception found" )
640 main.log.error( self.name + ": " + self.handle.before )
641 return False
642 except pexpect.EOF:
643 main.log.error( self.name + ": EOF exception found" )
644 main.log.error( self.name + ": " + self.handle.before )
645 main.cleanAndExit()
You Wangb65d2372018-08-17 15:37:59 -0700646
647 def exitFromCmd( self, expect, retry=10 ):
648 """
649 Call this function when sending ctrl+c is required to kill the current
650 command. It will retry multiple times until the running command is
651 completely killed and expected string is returned from the handle.
652 Required:
You Wangd4fae5c2018-08-22 13:56:49 -0700653 expect: expected string or list of strings which indicates that the
654 previous command was killed successfully.
You Wangb65d2372018-08-17 15:37:59 -0700655 Optional:
656 retry: maximum number of ctrl+c that will be sent.
657 """
You Wangd4fae5c2018-08-22 13:56:49 -0700658 expect = [ expect ] if isinstance( expect, str ) else expect
You Wangb65d2372018-08-17 15:37:59 -0700659 try:
660 while retry >= 0:
661 main.log.debug( self.name + ": sending ctrl+c to kill the command" )
662 self.handle.send( "\x03" )
You Wangd4fae5c2018-08-22 13:56:49 -0700663 i = self.handle.expect( expect + [ pexpect.TIMEOUT ], timeout=3 )
You Wangb65d2372018-08-17 15:37:59 -0700664 main.log.debug( self.handle.before )
You Wangd4fae5c2018-08-22 13:56:49 -0700665 if i < len( expect ):
You Wangb65d2372018-08-17 15:37:59 -0700666 main.log.debug( self.name + ": successfully killed the command" )
667 return main.TRUE
668 retry -= 1
669 main.log.warn( self.name + ": failed to kill the command" )
670 return main.FALSE
671 except pexpect.EOF:
672 main.log.error( self.name + ": EOF exception found" )
673 main.log.error( self.name + ": " + self.handle.before )
674 return main.FALSE
675 except Exception:
676 main.log.exception( self.name + ": Uncaught exception!" )
677 return main.FALSE
Jon Hall43060f62020-06-23 13:13:33 -0700678
679 def cleanOutput( self, output, debug=False ):
680 """
681 Clean ANSI characters from output
682 """
683 ansiEscape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
684 cleaned = ansiEscape.sub( '', output )
685 if debug:
686 main.log.debug( self.name + ": cleanOutput:" )
687 main.log.debug( self.name + ": " + repr( cleaned ) )
688 return cleaned
Jon Hall3c0114c2020-08-11 15:07:42 -0700689
690 def dockerPull( self, image, tag=None ):
691 """
692 Pull a docker image from a registry
693 """
694 try:
695 imgStr = "%s%s" % ( image, ":%s" % tag if tag else "" )
696 cmdStr = "docker pull %s" % imgStr
697 main.log.info( self.name + ": sending: " + cmdStr )
698 self.handle.sendline( cmdStr)
699 i = self.handle.expect( [ self.prompt,
700 "Error response from daemon",
701 pexpect.TIMEOUT ], 120 )
702 if i == 0:
703 return main.TRUE
704 else:
705 main.log.error( self.name + ": Error pulling docker image " + imgStr )
706 output = self.handle.before + str( self.handle.after )
707 if i == 1:
708 self.handle.expect( self.prompt )
709 output += self.handle.before + str( self.handle.after )
710 main.log.debug( self.name + ": " + output )
711 return main.FALSE
712 except pexpect.EOF:
713 main.log.error( self.name + ": EOF exception found" )
714 main.log.error( self.name + ": " + self.handle.before )
715 return main.FALSE
716 except Exception:
717 main.log.exception( self.name + ": Uncaught exception!" )
718 return main.FALSE
719
720 def dockerBuild( self, path, imageTag, pull=False, options="", timeout=600 ):
721 """
722 Build a docker image
723 Required Arguments:
724 - path: Path to the dockerfile, it is recommended to avoid relative paths
725 - imageTag: Give a tag to the built docker image
726 Optional Arguments:
727 - pull: Whether to attempt to pull latest images before building
728 - options: A string containing any addition optional arguments
729 for the docker build command
730 - timeout: How many seconds to wait for the build to complete
731 """
732 try:
733 response = main.TRUE
734 if pull:
735 options = "--pull " + options
736 cmdStr = "docker build -t %s %s %s" % ( imageTag, options, path )
737 main.log.info( self.name + ": sending: " + cmdStr )
738 self.handle.sendline( cmdStr)
739 i = self.handle.expect( [ "Successfully built",
740 "Error response from daemon",
741 pexpect.TIMEOUT ], timeout=timeout )
742 output = self.handle.before
743 if i == 0:
744 output += self.handle.after
745 self.handle.expect( self.prompt )
746 output += self.handle.before + self.handle.after
747 return response
748 elif i == 1:
749 response = main.FALSE
750 output += self.handle.after
751 self.handle.expect( self.prompt )
752 output += self.handle.before + self.handle.after
753 elif i == 2:
754 response = main.FALSE
755 main.log.error( self.name + ": Error building docker image" )
756 main.log.debug( self.name + ": " + output )
757 return response
758 except pexpect.EOF:
759 main.log.error( self.name + ": EOF exception found" )
760 main.log.error( self.name + ": " + self.handle.before )
761 return main.FALSE
762 except Exception:
763 main.log.exception( self.name + ": Uncaught exception!" )
764 return main.FALSE
765
766 def dockerStop( self, containerName ):
767 """
768 Stop a docker container
769 Required Arguments:
770 - containerName: Name of the container to stop
771 """
772 try:
773 cmdStr = "docker stop %s" % ( containerName )
774 main.log.info( self.name + ": sending: " + cmdStr )
775 self.handle.sendline( cmdStr)
776 i = self.handle.expect( [ self.prompt,
777 "Error response from daemon",
778 pexpect.TIMEOUT ], 120 )
779 output = self.handle.before
780 if i == 0:
781 return main.TRUE
782 elif i == 1:
783 output += self.handle.after
784 self.handle.expect( self.prompt )
785 output += self.handle.before
786 elif i == 2:
787 pass
788 main.log.debug( "%s: %s" % ( self.name, output ) )
789 if "No such container" in output:
790 return main.TRUE
791 main.log.error( self.name + ": Error stopping docker image" )
792 main.log.debug( self.name + ": " + output )
793 return main.FALSE
794 except pexpect.EOF:
795 main.log.error( self.name + ": EOF exception found" )
796 main.log.error( self.name + ": " + self.handle.before )
797 return main.FALSE
798 except Exception:
799 main.log.exception( self.name + ": Uncaught exception!" )
800 return main.FALSE
801
Jon Hall06fd0df2021-01-25 15:50:06 -0800802 def dockerRun( self, image, containerName, options="", imageArgs="", background=False ):
Jon Hall3c0114c2020-08-11 15:07:42 -0700803 """
804 Run a docker image
805 Required Arguments:
806 - containerName: Give a name to the container once its started
807 - image: Run the given image
808 Optional Arguments:
809 - options: A string containing any addition optional arguments
810 for the docker run command
811 - imageArgs: A string containing command line arguments for the
812 command run by docker
813 """
814 try:
815 cmdStr = "docker run --name %s %s %s %s" % ( containerName,
816 options if options else "",
817 image,
818 imageArgs )
Jon Hall06fd0df2021-01-25 15:50:06 -0800819 if background:
820 cmdStr += " &"
Jon Hall3c0114c2020-08-11 15:07:42 -0700821 main.log.info( self.name + ": sending: " + cmdStr )
822 self.handle.sendline( cmdStr)
823 i = self.handle.expect( [ self.prompt,
824 "Error response from daemon",
825 pexpect.TIMEOUT ], 120 )
826 if i == 0:
827 return main.TRUE
828 else:
829 output = self.handle.before
830 main.log.debug( self.name + ": " + output )
831 main.log.error( self.name + ": Error running docker image" )
832 if i == 1:
833 output += self.handle.after
834 self.handle.expect( self.prompt )
835 output += self.handle.before + self.handle.after
836 main.log.debug( self.name + ": " + output )
837 return main.FALSE
838 except pexpect.EOF:
839 main.log.error( self.name + ": EOF exception found" )
840 main.log.error( self.name + ": " + self.handle.before )
841 return main.FALSE
842 except Exception:
843 main.log.exception( self.name + ": Uncaught exception!" )
844 return main.FALSE
845
846 def dockerAttach( self, containerName, dockerPrompt="" ):
847 """
848 Attach to a docker image
849 Required Arguments:
850 - containerName: The name of the container to attach to
851 Optional Arguments:
852 - dockerPrompt: a regex for matching the docker shell prompt
853 """
854 try:
855 if dockerPrompt:
856 self.dockerPrompt = dockerPrompt
857 cmdStr = "docker attach %s" % containerName
858 main.log.info( self.name + ": sending: " + cmdStr )
859 self.handle.sendline( cmdStr)
860 i = self.handle.expect( [ self.dockerPrompt,
861 "Error response from daemon",
862 pexpect.TIMEOUT ] )
863 if i == 0:
864 self.inDocker = True
865 return main.TRUE
866 else:
867 main.log.error( self.name + ": Error connecting to docker container" )
868 output = self.handle.before + str( self.handle.after )
869 if i == 1:
870 self.handle.expect( self.prompt )
871 output += self.handle.before + str( self.handle.after )
872 main.log.debug( self.name + ": " + output )
873 return main.FALSE
874 except pexpect.EOF:
875 main.log.error( self.name + ": EOF exception found" )
876 main.log.error( self.name + ": " + self.handle.before )
877 return main.FALSE
878 except AttributeError as e:
879 main.log.exception( self.name + ": AttributeError - " + str( e ) )
880 main.log.warn( self.name + ": Make sure dockerPrompt is set" )
881 main.cleanup()
882 main.exit()
883 except Exception:
884 main.log.exception( self.name + ": Uncaught exception!" )
885 return main.FALSE
886
887 def dockerExec( self, containerName, command="/bin/bash", options="-it", dockerPrompt="" ):
888 """
889 Attach to a docker image
890 Required Arguments:
891 - containerName: The name of the container to attach to
892 Optional Arguments:
893 - command: Command to run in the docker container
894 - options: Docker exec options
895 - dockerPrompt: a regex for matching the docker shell prompt
896 """
897 try:
898 if dockerPrompt:
899 self.dockerPrompt = dockerPrompt
900 cmdStr = "docker exec %s %s %s" % ( options, containerName, command )
901 main.log.info( self.name + ": sending: " + cmdStr )
902 self.handle.sendline( cmdStr)
903 i = self.handle.expect( [ self.dockerPrompt,
904 "Error response from daemon",
905 pexpect.TIMEOUT ] )
906 if i == 0:
907 self.inDocker = True
908 return main.TRUE
909 else:
910 main.log.error( self.name + ": Error connecting to docker container" )
911 output = self.handle.before + str( self.handle.after )
912 if i == 1:
913 self.handle.expect( self.prompt )
914 output += self.handle.before + str( self.handle.after )
915 main.log.debug( self.name + ": " + output )
916 return main.FALSE
917 except pexpect.EOF:
918 main.log.error( self.name + ": EOF exception found" )
919 main.log.error( self.name + ": " + self.handle.before )
920 return main.FALSE
921 except AttributeError as e:
922 main.log.exception( self.name + ": AttributeError - " + str( e ) )
923 main.log.warn( self.name + ": Make sure dockerPrompt is set" )
924 main.cleanup()
925 main.exit()
926 except Exception:
927 main.log.exception( self.name + ": Uncaught exception!" )
928 return main.FALSE
929
930 def dockerCp( self, containerName, dockerPath, hostPath, direction="from" ):
931 """
932 Copy a file from/to a docker container to the host
933 Required Arguments:
934 - containerName: The name of the container to copy from/to
935 - dockerPath: the path in the container to copy from/to
936 - hostPath: the path on the host to copy to/from
937 Optional Arguments:
938 - direction: Choose whether to copy "from" the container or "to" the container
939 """
940 try:
941 cmdStr = "docker cp "
942 if direction == "from":
943 cmdStr += "%s:%s %s" % ( containerName, dockerPath, hostPath )
944 elif direction == "to":
945 cmdStr += "%s %s:%s" % ( hostPath, containerName, dockerPath )
946 main.log.info( self.name + ": sending: " + cmdStr )
947 self.handle.sendline( cmdStr)
948 i = self.handle.expect( [ self.prompt,
949 "Error",
950 pexpect.TIMEOUT ] )
951 if i == 0:
952 retValue = main.TRUE
953 else:
954 main.log.error( self.name + ": Error in docker cp" )
955 output = self.handle.before + str( self.handle.after )
956 if i == 1:
957 self.handle.expect( self.prompt )
958 output += self.handle.before + str( self.handle.after )
959 main.log.debug( self.name + ": " + output )
960 retValue = main.FALSE
961 return retValue
962 except pexpect.EOF:
963 main.log.error( self.name + ": EOF exception found" )
964 main.log.error( self.name + ": " + self.handle.before )
965 return main.FALSE
966 except AttributeError as e:
967 main.log.exception( self.name + ": AttributeError - " + str( e ) )
968 main.log.warn( self.name + ": Make sure dockerPrompt is set" )
969 main.cleanup()
970 main.exit()
971 except Exception:
972 main.log.exception( self.name + ": Uncaught exception!" )
973 return main.FALSE
974
975 def dockerDisconnect( self ):
976 """
977 Send ctrl-c, ctrl-d to session, which should close and exit the
978 attached docker session. This will likely exit the running program
979 in the container and also stop the container.
980 """
981 try:
982 cmdStr = "\x03"
983 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
984 self.handle.send( cmdStr)
985 cmdStr = "\x04"
986 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
987 self.handle.send( cmdStr)
988 i = self.handle.expect( [ self.prompt, pexpect.TIMEOUT ] )
989 if i == 0:
990 self.inDocker = False
991 return main.TRUE
992 else:
993 main.log.error( self.name + ": Error disconnecting from docker image" )
994 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
995 return main.FALSE
996 except pexpect.EOF:
997 main.log.error( self.name + ": EOF exception found" )
998 main.log.error( self.name + ": " + self.handle.before )
999 return main.FALSE
1000 except Exception:
1001 main.log.exception( self.name + ": Uncaught exception!" )
1002 return main.FALSE
Jon Hall06fd0df2021-01-25 15:50:06 -08001003
1004# TODO: How is this different from exitFromCmd used elsewhere?
1005 def exitFromProcess( self ):
1006 """
1007 Send ctrl-c, which should close and exit the program
1008 """
1009 try:
1010 cmdStr = "\x03"
1011 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1012 self.handle.send( cmdStr)
1013 i = self.handle.expect( [ self.prompt, pexpect.TIMEOUT ] )
1014 if i == 0:
1015 return main.TRUE
1016 else:
1017 main.log.error( self.name + ": Error exiting process" )
1018 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1019 return main.FALSE
1020 except pexpect.EOF:
1021 main.log.error( self.name + ": EOF exception found" )
1022 main.log.error( self.name + ": " + self.handle.before )
1023 return main.FALSE
1024 except Exception:
1025 main.log.exception( self.name + ": Uncaught exception!" )
1026 return main.FALSE
1027
1028 def preDisconnect( self ):
1029 """
1030 A Stub for a function that will be called before disconnect.
1031 This can be set if for instance, the shell is running a program
1032 and needs to exit the program before disconnecting from the component
1033 """
1034 print "preDisconnect"
1035 return main.TRUE
1036
Jon Hall22a3bcf2021-07-23 11:40:11 -07001037 def kubectlGetPodNames( self, kubeconfig=None, namespace=None, app=None, name=None,
1038 nodeName=None, status=None ):
Jon Hall06fd0df2021-01-25 15:50:06 -08001039 """
1040 Use kubectl to get the names of pods
1041 Optional Arguments:
1042 - kubeconfig: The path to a kubeconfig file
1043 - namespace: The namespace to search in
1044 - app: Get pods belonging to a specific app
1045 - name: Get pods with a specific name label
Jon Hallbe3a2ac2021-03-15 12:28:06 -07001046 - nodeName: Get pods on a specific node
Jon Hall22a3bcf2021-07-23 11:40:11 -07001047 - status: Get pods with the specified Status
Jon Hall06fd0df2021-01-25 15:50:06 -08001048 Returns a list containing the names of the pods or
1049 main.FALSE on Error
1050 """
1051
1052 try:
Jon Hall22a3bcf2021-07-23 11:40:11 -07001053 self.handle.sendline( "" )
1054 self.handle.expect( self.prompt )
1055 main.log.debug( self.handle.before + self.handle.after )
1056 cmdStr = "kubectl %s %s get pods %s %s %s %s --output=jsonpath='{.items..metadata.name}{\"\\n\"}'" % (
Jon Hall06fd0df2021-01-25 15:50:06 -08001057 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1058 "-n %s" % namespace if namespace else "",
1059 "-l app=%s" % app if app else "",
Jon Hallbe3a2ac2021-03-15 12:28:06 -07001060 "-l name=%s" % name if name else "",
Jon Hall22a3bcf2021-07-23 11:40:11 -07001061 "--field-selector=spec.nodeName=%s" % nodeName if nodeName else "",
1062 "--field-selector=status.phase=%s" % status if status else "" )
Jon Hall06fd0df2021-01-25 15:50:06 -08001063 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1064 self.handle.sendline( cmdStr )
Jon Hall2941fce2021-04-13 10:38:23 -07001065 i = self.handle.expect( [ "not found", "error", "The connection to the server", "Unable to find", "No resources found", self.prompt ] )
1066 if i == 4:
1067 # Command worked, but returned no pods
1068 output = self.handle.before + self.handle.after
1069 main.log.warn( self.name + ": " + output )
1070 return []
1071 elif i == 5:
1072 # Command returned pods
Jon Hall06fd0df2021-01-25 15:50:06 -08001073 output = self.handle.before + self.handle.after
1074 names = output.split( '\r\n' )[1].split()
1075 return names
1076 else:
Jon Hall2941fce2021-04-13 10:38:23 -07001077 # Some error occured
Jon Hall06fd0df2021-01-25 15:50:06 -08001078 main.log.error( self.name + ": Error executing command" )
1079 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1080 return main.FALSE
1081 except pexpect.EOF:
1082 main.log.error( self.name + ": EOF exception found" )
1083 main.log.error( self.name + ": " + self.handle.before )
1084 return main.FALSE
1085 except pexpect.TIMEOUT:
1086 main.log.exception( self.name + ": TIMEOUT exception found" )
1087 main.log.error( self.name + ": " + self.handle.before )
1088 return main.FALSE
1089 except Exception:
1090 main.log.exception( self.name + ": Uncaught exception!" )
1091 return main.FALSE
1092
1093 def kubectlDescribe( self, describeString, dstPath, kubeconfig=None, namespace=None ):
1094 """
1095 Use kubectl to get the logs from a pod
1096 Required Arguments:
1097 - describeString: The string passed to the cli. Example: "pods"
1098 - dstPath: The location to save the logs to
1099 Optional Arguments:
1100 - kubeconfig: The path to a kubeconfig file
1101 - namespace: The namespace to search in
1102 Returns main.TRUE or
1103 main.FALSE on Error
1104 """
1105
1106 try:
1107 cmdStr = "kubectl %s %s describe %s > %s " % (
1108 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1109 "-n %s" % namespace if namespace else "",
1110 describeString,
1111 dstPath )
1112 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1113 self.handle.sendline( cmdStr )
1114 i = self.handle.expect( [ "not found", "error", "The connection to the server", self.prompt ] )
1115 if i == 3:
1116 main.log.debug( self.name + ": " + self.handle.before )
1117 return main.TRUE
1118 else:
1119 main.log.error( self.name + ": Error executing command" )
1120 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1121 return main.FALSE
1122 except pexpect.EOF:
1123 main.log.error( self.name + ": EOF exception found" )
1124 main.log.error( self.name + ": " + self.handle.before )
1125 return main.FALSE
1126 except pexpect.TIMEOUT:
1127 main.log.exception( self.name + ": TIMEOUT exception found" )
1128 main.log.error( self.name + ": " + self.handle.before )
1129 return main.FALSE
1130 except Exception:
1131 main.log.exception( self.name + ": Uncaught exception!" )
1132 return main.FALSE
1133
1134 def kubectlPodNodes( self, dstPath=None, kubeconfig=None, namespace=None ):
1135 """
Jon Halla16b4db2021-10-20 14:11:59 -07001136 Use kubectl to get the pod to node mappings
Jon Hall06fd0df2021-01-25 15:50:06 -08001137 Optional Arguments:
1138 - dstPath: The location to save the logs to
1139 - kubeconfig: The path to a kubeconfig file
1140 - namespace: The namespace to search in
1141 Returns main.TRUE if dstPath is given, else the output of the command or
1142 main.FALSE on Error
1143 """
Jon Hall06fd0df2021-01-25 15:50:06 -08001144 try:
Jon Hall22a3bcf2021-07-23 11:40:11 -07001145 self.handle.sendline( "" )
1146 self.handle.expect( self.prompt )
1147 main.log.debug( self.handle.before + self.handle.after )
Jon Hall50a00012021-03-08 11:06:11 -08001148 cmdStr = "kubectl %s %s get pods -o wide %s " % (
Jon Hall06fd0df2021-01-25 15:50:06 -08001149 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1150 "-n %s" % namespace if namespace else "",
1151 " > %s" % dstPath if dstPath else "" )
1152 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1153 self.handle.sendline( cmdStr )
1154 i = self.handle.expect( [ "not found", "error", "The connection to the server", self.prompt ] )
1155 if i == 3:
1156 output = self.handle.before
1157 main.log.debug( self.name + ": " + output )
1158 return output if dstPath else main.TRUE
1159 else:
1160 main.log.error( self.name + ": Error executing command" )
1161 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1162 return main.FALSE
1163 except pexpect.EOF:
1164 main.log.error( self.name + ": EOF exception found" )
1165 main.log.error( self.name + ": " + self.handle.before )
1166 return main.FALSE
1167 except pexpect.TIMEOUT:
1168 main.log.exception( self.name + ": TIMEOUT exception found" )
1169 main.log.error( self.name + ": " + self.handle.before )
1170 return main.FALSE
1171 except Exception:
1172 main.log.exception( self.name + ": Uncaught exception!" )
1173 return main.FALSE
1174
Jon Halla16b4db2021-10-20 14:11:59 -07001175 def kubectlGetPodNode( self, podName, kubeconfig=None, namespace=None ):
1176 """
1177 Use kubectl to get the node a given pod is running on
1178 Arguments:
1179 - podName: The name of the pod
1180 Optional Arguments:
1181 - kubeconfig: The path to a kubeconfig file
1182 - namespace: The namespace to search in
1183 Returns a string of the node name or None
1184 """
1185 try:
1186 self.handle.sendline( "" )
1187 self.handle.expect( self.prompt )
1188 main.log.debug( self.handle.before + self.handle.after )
1189 cmdStr = "kubectl %s %s get pods %s --output=jsonpath='{.spec.nodeName}{\"\\n\"}'" % (
1190 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1191 "-n %s" % namespace if namespace else "",
1192 podName )
1193 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1194 self.handle.sendline( cmdStr )
1195 i = self.handle.expect( [ "not found", "error", "The connection to the server", self.prompt ] )
1196 if i == 3:
1197 output = self.handle.before
1198 main.log.debug( self.name + ": " + output )
1199 output = output.splitlines()
1200 main.log.warn( output )
1201 return output[1] if len( output ) == 3 else None
1202 else:
1203 main.log.error( self.name + ": Error executing command" )
1204 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1205 return None
1206 except pexpect.EOF:
1207 main.log.error( self.name + ": EOF exception found" )
1208 main.log.error( self.name + ": " + self.handle.before )
1209 return None
1210 except pexpect.TIMEOUT:
1211 main.log.exception( self.name + ": TIMEOUT exception found" )
1212 main.log.error( self.name + ": " + self.handle.before )
1213 return None
1214 except Exception:
1215 main.log.exception( self.name + ": Uncaught exception!" )
1216 return None
1217
Jon Halla7b27e62021-06-29 12:13:51 -07001218 def sternLogs( self, podString, dstPath, kubeconfig=None, namespace=None, since='1h', wait=60 ):
1219 """
1220 Use stern to get the logs from a pod
1221 Required Arguments:
1222 - podString: The name of the pod or partial name of the pods to get the logs of
1223 - dstPath: The location to save the logs to
1224 Optional Arguments:
1225 - kubeconfig: The path to a kubeconfig file
1226 - namespace: The namespace to search in
1227 - since: Return logs newer than a relative duration like 5s, 2m, or 3h. Defaults to 1h
1228 - wait: How long to wait, in seconds, before killing the process. Stern does not currently
1229 support a way to exit if cought up to present time. Defaults to 60 seconds
1230 Returns main.TRUE or
1231 main.FALSE on Error
1232 """
1233 import time
1234 try:
1235 cmdStr = "stern %s %s %s %s > %s " % (
1236 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1237 "-n %s" % namespace if namespace else "",
1238 "--since %s" % since if since else "",
1239 podString,
1240 dstPath )
1241 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1242 self.handle.sendline( cmdStr )
Jon Hall22a3bcf2021-07-23 11:40:11 -07001243 if int( wait ) >= 0:
1244 time.sleep( int( wait ) )
1245 self.handle.send( '\x03' ) # CTRL-C
1246 i = self.handle.expect( [ "not found", "Error: ", "The connection to the server", self.prompt ] )
1247 if i == 3:
1248 main.log.debug( self.name + ": " + self.handle.before )
1249 return main.TRUE
1250 else:
1251 main.log.error( self.name + ": Error executing command" )
1252 response = self.handle.before + str( self.handle.after )
1253 self.handle.expect( [ self.prompt, pexpect.TIMEOUT ], timeout=5 )
1254 response += self.handle.before + str( self.handle.after )
1255 main.log.debug( self.name + ": " + response )
1256 return main.FALSE
Jon Halla7b27e62021-06-29 12:13:51 -07001257 else:
Jon Hall22a3bcf2021-07-23 11:40:11 -07001258 self.preDisconnect = self.exitFromProcess
1259 return main.TRUE
Jon Halla7b27e62021-06-29 12:13:51 -07001260 except pexpect.EOF:
1261 main.log.error( self.name + ": EOF exception found" )
1262 main.log.error( self.name + ": " + self.handle.before )
1263 return main.FALSE
1264 except pexpect.TIMEOUT:
1265 main.log.exception( self.name + ": TIMEOUT exception found" )
1266 main.log.error( self.name + ": " + self.handle.before )
1267 return main.FALSE
1268 except Exception:
1269 main.log.exception( self.name + ": Uncaught exception!" )
1270 return main.FALSE
1271
Jon Hall06fd0df2021-01-25 15:50:06 -08001272 def kubectlLogs( self, podName, dstPath, kubeconfig=None, namespace=None, timeout=240 ):
1273 """
1274 Use kubectl to get the logs from a pod
1275 Required Arguments:
1276 - podName: The name of the pod to get the logs of
1277 - dstPath: The location to save the logs to
1278 Optional Arguments:
1279 - kubeconfig: The path to a kubeconfig file
1280 - namespace: The namespace to search in
1281 - timeout: Timeout for command to return. The longer the logs, the longer it will take to fetch them.
1282 Returns main.TRUE or
1283 main.FALSE on Error
1284 """
1285
1286 try:
1287 cmdStr = "kubectl %s %s logs %s > %s " % (
1288 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1289 "-n %s" % namespace if namespace else "",
1290 podName,
1291 dstPath )
1292 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1293 self.handle.sendline( cmdStr )
1294 i = self.handle.expect( [ "not found", "error", "The connection to the server", self.prompt ], timeout=timeout )
1295 if i == 3:
1296 main.log.debug( self.name + ": " + self.handle.before )
1297 return main.TRUE
1298 else:
1299 main.log.error( self.name + ": Error executing command" )
1300 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1301 return main.FALSE
1302 except pexpect.EOF:
1303 main.log.error( self.name + ": EOF exception found" )
1304 main.log.error( self.name + ": " + self.handle.before )
1305 return main.FALSE
1306 except pexpect.TIMEOUT:
1307 main.log.exception( self.name + ": TIMEOUT exception found" )
1308 main.log.error( self.name + ": " + self.handle.before )
1309 return main.FALSE
1310 except Exception:
1311 main.log.exception( self.name + ": Uncaught exception!" )
1312 return main.FALSE
1313
Jon Hallbe3a2ac2021-03-15 12:28:06 -07001314 def kubectlCp( self, podName, srcPath, dstPath, kubeconfig=None, namespace=None, timeout=240 ):
1315 """
1316 Use kubectl to get a file from a pod
1317 Required Arguments:
1318 - podName: The name of the pod to get the logs of
1319 - srcPath: The file to copy from the pod
1320 - dstPath: The location to save the file to locally
1321 Optional Arguments:
1322 - kubeconfig: The path to a kubeconfig file
1323 - namespace: The namespace to search in
1324 - timeout: Timeout for command to return. The longer the logs, the longer it will take to fetch them.
1325 Returns main.TRUE or
1326 main.FALSE on Error
1327 """
1328
1329 try:
1330 cmdStr = "kubectl %s %s cp %s:%s %s" % (
1331 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1332 "-n %s" % namespace if namespace else "",
1333 podName,
1334 srcPath,
1335 dstPath )
1336 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1337 self.handle.sendline( cmdStr )
1338 i = self.handle.expect( [ "not found", "error", "The connection to the server", self.prompt ], timeout=timeout )
1339 if i == 3:
1340 main.log.debug( self.name + ": " + self.handle.before )
1341 return main.TRUE
1342 else:
Jon Hall22a3bcf2021-07-23 11:40:11 -07001343 output = self.handle.before + str( self.handle.after )
Jon Hallbe3a2ac2021-03-15 12:28:06 -07001344 main.log.error( self.name + ": Error executing command" )
Jon Hall22a3bcf2021-07-23 11:40:11 -07001345 self.handle.expect( [ self.prompt, pexpect.TIMEOUT ] )
1346 output += self.handle.before + str( self.handle.after )
1347 main.log.debug( self.name + ": " + output )
Jon Hallbe3a2ac2021-03-15 12:28:06 -07001348 return main.FALSE
1349 except pexpect.EOF:
1350 main.log.error( self.name + ": EOF exception found" )
1351 main.log.error( self.name + ": " + self.handle.before )
1352 return main.FALSE
1353 except pexpect.TIMEOUT:
1354 main.log.exception( self.name + ": TIMEOUT exception found" )
1355 main.log.error( self.name + ": " + self.handle.before )
1356 return main.FALSE
1357 except Exception:
1358 main.log.exception( self.name + ": Uncaught exception!" )
1359 return main.FALSE
1360
Jon Halla16b4db2021-10-20 14:11:59 -07001361 def kubectlPortForward( self, podName, portsList, kubeconfig=None, namespace=None ):
Jon Hall06fd0df2021-01-25 15:50:06 -08001362 """
1363 Use kubectl to setup port forwarding from the local machine to the kubernetes pod
1364
Jon Halla16b4db2021-10-20 14:11:59 -07001365 Note: This cli command does not return until the port forwarding session is ended.
Jon Hall06fd0df2021-01-25 15:50:06 -08001366
1367 Required Arguments:
1368 - podName: The name of the pod as a string
1369 - portsList: The list of ports to forward, as a string. see kubectl help for details
1370 Optional Arguments:
1371 - kubeconfig: The path to a kubeconfig file
1372 - namespace: The namespace to search in
Jon Halla16b4db2021-10-20 14:11:59 -07001373 Returns main.TRUE if a port-forward session was created or main.FALSE on Error
Jon Hall06fd0df2021-01-25 15:50:06 -08001374
1375
1376 """
1377 try:
1378 cmdStr = "kubectl %s %s port-forward pod/%s %s" % (
1379 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1380 "-n %s" % namespace if namespace else "",
1381 podName,
1382 portsList )
1383 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1384 self.handle.sendline( cmdStr )
Jon Halla16b4db2021-10-20 14:11:59 -07001385 self.handle.expect( "pod/%s" % podName )
1386 output = self.handle.before + self.handle.after
Jon Hall06fd0df2021-01-25 15:50:06 -08001387 i = self.handle.expect( [ "not found", "error", "closed/timedout",
1388 self.prompt, "The connection to the server", "Forwarding from" ] )
Jon Halla16b4db2021-10-20 14:11:59 -07001389 output += self.handle.before + str( self.handle.after )
Jon Hall06fd0df2021-01-25 15:50:06 -08001390 # NOTE: This won't clear the buffer entirely, and each time the port forward
1391 # is used, another line will be added to the buffer. We need to make
1392 # sure we clear the buffer before using this component again.
1393
1394 if i == 5:
1395 # Setup preDisconnect function
1396 self.preDisconnect = self.exitFromProcess
Jon Halla16b4db2021-10-20 14:11:59 -07001397 self.portForwardList = portsList
Jon Hall06fd0df2021-01-25 15:50:06 -08001398 return main.TRUE
1399 else:
1400 main.log.error( self.name + ": Error executing command" )
Jon Halla16b4db2021-10-20 14:11:59 -07001401 main.log.debug( self.name + ": " + output )
Jon Hall06fd0df2021-01-25 15:50:06 -08001402 return main.FALSE
1403 except pexpect.EOF:
1404 main.log.error( self.name + ": EOF exception found" )
1405 main.log.error( self.name + ": " + self.handle.before )
1406 return main.FALSE
1407 except pexpect.TIMEOUT:
1408 main.log.exception( self.name + ": TIMEOUT exception found" )
1409 main.log.error( self.name + ": " + self.handle.before )
1410 return main.FALSE
1411 except Exception:
1412 main.log.exception( self.name + ": Uncaught exception!" )
1413 return main.FALSE
Daniele Moroe1d05eb2021-09-23 19:52:30 +02001414
Jon Halla16b4db2021-10-20 14:11:59 -07001415 def checkPortForward( self, podName, portsList=None, kubeconfig=None, namespace=None ):
1416 """
1417 Check that kubectl port-forward session is still active and restarts it if it was closed.
1418
1419
1420 Required Arguments:
1421 - podName: The name of the pod as a string
1422 - portsList: The list of ports to forward, as a string. see kubectl help for details. Deafults to
1423 the last used string on this node.
1424 Optional Arguments:
1425 - kubeconfig: The path to a kubeconfig file
1426 - namespace: The namespace to search in
1427 Returns main.TRUE if a port-forward session was created or is still active, main.FALSE on Error
1428
1429
1430 """
1431 try:
1432 if not portsList:
1433 portsList = self.portForwardList
1434 self.handle.sendline( "" )
1435 i = self.handle.expect( [ self.prompt, pexpect.TIMEOUT ], timeout=5 )
1436 output = self.handle.before + str( self.handle.after )
1437 main.log.debug( "%s: %s" % ( self.name, output ) )
1438 if i == 0:
1439 # We are not currently in a port-forwarding session, try to re-establish.
1440 return self.kubectlPortForward( podName, portsList, kubeconfig, namespace )
1441 elif i == 1:
1442 # Still in a command, port-forward is probably still active
1443 return main.TRUE
1444 except pexpect.EOF:
1445 main.log.error( self.name + ": EOF exception found" )
1446 main.log.error( self.name + ": " + self.handle.before )
1447 return main.FALSE
1448 except pexpect.TIMEOUT:
1449 main.log.exception( self.name + ": TIMEOUT exception found" )
1450 main.log.error( self.name + ": " + self.handle.before )
1451 return main.FALSE
1452 except Exception:
1453 main.log.exception( self.name + ": Uncaught exception!" )
1454 return main.FALSE
1455
1456 def kubectlCordonNode( self, nodeName, kubeconfig=None, namespace=None, timeout=240, uncordonOnDisconnect=True ):
1457 try:
1458 cmdStr = "kubectl %s %s cordon %s" % (
1459 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1460 "-n %s" % namespace if namespace else "",
1461 nodeName )
1462 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1463 if uncordonOnDisconnect:
1464 self.nodeName = nodeName
1465 if kubeconfig:
1466 self.kubeconfig = kubeconfig
1467 if namespace:
1468 self.namespace = namespace
1469 self.preDisconnect = self.kubectlUncordonNode
1470 self.handle.sendline( cmdStr )
1471 i = self.handle.expect( [ "not found", "error",
1472 "The connection to the server",
1473 "node/%s cordoned" % nodeName,
1474 "node/%s already cordoned" % nodeName, ],
1475 timeout=timeout )
1476 if i == 3 or i == 4:
1477 output = self.handle.before + self.handle.after
1478 main.log.debug( self.name + ": " + output )
1479 self.clearBuffer()
1480 return main.TRUE
1481 else:
1482 main.log.error( self.name + ": Error executing command" )
1483 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1484 self.clearBuffer()
1485 return main.FALSE
1486 except pexpect.EOF:
1487 main.log.error( self.name + ": EOF exception found" )
1488 main.log.error( self.name + ": " + self.handle.before )
1489 return main.FALSE
1490 except pexpect.TIMEOUT:
1491 main.log.exception( self.name + ": TIMEOUT exception found" )
1492 main.log.error( self.name + ": " + self.handle.before )
1493 self.clearBuffer()
1494 return main.FALSE
1495 except Exception:
1496 main.log.exception( self.name + ": Uncaught exception!" )
1497 return main.FALSE
1498
1499 def kubectlUncordonNode( self, nodeName=None, kubeconfig=None, namespace=None, timeout=240 ):
1500 try:
1501 if not nodeName:
1502 nodeName = getattr( self, "nodeName" )
1503 if not kubeconfig:
1504 kubeconfig = getattr( self, "kubeconfig", None )
1505 if not kubeconfig:
1506 namespace = getattr( self, "namespace", None )
1507 cmdStr = "kubectl %s %s uncordon %s" % (
1508 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1509 "-n %s" % namespace if namespace else "",
1510 nodeName )
1511 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1512 self.handle.sendline( cmdStr )
1513 i = self.handle.expect( [ "not found", "error",
1514 "The connection to the server",
1515 "node/%s uncordoned" % nodeName,
1516 "node/%s already uncordoned" % nodeName, ],
1517 timeout=timeout )
1518 if i == 3 or i == 4:
1519 output = self.handle.before + self.handle.after
1520 main.log.debug( self.name + ": " + output )
1521 self.clearBuffer()
1522 return main.TRUE
1523 else:
1524 main.log.error( self.name + ": Error executing command" )
1525 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1526 self.clearBuffer()
1527 return main.FALSE
1528 except pexpect.EOF:
1529 main.log.error( self.name + ": EOF exception found" )
1530 main.log.error( self.name + ": " + self.handle.before )
1531 return main.FALSE
1532 except pexpect.TIMEOUT:
1533 main.log.exception( self.name + ": TIMEOUT exception found" )
1534 main.log.error( self.name + ": " + self.handle.before )
1535 self.clearBuffer()
1536 return main.FALSE
1537 except Exception:
1538 main.log.exception( self.name + ": Uncaught exception!" )
1539 return main.FALSE
1540
Daniele Moroe1d05eb2021-09-23 19:52:30 +02001541 def kubectlDeletePod( self, podName, kubeconfig=None, namespace=None, timeout=240 ):
1542 try:
1543 cmdStr = "kubectl %s %s delete pod %s" % (
1544 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1545 "-n %s" % namespace if namespace else "",
1546 podName )
1547 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1548 self.handle.sendline( cmdStr )
1549 i = self.handle.expect( [ "not found", "error",
1550 "The connection to the server",
1551 self.prompt ],
1552 timeout=timeout )
1553 if i == 3:
1554 main.log.debug( self.name + ": " + self.handle.before )
1555 self.clearBuffer()
1556 return main.TRUE
1557 else:
1558 main.log.error( self.name + ": Error executing command" )
1559 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1560 self.clearBuffer()
1561 return main.FALSE
1562 except pexpect.EOF:
1563 main.log.error( self.name + ": EOF exception found" )
1564 main.log.error( self.name + ": " + self.handle.before )
1565 return main.FALSE
1566 except pexpect.TIMEOUT:
1567 main.log.exception( self.name + ": TIMEOUT exception found" )
1568 main.log.error( self.name + ": " + self.handle.before )
1569 return main.FALSE
1570 except Exception:
1571 main.log.exception( self.name + ": Uncaught exception!" )
1572 return main.FALSE
1573
1574 def kubectlCheckPodReady( self, podName, kubeconfig=None, namespace=None, timeout=240 ):
1575 try:
1576 cmdStr = "kubectl %s %s get pods " \
1577 "-o go-template='{{range $index, $element := .items}}{{range .status.containerStatuses}}{{if .ready}}{{$element.metadata.name}}{{\" ready\\n\"}}{{end}}{{end}}{{end}}' | grep --color=never %s" % (
1578 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1579 "-n %s" % namespace if namespace else "",
1580 podName )
1581 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1582 self.handle.sendline( cmdStr )
1583 # Since the command contains the prompt ($), we first expect for the
1584 # last part of the command and then we expect the actual values
Jon Halla16b4db2021-10-20 14:11:59 -07001585 self.handle.expect( "grep --color=never %s" % podName, timeout=1 )
Daniele Moroe1d05eb2021-09-23 19:52:30 +02001586 i = self.handle.expect( [ podName + " ready",
1587 self.prompt ],
1588 timeout=timeout )
1589 if i == 0:
1590 main.log.debug( self.name + ": " + podName + " ready" )
1591 self.clearBuffer()
1592 return main.TRUE
1593 else:
1594 main.log.error( self.name + ": Error executing command" )
1595 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1596 self.clearBuffer()
1597 return main.FALSE
1598 except pexpect.EOF:
1599 main.log.error( self.name + ": EOF exception found" )
1600 main.log.error( self.name + ": " + self.handle.before )
1601 return main.FALSE
1602 except pexpect.TIMEOUT:
1603 main.log.exception( self.name + ": TIMEOUT exception found" )
1604 main.log.error( self.name + ": " + self.handle.before )
1605 return main.FALSE
1606 except Exception:
1607 main.log.exception( self.name + ": Uncaught exception!" )
1608 return main.FALSE
1609
1610 def clearBuffer(self):
1611 i = 0
1612 response = ''
1613 while True:
1614 try:
1615 i += 1
1616 self.handle.expect( self.prompt, timeout=5 )
1617 response += self.cleanOutput( self.handle.before )
1618 except pexpect.TIMEOUT:
Jon Halla16b4db2021-10-20 14:11:59 -07001619 return response