blob: 1e4d233044f6a549fb22c100a2aee803adeea365 [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 )
Jon Hall7676c662021-11-17 14:32:06 -0800148
149 # disable bracketed paste mode which is enabled by default on newer versions of bash/readline
150 self.handle.sendline( "bind 'set enable-bracketed-paste off'" )
151 self.handle.expect( self.prompt )
152
Jeremy Ronquillo0f2008a2017-06-23 15:32:51 -0700153 self.handle.sendline( "cd" )
154 self.handle.expect( self.prompt )
adminbae64d82013-08-01 10:50:15 -0700155 return self.handle
156
kelvin8ec71442015-01-15 16:57:00 -0800157 def disconnect( self ):
Jon Hall06fd0df2021-01-25 15:50:06 -0800158 result = self.preDisconnect()
kelvin8ec71442015-01-15 16:57:00 -0800159 result = super( CLI, self ).disconnect( self )
adminbae64d82013-08-01 10:50:15 -0700160 result = main.TRUE
Jon Hall3c0114c2020-08-11 15:07:42 -0700161
162 def Prompt( self ):
163 """
164 Returns the prompt to expect depending on what program we are in
165 """
166 return self.prompt if not self.inDocker else self.dockerPrompt
kelvin8ec71442015-01-15 16:57:00 -0800167
168 def execute( self, **execparams ):
169 """
adminbae64d82013-08-01 10:50:15 -0700170 It facilitates the command line execution of a given command. It has arguments as :
171 cmd => represents command to be executed,
172 prompt => represents expect command prompt or output,
173 timeout => timeout for command execution,
174 more => to provide a key press if it is on.
You Wang7d14d642019-01-23 15:10:08 -0800175 logCmd => log the command executed if True
adminbae64d82013-08-01 10:50:15 -0700176
177 It will return output of command exection.
kelvin8ec71442015-01-15 16:57:00 -0800178 """
179 result = super( CLI, self ).execute( self )
adminaef00552014-05-08 09:18:36 -0700180 defaultPrompt = '.*[$>\#]'
Jon Hall3b489db2015-10-05 14:38:37 -0700181 args = utilities.parse_args( [ "CMD",
182 "TIMEOUT",
183 "PROMPT",
You Wang7d14d642019-01-23 15:10:08 -0800184 "MORE",
185 "LOGCMD" ],
Jon Hall3b489db2015-10-05 14:38:37 -0700186 **execparams )
kelvin8ec71442015-01-15 16:57:00 -0800187
188 expectPrompt = args[ "PROMPT" ] if args[ "PROMPT" ] else defaultPrompt
adminbae64d82013-08-01 10:50:15 -0700189 self.LASTRSP = ""
kelvin8ec71442015-01-15 16:57:00 -0800190 timeoutVar = args[ "TIMEOUT" ] if args[ "TIMEOUT" ] else 10
adminbae64d82013-08-01 10:50:15 -0700191 cmd = ''
kelvin8ec71442015-01-15 16:57:00 -0800192 if args[ "CMD" ]:
193 cmd = args[ "CMD" ]
194 else:
adminbae64d82013-08-01 10:50:15 -0700195 return 0
kelvin8ec71442015-01-15 16:57:00 -0800196 if args[ "MORE" ] is None:
197 args[ "MORE" ] = " "
198 self.handle.sendline( cmd )
adminbae64d82013-08-01 10:50:15 -0700199 self.lastCommand = cmd
Jon Hall3b489db2015-10-05 14:38:37 -0700200 index = self.handle.expect( [ expectPrompt,
201 "--More--",
202 'Command not found.',
203 pexpect.TIMEOUT,
204 "^:$" ],
205 timeout=timeoutVar )
adminbae64d82013-08-01 10:50:15 -0700206 if index == 0:
kelvin8ec71442015-01-15 16:57:00 -0800207 self.LASTRSP = self.LASTRSP + \
208 self.handle.before + self.handle.after
You Wang7d14d642019-01-23 15:10:08 -0800209 if not args[ "LOGCMD" ] is False:
Jon Hall3c0114c2020-08-11 15:07:42 -0700210 main.log.info( self.name + ": Executed :" + str( cmd ) +
You Wang7d14d642019-01-23 15:10:08 -0800211 " \t\t Expected Prompt '" + str( expectPrompt ) +
212 "' Found" )
adminbae64d82013-08-01 10:50:15 -0700213 elif index == 1:
214 self.LASTRSP = self.LASTRSP + self.handle.before
kelvin8ec71442015-01-15 16:57:00 -0800215 self.handle.send( args[ "MORE" ] )
216 main.log.info(
217 "Found More screen to go , Sending a key to proceed" )
218 indexMore = self.handle.expect(
219 [ "--More--", expectPrompt ], timeout=timeoutVar )
adminbae64d82013-08-01 10:50:15 -0700220 while indexMore == 0:
kelvin8ec71442015-01-15 16:57:00 -0800221 main.log.info(
222 "Found anoother More screen to go , Sending a key to proceed" )
223 self.handle.send( args[ "MORE" ] )
224 indexMore = self.handle.expect(
225 [ "--More--", expectPrompt ], timeout=timeoutVar )
adminbae64d82013-08-01 10:50:15 -0700226 self.LASTRSP = self.LASTRSP + self.handle.before
kelvin8ec71442015-01-15 16:57:00 -0800227 elif index == 2:
Jon Hall3c0114c2020-08-11 15:07:42 -0700228 main.log.error( self.name + ": Command not found" )
adminbae64d82013-08-01 10:50:15 -0700229 self.LASTRSP = self.LASTRSP + self.handle.before
kelvin8ec71442015-01-15 16:57:00 -0800230 elif index == 3:
Jon Hall3c0114c2020-08-11 15:07:42 -0700231 main.log.error( self.name + ": Expected Prompt not found, Time Out!!" )
kelvin8ec71442015-01-15 16:57:00 -0800232 main.log.error( expectPrompt )
Jon Hall3b489db2015-10-05 14:38:37 -0700233 self.LASTRSP = self.LASTRSP + self.handle.before
234 return self.LASTRSP
adminbae64d82013-08-01 10:50:15 -0700235 elif index == 4:
236 self.LASTRSP = self.LASTRSP + self.handle.before
kelvin8ec71442015-01-15 16:57:00 -0800237 # self.handle.send( args[ "MORE" ] )
238 self.handle.sendcontrol( "D" )
239 main.log.info(
Jon Hall3b489db2015-10-05 14:38:37 -0700240 "Found More screen to go, Sending a key to proceed" )
kelvin8ec71442015-01-15 16:57:00 -0800241 indexMore = self.handle.expect(
242 [ "^:$", expectPrompt ], timeout=timeoutVar )
adminbae64d82013-08-01 10:50:15 -0700243 while indexMore == 0:
kelvin8ec71442015-01-15 16:57:00 -0800244 main.log.info(
Jon Hall3b489db2015-10-05 14:38:37 -0700245 "Found another More screen to go, Sending a key to proceed" )
kelvin8ec71442015-01-15 16:57:00 -0800246 self.handle.sendcontrol( "D" )
247 indexMore = self.handle.expect(
248 [ "^:$", expectPrompt ], timeout=timeoutVar )
adminbae64d82013-08-01 10:50:15 -0700249 self.LASTRSP = self.LASTRSP + self.handle.before
kelvin8ec71442015-01-15 16:57:00 -0800250 main.last_response = self.remove_contol_chars( self.LASTRSP )
adminbae64d82013-08-01 10:50:15 -0700251 return self.LASTRSP
kelvin8ec71442015-01-15 16:57:00 -0800252
253 def remove_contol_chars( self, response ):
254 # 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 ) )
255 # response = re.sub( RE_XML_ILLEGAL, "\n", response )
256 response = re.sub( r"[\x01-\x1F\x7F]", "", response )
257 # response = re.sub( r"\[\d+\;1H", "\n", response )
258 response = re.sub( r"\[\d+\;\d+H", "", response )
adminbae64d82013-08-01 10:50:15 -0700259 return response
adminbae64d82013-08-01 10:50:15 -0700260
kelvin8ec71442015-01-15 16:57:00 -0800261 def runAsSudoUser( self, handle, pwd, default ):
262
263 i = handle.expect( [ ".ssword:*", default, pexpect.EOF ] )
264 if i == 0:
265 handle.sendline( pwd )
Jon Hall5ec6b1b2015-09-17 18:20:14 -0700266 handle.sendline( "\n" )
kelvin8ec71442015-01-15 16:57:00 -0800267
268 if i == 1:
269 handle.expect( default )
270
271 if i == 2:
Jon Hall3c0114c2020-08-11 15:07:42 -0700272 main.log.error( self.name + ": Unable to run as Sudo user" )
kelvin8ec71442015-01-15 16:57:00 -0800273
adminbae64d82013-08-01 10:50:15 -0700274 return handle
adminbae64d82013-08-01 10:50:15 -0700275
kelvin8ec71442015-01-15 16:57:00 -0800276 def onfail( self ):
277 if 'onfail' in main.componentDictionary[ self.name ]:
278 commandList = main.componentDictionary[
279 self.name ][ 'onfail' ].split( "," )
280 for command in commandList:
281 response = self.execute(
282 cmd=command,
283 prompt="(.*)",
284 timeout=120 )
adminbae64d82013-08-01 10:50:15 -0700285
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700286 def secureCopy( self, userName, ipAddress, filePath, dstPath, pwd="",
Jon Hall22a3bcf2021-07-23 11:40:11 -0700287 direction="from", options="", timeout=120 ):
kelvin8ec71442015-01-15 16:57:00 -0800288 """
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700289 Definition:
290 Execute scp command in linux to copy to/from a remote host
291 Required:
292 str userName - User name of the remote host
293 str ipAddress - IP address of the remote host
294 str filePath - File path including the file it self
295 str dstPath - Destination path
296 Optional:
297 str pwd - Password of the host
298 str direction - Direction of the scp, default to "from" which means
299 copy "from" the remote machine to local machine,
300 while "to" means copy "to" the remote machine from
301 local machine
kelvin8ec71442015-01-15 16:57:00 -0800302 """
Jon Hall669bc862021-03-09 12:24:44 -0800303 returnVal = main.FALSE
adminbae64d82013-08-01 10:50:15 -0700304 ssh_newkey = 'Are you sure you want to continue connecting'
kelvin8ec71442015-01-15 16:57:00 -0800305 refused = "ssh: connect to host " + \
Jon Hall547e0582015-09-21 17:35:40 -0700306 ipAddress + " port 22: Connection refused"
Jon Hall39570262020-11-17 12:18:19 -0800307 cmd = "scp %s " % options
Jon Hall669bc862021-03-09 12:24:44 -0800308 try:
309 self.handle.sendline( "" )
310 self.handle.expect( self.prompt, timeout=5 )
311 except pexpect.TIMEOUT:
312 main.log.error( "%s: Component not ready for input" % self.name )
313 main.log.debug( "%s: %s%s" % ( self.name, self.handle.before, str( self.handle.after ) ) )
314 self.handle.send( "\x03" ) # CTRL-C
315 self.handle.expect( self.prompt, timeout=5 )
acsmars07f9d392015-07-15 10:30:58 -0700316
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700317 if direction == "from":
Jon Hall39570262020-11-17 12:18:19 -0800318 cmd = cmd + str( userName ) + '@' + str( ipAddress ) + ':' + \
Jon Hall547e0582015-09-21 17:35:40 -0700319 str( filePath ) + ' ' + str( dstPath )
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700320 elif direction == "to":
Jon Hall39570262020-11-17 12:18:19 -0800321 cmd = cmd + str( filePath ) + ' ' + str( userName ) + \
Jon Hall547e0582015-09-21 17:35:40 -0700322 '@' + str( ipAddress ) + ':' + str( dstPath )
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700323 else:
324 main.log.debug( "Wrong direction using secure copy command!" )
325 return main.FALSE
kelvin8ec71442015-01-15 16:57:00 -0800326
Jon Hall3c0114c2020-08-11 15:07:42 -0700327 main.log.info( self.name + ": Sending: " + cmd )
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700328 self.handle.sendline( cmd )
Jon Hall547e0582015-09-21 17:35:40 -0700329 i = 0
Jon Hall66ce22f2021-06-30 14:57:40 -0700330 hit = False
Jon Hall669bc862021-03-09 12:24:44 -0800331 while i <= 6 :
Jon Hall547e0582015-09-21 17:35:40 -0700332 i = self.handle.expect( [
333 ssh_newkey,
334 'password:',
335 "100%",
336 refused,
337 "No such file or directory",
Jon Hall53c5e662016-04-13 16:06:56 -0700338 "Permission denied",
Devin Limdc78e202017-06-09 18:30:07 -0700339 self.prompt,
Jon Hall547e0582015-09-21 17:35:40 -0700340 pexpect.EOF,
341 pexpect.TIMEOUT ],
Jon Hall669bc862021-03-09 12:24:44 -0800342 timeout=timeout )
Jon Hall547e0582015-09-21 17:35:40 -0700343 if i == 0: # ask for ssh key confirmation
Jon Hall66ce22f2021-06-30 14:57:40 -0700344 hit = True
Jon Hall3c0114c2020-08-11 15:07:42 -0700345 main.log.info( self.name + ": ssh key confirmation received, sending yes" )
Jon Hall547e0582015-09-21 17:35:40 -0700346 self.handle.sendline( 'yes' )
347 elif i == 1: # Asked for ssh password
Jon Hall66ce22f2021-06-30 14:57:40 -0700348 hit = True
Jon Hall669bc862021-03-09 12:24:44 -0800349 timeout = 120
Jon Hall3c0114c2020-08-11 15:07:42 -0700350 main.log.info( self.name + ": ssh connection asked for password, gave password" )
Jon Hall547e0582015-09-21 17:35:40 -0700351 self.handle.sendline( pwd )
352 elif i == 2: # File finished transfering
Jon Hall66ce22f2021-06-30 14:57:40 -0700353 hit = True
Jon Hall3c0114c2020-08-11 15:07:42 -0700354 main.log.info( self.name + ": Secure copy successful" )
Jon Hall669bc862021-03-09 12:24:44 -0800355 timeout = 10
Jon Hall547e0582015-09-21 17:35:40 -0700356 returnVal = main.TRUE
357 elif i == 3: # Connection refused
Jon Hall66ce22f2021-06-30 14:57:40 -0700358 hit = True
Jon Hall547e0582015-09-21 17:35:40 -0700359 main.log.error(
360 "ssh: connect to host " +
361 ipAddress +
362 " port 22: Connection refused" )
363 returnVal = main.FALSE
364 elif i == 4: # File Not found
Jon Hall66ce22f2021-06-30 14:57:40 -0700365 hit = True
Jon Hall3c0114c2020-08-11 15:07:42 -0700366 main.log.error( self.name + ": No such file found" )
Jon Hall39570262020-11-17 12:18:19 -0800367 main.log.debug( self.handle.before + self.handle.after )
Jon Hall547e0582015-09-21 17:35:40 -0700368 returnVal = main.FALSE
Jon Hall53c5e662016-04-13 16:06:56 -0700369 elif i == 5: # Permission denied
Jon Hall66ce22f2021-06-30 14:57:40 -0700370 hit = True
Jon Hall3c0114c2020-08-11 15:07:42 -0700371 main.log.error( self.name + ": Permission denied. Check folder permissions" )
Jon Hall39570262020-11-17 12:18:19 -0800372 main.log.debug( self.handle.before + self.handle.after )
Jon Hall53c5e662016-04-13 16:06:56 -0700373 returnVal = main.FALSE
374 elif i == 6: # prompt returned
Jon Hall66ce22f2021-06-30 14:57:40 -0700375 hit = True
Jon Hall669bc862021-03-09 12:24:44 -0800376 timeout = 10
377 main.log.debug( "%s: %s%s" % ( self.name, repr( self.handle.before ), repr( self.handle.after ) ) )
Jon Hall53c5e662016-04-13 16:06:56 -0700378 elif i == 7: # EOF
Jon Hall66ce22f2021-06-30 14:57:40 -0700379 hit = True
Jon Hall3c0114c2020-08-11 15:07:42 -0700380 main.log.error( self.name + ": Pexpect.EOF found!!!" )
Devin Lim44075962017-08-11 10:56:37 -0700381 main.cleanAndExit()
Jon Hall53c5e662016-04-13 16:06:56 -0700382 elif i == 8: # timeout
Jon Hall66ce22f2021-06-30 14:57:40 -0700383 if not hit:
Jon Hall669bc862021-03-09 12:24:44 -0800384 main.log.error(
385 "No route to the Host " +
386 userName +
387 "@" +
388 ipAddress )
389 return returnVal
390 self.handle.expect( [ self.prompt, pexpect.TIMEOUT ], timeout=5 )
391 main.log.debug( "%s: %s%s" % ( self.name, repr( self.handle.before ), repr( self.handle.after ) ) )
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700392 return returnVal
393
Jon Hall22a3bcf2021-07-23 11:40:11 -0700394 def scp( self, remoteHost, filePath, dstPath, direction="from", options="", timeout=120 ):
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700395 """
396 Definition:
397 Execute scp command in linux to copy to/from a remote host
398 Required:
399 * remoteHost - Test ON component to be parsed
400 str filePath - File path including the file it self
401 str dstPath - Destination path
402 Optional:
403 str direction - Direction of the scp, default to "from" which means
404 copy "from" the remote machine to local machine,
405 while "to" means copy "to" the remote machine from
406 local machine
407 """
Jon Hall06fd0df2021-01-25 15:50:06 -0800408 jump_host = main.componentDictionary[ remoteHost.name ].get( 'jump_host' )
409 if jump_host:
410 jump_host = main.componentDictionary.get( jump_host )
Jon Hall669bc862021-03-09 12:24:44 -0800411 options += " -o 'ProxyJump %s@%s' " % ( jump_host.get( 'user' ), jump_host.get( 'host' ) )
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700412 return self.secureCopy( remoteHost.user_name,
413 remoteHost.ip_address,
414 filePath,
415 dstPath,
416 pwd=remoteHost.pwd,
Jon Hall39570262020-11-17 12:18:19 -0800417 direction=direction,
Jon Hall22a3bcf2021-07-23 11:40:11 -0700418 options=options,
419 timeout=timeout )
Devin Lim142b5342017-07-20 15:22:39 -0700420
421 def sshToNode( self, ipAddress, uName="sdn", pwd="rocks" ):
422 ssh_newkey = 'Are you sure you want to continue connecting'
423 refused = "ssh: connect to host " + ipAddress + " port 22: Connection refused"
424 handle = pexpect.spawn( 'ssh -X ' +
425 uName +
426 '@' +
427 ipAddress,
Jon Hall6c9e2da2018-11-06 12:01:23 -0800428 env={ "TERM": "vt100" },
Devin Lim142b5342017-07-20 15:22:39 -0700429 maxread=1000000,
430 timeout=60 )
431
432 # set tty window size
433 handle.setwinsize( 24, 250 )
434
435 i = 5
436 while i == 5:
Jon Hall4173b242017-09-12 17:04:38 -0700437 i = handle.expect( [ ssh_newkey,
438 'password:|Password:',
439 pexpect.EOF,
440 pexpect.TIMEOUT,
441 refused,
442 'teston>',
443 self.prompt ],
444 120 )
Devin Lim142b5342017-07-20 15:22:39 -0700445 if i == 0: # Accept key, then expect either a password prompt or access
Jon Hall3c0114c2020-08-11 15:07:42 -0700446 main.log.info( self.name + ": ssh key confirmation received, send yes" )
Devin Lim142b5342017-07-20 15:22:39 -0700447 handle.sendline( 'yes' )
448 i = 5 # Run the loop again
449 continue
450 if i == 1: # Password required
451 if pwd:
452 main.log.info(
Jon Hall4173b242017-09-12 17:04:38 -0700453 "ssh connection asked for password, gave password" )
Devin Lim142b5342017-07-20 15:22:39 -0700454 else:
Jon Hall3c0114c2020-08-11 15:07:42 -0700455 main.log.info( self.name + ": Server asked for password, but none was "
Devin Lim142b5342017-07-20 15:22:39 -0700456 "given in the .topo file. Trying "
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700457 "no password." )
Devin Lim142b5342017-07-20 15:22:39 -0700458 pwd = ""
459 handle.sendline( pwd )
460 j = handle.expect( [ self.prompt,
461 'password:|Password:',
462 pexpect.EOF,
463 pexpect.TIMEOUT ],
464 120 )
465 if j != 0:
Jon Hall3c0114c2020-08-11 15:07:42 -0700466 main.log.error( self.name + ": Incorrect Password" )
Devin Lim44075962017-08-11 10:56:37 -0700467 main.cleanAndExit()
Devin Lim142b5342017-07-20 15:22:39 -0700468 elif i == 2:
Jon Hall3c0114c2020-08-11 15:07:42 -0700469 main.log.error( self.name + ": Connection timeout" )
Devin Lim44075962017-08-11 10:56:37 -0700470 main.cleanAndExit()
Devin Lim142b5342017-07-20 15:22:39 -0700471 elif i == 3: # timeout
472 main.log.error(
473 "No route to the Host " +
474 uName +
475 "@" +
476 ipAddress )
Devin Lim44075962017-08-11 10:56:37 -0700477 main.cleanAndExit()
Devin Lim142b5342017-07-20 15:22:39 -0700478 elif i == 4:
479 main.log.error(
480 "ssh: connect to host " +
481 ipAddress +
482 " port 22: Connection refused" )
Devin Lim44075962017-08-11 10:56:37 -0700483 main.cleanAndExit()
Devin Lim142b5342017-07-20 15:22:39 -0700484 elif i == 6:
Jon Hall3c0114c2020-08-11 15:07:42 -0700485 main.log.info( self.name + ": Password not required logged in" )
Devin Lim142b5342017-07-20 15:22:39 -0700486
487 handle.sendline( "" )
488 handle.expect( self.prompt )
489 handle.sendline( "cd" )
490 handle.expect( self.prompt )
491
Jon Hall3c0114c2020-08-11 15:07:42 -0700492 main.log.info( self.name + ": Successfully ssh to " + ipAddress + "." )
Devin Lim142b5342017-07-20 15:22:39 -0700493 return handle
494
495 def exitFromSsh( self, handle, ipAddress ):
Devin Lim142b5342017-07-20 15:22:39 -0700496 try:
Jon Hall4f360bc2017-09-07 10:19:52 -0700497 handle.sendline( "logout" )
Devin Lim142b5342017-07-20 15:22:39 -0700498 handle.expect( "closed." )
Jon Hall3c0114c2020-08-11 15:07:42 -0700499 main.log.info( self.name + ": Successfully closed ssh connection from " + ipAddress )
Devin Lim142b5342017-07-20 15:22:39 -0700500 except pexpect.EOF:
Jon Hall3c0114c2020-08-11 15:07:42 -0700501 main.log.error( self.name + ": Failed to close the connection from " + ipAddress )
Jon Hall4f360bc2017-09-07 10:19:52 -0700502 try:
503 # check that this component handle still works
504 self.handle.sendline( "" )
505 self.handle.expect( self.prompt )
506 except pexpect.EOF:
507 main.log.error( self.handle.before )
Jon Hall3c0114c2020-08-11 15:07:42 -0700508 main.log.error( self.name + ": EOF after closing ssh connection" )
Jon Hall4173b242017-09-12 17:04:38 -0700509
510 def folderSize( self, path, size='10', unit='M', ignoreRoot=True ):
511 """
512 Run `du -h` on the folder path and verifies the folder(s) size is
513 less than the given size. Note that if multiple subdirectories are
514 present, the result will be the OR of all the individual subdirectories.
515
516 Arguments:
517 path - A string containing the path supplied to the du command
518 size - The number portion of the file size that the results will be compared to
519 unit - The unit portion of the file size that the results will be compared to
520 ignoreRoot - If True, will ignore the "root" of the path supplied to du. I.E. will ignore `.`
521
522 Returns True if the folder(s) size(s) are less than SIZE UNITS, else returns False
523 """
524 sizeRe = r'(?P<number>\d+\.*\d*)(?P<unit>\D)'
525 unitsList = [ 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y' ]
526 try:
527 # make sure we convert units if size is too big
528 size = float( size )
529 if size >= 1000:
530 size = size / 1000
531 unit = unitsList[ unitsList.index( unit + 1 ) ]
532 cmdStr = "du -h " + path
533 self.handle.sendline( cmdStr )
534 self.handle.expect( self.prompt )
535 output = self.handle.before
536 assert "cannot access" not in output
537 assert "command not found" not in output
538 main.log.debug( output )
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700539 lines = [ line for line in output.split( '\r\n' ) ]
Jon Hall4173b242017-09-12 17:04:38 -0700540 retValue = True
541 if ignoreRoot:
542 lastIndex = -2
543 else:
544 lastIndex = -1
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700545 for line in lines[ 1:lastIndex ]:
Jon Hall4173b242017-09-12 17:04:38 -0700546 parsed = line.split()
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700547 sizeMatch = parsed[ 0 ]
548 folder = parsed[ 1 ]
Jon Hall4173b242017-09-12 17:04:38 -0700549 match = re.search( sizeRe, sizeMatch )
550 num = match.group( 'number' )
551 unitMatch = match.group( 'unit' )
552 if unitsList.index( unitMatch ) < unitsList.index( unit ):
553 retValue &= True
554 elif unitsList.index( unitMatch ) == unitsList.index( unit ):
555 if float( num ) < float( size ):
556 retValue &= True
557 else:
558 retValue &= False
559 elif unitsList.index( unitMatch ) > unitsList.index( unit ):
560 retValue &= False
561 return retValue
562 except AssertionError:
563 main.log.error( self.name + ": Could not execute command: " + output )
564 return False
Jon Hall43060f62020-06-23 13:13:33 -0700565 except ValueError as e:
566 main.log.error( self.name + ": Error parsing output: " + output )
567 main.log.error( e )
568 return False
Jon Hall4173b242017-09-12 17:04:38 -0700569 except pexpect.TIMEOUT:
570 main.log.exception( self.name + ": TIMEOUT exception found" )
571 main.log.error( self.name + ": " + self.handle.before )
572 return False
573 except pexpect.EOF:
574 main.log.error( self.name + ": EOF exception found" )
575 main.log.error( self.name + ": " + self.handle.before )
576 main.cleanAndExit()
Jon Hall0e240372018-05-02 11:21:57 -0700577
Jon Hall22a3bcf2021-07-23 11:40:11 -0700578 def fileSize( self, path, inBytes=True ):
579 """
580 Run `du` on the file path and returns the file size
581
582 Arguments:
583 path - A string containing the path supplied to the du command
584 Optional Arguments:
585 inBytes - Display size in bytes, defaults to true
586
587 Returns the size of the file as an int
588 """
589 sizeRe = r'(?P<number>\d+\.*\d*)(?P<unit>\D)'
590 try:
591 cmdStr = "du %s %s" % ( "-b" if inBytes else "", path )
592 self.handle.sendline( cmdStr )
593 self.handle.expect( self.prompt )
594 output = self.handle.before
595 assert "cannot access" not in output
596 assert "command not found" not in output
597 assert "No such file or directory" not in output
598 main.log.debug( output )
599 lines = [ line for line in output.split( '\r\n' ) ]
600 return int( lines[1].split()[0] )
601 except AssertionError:
602 main.log.error( self.name + ": Could not execute command: " + output )
603 return False
604 except ValueError as e:
605 main.log.error( self.name + ": Error parsing output: " + output )
606 main.log.error( e )
607 return False
608 except pexpect.TIMEOUT:
609 main.log.exception( self.name + ": TIMEOUT exception found" )
610 main.log.error( self.name + ": " + self.handle.before )
611 return False
612 except pexpect.EOF:
613 main.log.error( self.name + ": EOF exception found" )
614 main.log.error( self.name + ": " + self.handle.before )
615 main.cleanAndExit()
616
Jon Hall0e240372018-05-02 11:21:57 -0700617 def setEnv( self, variable, value=None ):
618 """
619 Sets the environment variable to the given value for the current shell session.
620 If value is None, will unset the variable.
621
622 Required Arguments:
623 variable - The name of the environment variable to set.
624
625 Optional Arguments:
626 value - The value to set the variable to. ( Defaults to None, which unsets the variable )
627
628 Returns True if no errors are detected else returns False
629 """
630 try:
631 if value:
632 cmd = "export {}={}".format( variable, value )
633 else:
634 cmd = "unset {}".format( variable )
635 self.handle.sendline( cmd )
636 self.handle.expect( self.prompt )
Jon Hall3c0114c2020-08-11 15:07:42 -0700637 output = self.handle.before
638 main.log.debug( output )
Jon Hall0e240372018-05-02 11:21:57 -0700639 return True
640 except AssertionError:
641 main.log.error( self.name + ": Could not execute command: " + output )
642 return False
643 except pexpect.TIMEOUT:
644 main.log.exception( self.name + ": TIMEOUT exception found" )
645 main.log.error( self.name + ": " + self.handle.before )
646 return False
647 except pexpect.EOF:
648 main.log.error( self.name + ": EOF exception found" )
649 main.log.error( self.name + ": " + self.handle.before )
650 main.cleanAndExit()
You Wangb65d2372018-08-17 15:37:59 -0700651
652 def exitFromCmd( self, expect, retry=10 ):
653 """
654 Call this function when sending ctrl+c is required to kill the current
655 command. It will retry multiple times until the running command is
656 completely killed and expected string is returned from the handle.
657 Required:
You Wangd4fae5c2018-08-22 13:56:49 -0700658 expect: expected string or list of strings which indicates that the
659 previous command was killed successfully.
You Wangb65d2372018-08-17 15:37:59 -0700660 Optional:
661 retry: maximum number of ctrl+c that will be sent.
662 """
You Wangd4fae5c2018-08-22 13:56:49 -0700663 expect = [ expect ] if isinstance( expect, str ) else expect
You Wangb65d2372018-08-17 15:37:59 -0700664 try:
665 while retry >= 0:
666 main.log.debug( self.name + ": sending ctrl+c to kill the command" )
667 self.handle.send( "\x03" )
You Wangd4fae5c2018-08-22 13:56:49 -0700668 i = self.handle.expect( expect + [ pexpect.TIMEOUT ], timeout=3 )
You Wangb65d2372018-08-17 15:37:59 -0700669 main.log.debug( self.handle.before )
You Wangd4fae5c2018-08-22 13:56:49 -0700670 if i < len( expect ):
You Wangb65d2372018-08-17 15:37:59 -0700671 main.log.debug( self.name + ": successfully killed the command" )
672 return main.TRUE
673 retry -= 1
674 main.log.warn( self.name + ": failed to kill the command" )
675 return main.FALSE
676 except pexpect.EOF:
677 main.log.error( self.name + ": EOF exception found" )
678 main.log.error( self.name + ": " + self.handle.before )
679 return main.FALSE
680 except Exception:
681 main.log.exception( self.name + ": Uncaught exception!" )
682 return main.FALSE
Jon Hall43060f62020-06-23 13:13:33 -0700683
684 def cleanOutput( self, output, debug=False ):
685 """
686 Clean ANSI characters from output
687 """
688 ansiEscape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
689 cleaned = ansiEscape.sub( '', output )
690 if debug:
691 main.log.debug( self.name + ": cleanOutput:" )
692 main.log.debug( self.name + ": " + repr( cleaned ) )
693 return cleaned
Jon Hall3c0114c2020-08-11 15:07:42 -0700694
695 def dockerPull( self, image, tag=None ):
696 """
697 Pull a docker image from a registry
698 """
699 try:
700 imgStr = "%s%s" % ( image, ":%s" % tag if tag else "" )
701 cmdStr = "docker pull %s" % imgStr
702 main.log.info( self.name + ": sending: " + cmdStr )
703 self.handle.sendline( cmdStr)
704 i = self.handle.expect( [ self.prompt,
705 "Error response from daemon",
706 pexpect.TIMEOUT ], 120 )
707 if i == 0:
708 return main.TRUE
709 else:
710 main.log.error( self.name + ": Error pulling docker image " + imgStr )
711 output = self.handle.before + str( self.handle.after )
712 if i == 1:
713 self.handle.expect( self.prompt )
714 output += self.handle.before + str( self.handle.after )
715 main.log.debug( self.name + ": " + output )
716 return main.FALSE
717 except pexpect.EOF:
718 main.log.error( self.name + ": EOF exception found" )
719 main.log.error( self.name + ": " + self.handle.before )
720 return main.FALSE
721 except Exception:
722 main.log.exception( self.name + ": Uncaught exception!" )
723 return main.FALSE
724
725 def dockerBuild( self, path, imageTag, pull=False, options="", timeout=600 ):
726 """
727 Build a docker image
728 Required Arguments:
729 - path: Path to the dockerfile, it is recommended to avoid relative paths
730 - imageTag: Give a tag to the built docker image
731 Optional Arguments:
732 - pull: Whether to attempt to pull latest images before building
733 - options: A string containing any addition optional arguments
734 for the docker build command
735 - timeout: How many seconds to wait for the build to complete
736 """
737 try:
738 response = main.TRUE
739 if pull:
740 options = "--pull " + options
741 cmdStr = "docker build -t %s %s %s" % ( imageTag, options, path )
742 main.log.info( self.name + ": sending: " + cmdStr )
743 self.handle.sendline( cmdStr)
744 i = self.handle.expect( [ "Successfully built",
745 "Error response from daemon",
746 pexpect.TIMEOUT ], timeout=timeout )
747 output = self.handle.before
748 if i == 0:
749 output += self.handle.after
750 self.handle.expect( self.prompt )
751 output += self.handle.before + self.handle.after
752 return response
753 elif i == 1:
754 response = main.FALSE
755 output += self.handle.after
756 self.handle.expect( self.prompt )
757 output += self.handle.before + self.handle.after
758 elif i == 2:
759 response = main.FALSE
760 main.log.error( self.name + ": Error building docker image" )
761 main.log.debug( self.name + ": " + output )
762 return response
763 except pexpect.EOF:
764 main.log.error( self.name + ": EOF exception found" )
765 main.log.error( self.name + ": " + self.handle.before )
766 return main.FALSE
767 except Exception:
768 main.log.exception( self.name + ": Uncaught exception!" )
769 return main.FALSE
770
771 def dockerStop( self, containerName ):
772 """
773 Stop a docker container
774 Required Arguments:
775 - containerName: Name of the container to stop
776 """
777 try:
778 cmdStr = "docker stop %s" % ( containerName )
779 main.log.info( self.name + ": sending: " + cmdStr )
780 self.handle.sendline( cmdStr)
781 i = self.handle.expect( [ self.prompt,
782 "Error response from daemon",
783 pexpect.TIMEOUT ], 120 )
784 output = self.handle.before
785 if i == 0:
786 return main.TRUE
787 elif i == 1:
788 output += self.handle.after
789 self.handle.expect( self.prompt )
790 output += self.handle.before
791 elif i == 2:
792 pass
793 main.log.debug( "%s: %s" % ( self.name, output ) )
794 if "No such container" in output:
795 return main.TRUE
796 main.log.error( self.name + ": Error stopping docker image" )
797 main.log.debug( self.name + ": " + output )
798 return main.FALSE
799 except pexpect.EOF:
800 main.log.error( self.name + ": EOF exception found" )
801 main.log.error( self.name + ": " + self.handle.before )
802 return main.FALSE
803 except Exception:
804 main.log.exception( self.name + ": Uncaught exception!" )
805 return main.FALSE
806
Jon Hall06fd0df2021-01-25 15:50:06 -0800807 def dockerRun( self, image, containerName, options="", imageArgs="", background=False ):
Jon Hall3c0114c2020-08-11 15:07:42 -0700808 """
809 Run a docker image
810 Required Arguments:
811 - containerName: Give a name to the container once its started
812 - image: Run the given image
813 Optional Arguments:
814 - options: A string containing any addition optional arguments
815 for the docker run command
816 - imageArgs: A string containing command line arguments for the
817 command run by docker
818 """
819 try:
820 cmdStr = "docker run --name %s %s %s %s" % ( containerName,
821 options if options else "",
822 image,
823 imageArgs )
Jon Hall06fd0df2021-01-25 15:50:06 -0800824 if background:
825 cmdStr += " &"
Jon Hall3c0114c2020-08-11 15:07:42 -0700826 main.log.info( self.name + ": sending: " + cmdStr )
827 self.handle.sendline( cmdStr)
828 i = self.handle.expect( [ self.prompt,
829 "Error response from daemon",
830 pexpect.TIMEOUT ], 120 )
831 if i == 0:
832 return main.TRUE
833 else:
834 output = self.handle.before
835 main.log.debug( self.name + ": " + output )
836 main.log.error( self.name + ": Error running docker image" )
837 if i == 1:
838 output += self.handle.after
839 self.handle.expect( self.prompt )
840 output += self.handle.before + self.handle.after
841 main.log.debug( self.name + ": " + output )
842 return main.FALSE
843 except pexpect.EOF:
844 main.log.error( self.name + ": EOF exception found" )
845 main.log.error( self.name + ": " + self.handle.before )
846 return main.FALSE
847 except Exception:
848 main.log.exception( self.name + ": Uncaught exception!" )
849 return main.FALSE
850
851 def dockerAttach( self, containerName, dockerPrompt="" ):
852 """
853 Attach to a docker image
854 Required Arguments:
855 - containerName: The name of the container to attach to
856 Optional Arguments:
857 - dockerPrompt: a regex for matching the docker shell prompt
858 """
859 try:
860 if dockerPrompt:
861 self.dockerPrompt = dockerPrompt
862 cmdStr = "docker attach %s" % containerName
863 main.log.info( self.name + ": sending: " + cmdStr )
864 self.handle.sendline( cmdStr)
865 i = self.handle.expect( [ self.dockerPrompt,
866 "Error response from daemon",
867 pexpect.TIMEOUT ] )
868 if i == 0:
869 self.inDocker = True
870 return main.TRUE
871 else:
872 main.log.error( self.name + ": Error connecting to docker container" )
873 output = self.handle.before + str( self.handle.after )
874 if i == 1:
875 self.handle.expect( self.prompt )
876 output += self.handle.before + str( self.handle.after )
877 main.log.debug( self.name + ": " + output )
878 return main.FALSE
879 except pexpect.EOF:
880 main.log.error( self.name + ": EOF exception found" )
881 main.log.error( self.name + ": " + self.handle.before )
882 return main.FALSE
883 except AttributeError as e:
884 main.log.exception( self.name + ": AttributeError - " + str( e ) )
885 main.log.warn( self.name + ": Make sure dockerPrompt is set" )
886 main.cleanup()
887 main.exit()
888 except Exception:
889 main.log.exception( self.name + ": Uncaught exception!" )
890 return main.FALSE
891
892 def dockerExec( self, containerName, command="/bin/bash", options="-it", dockerPrompt="" ):
893 """
894 Attach to a docker image
895 Required Arguments:
896 - containerName: The name of the container to attach to
897 Optional Arguments:
898 - command: Command to run in the docker container
899 - options: Docker exec options
900 - dockerPrompt: a regex for matching the docker shell prompt
901 """
902 try:
903 if dockerPrompt:
904 self.dockerPrompt = dockerPrompt
905 cmdStr = "docker exec %s %s %s" % ( options, containerName, command )
906 main.log.info( self.name + ": sending: " + cmdStr )
907 self.handle.sendline( cmdStr)
908 i = self.handle.expect( [ self.dockerPrompt,
909 "Error response from daemon",
910 pexpect.TIMEOUT ] )
911 if i == 0:
912 self.inDocker = True
913 return main.TRUE
914 else:
915 main.log.error( self.name + ": Error connecting to docker container" )
916 output = self.handle.before + str( self.handle.after )
917 if i == 1:
918 self.handle.expect( self.prompt )
919 output += self.handle.before + str( self.handle.after )
920 main.log.debug( self.name + ": " + output )
921 return main.FALSE
922 except pexpect.EOF:
923 main.log.error( self.name + ": EOF exception found" )
924 main.log.error( self.name + ": " + self.handle.before )
925 return main.FALSE
926 except AttributeError as e:
927 main.log.exception( self.name + ": AttributeError - " + str( e ) )
928 main.log.warn( self.name + ": Make sure dockerPrompt is set" )
929 main.cleanup()
930 main.exit()
931 except Exception:
932 main.log.exception( self.name + ": Uncaught exception!" )
933 return main.FALSE
934
935 def dockerCp( self, containerName, dockerPath, hostPath, direction="from" ):
936 """
937 Copy a file from/to a docker container to the host
938 Required Arguments:
939 - containerName: The name of the container to copy from/to
940 - dockerPath: the path in the container to copy from/to
941 - hostPath: the path on the host to copy to/from
942 Optional Arguments:
943 - direction: Choose whether to copy "from" the container or "to" the container
944 """
945 try:
946 cmdStr = "docker cp "
947 if direction == "from":
948 cmdStr += "%s:%s %s" % ( containerName, dockerPath, hostPath )
949 elif direction == "to":
950 cmdStr += "%s %s:%s" % ( hostPath, containerName, dockerPath )
951 main.log.info( self.name + ": sending: " + cmdStr )
952 self.handle.sendline( cmdStr)
953 i = self.handle.expect( [ self.prompt,
954 "Error",
955 pexpect.TIMEOUT ] )
956 if i == 0:
957 retValue = main.TRUE
958 else:
959 main.log.error( self.name + ": Error in docker cp" )
960 output = self.handle.before + str( self.handle.after )
961 if i == 1:
962 self.handle.expect( self.prompt )
963 output += self.handle.before + str( self.handle.after )
964 main.log.debug( self.name + ": " + output )
965 retValue = main.FALSE
966 return retValue
967 except pexpect.EOF:
968 main.log.error( self.name + ": EOF exception found" )
969 main.log.error( self.name + ": " + self.handle.before )
970 return main.FALSE
971 except AttributeError as e:
972 main.log.exception( self.name + ": AttributeError - " + str( e ) )
973 main.log.warn( self.name + ": Make sure dockerPrompt is set" )
974 main.cleanup()
975 main.exit()
976 except Exception:
977 main.log.exception( self.name + ": Uncaught exception!" )
978 return main.FALSE
979
980 def dockerDisconnect( self ):
981 """
982 Send ctrl-c, ctrl-d to session, which should close and exit the
983 attached docker session. This will likely exit the running program
984 in the container and also stop the container.
985 """
986 try:
987 cmdStr = "\x03"
988 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
989 self.handle.send( cmdStr)
990 cmdStr = "\x04"
991 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
992 self.handle.send( cmdStr)
993 i = self.handle.expect( [ self.prompt, pexpect.TIMEOUT ] )
994 if i == 0:
995 self.inDocker = False
996 return main.TRUE
997 else:
998 main.log.error( self.name + ": Error disconnecting from docker image" )
999 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1000 return main.FALSE
1001 except pexpect.EOF:
1002 main.log.error( self.name + ": EOF exception found" )
1003 main.log.error( self.name + ": " + self.handle.before )
1004 return main.FALSE
1005 except Exception:
1006 main.log.exception( self.name + ": Uncaught exception!" )
1007 return main.FALSE
Jon Hall06fd0df2021-01-25 15:50:06 -08001008
1009# TODO: How is this different from exitFromCmd used elsewhere?
1010 def exitFromProcess( self ):
1011 """
1012 Send ctrl-c, which should close and exit the program
1013 """
1014 try:
1015 cmdStr = "\x03"
1016 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1017 self.handle.send( cmdStr)
1018 i = self.handle.expect( [ self.prompt, pexpect.TIMEOUT ] )
1019 if i == 0:
1020 return main.TRUE
1021 else:
1022 main.log.error( self.name + ": Error exiting process" )
1023 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1024 return main.FALSE
1025 except pexpect.EOF:
1026 main.log.error( self.name + ": EOF exception found" )
1027 main.log.error( self.name + ": " + self.handle.before )
1028 return main.FALSE
1029 except Exception:
1030 main.log.exception( self.name + ": Uncaught exception!" )
1031 return main.FALSE
1032
1033 def preDisconnect( self ):
1034 """
1035 A Stub for a function that will be called before disconnect.
1036 This can be set if for instance, the shell is running a program
1037 and needs to exit the program before disconnecting from the component
1038 """
1039 print "preDisconnect"
1040 return main.TRUE
1041
Jon Hall22a3bcf2021-07-23 11:40:11 -07001042 def kubectlGetPodNames( self, kubeconfig=None, namespace=None, app=None, name=None,
1043 nodeName=None, status=None ):
Jon Hall06fd0df2021-01-25 15:50:06 -08001044 """
1045 Use kubectl to get the names of pods
1046 Optional Arguments:
1047 - kubeconfig: The path to a kubeconfig file
1048 - namespace: The namespace to search in
1049 - app: Get pods belonging to a specific app
1050 - name: Get pods with a specific name label
Jon Hallbe3a2ac2021-03-15 12:28:06 -07001051 - nodeName: Get pods on a specific node
Jon Hall22a3bcf2021-07-23 11:40:11 -07001052 - status: Get pods with the specified Status
Jon Hall06fd0df2021-01-25 15:50:06 -08001053 Returns a list containing the names of the pods or
1054 main.FALSE on Error
1055 """
1056
1057 try:
Jon Hall22a3bcf2021-07-23 11:40:11 -07001058 self.handle.sendline( "" )
1059 self.handle.expect( self.prompt )
1060 main.log.debug( self.handle.before + self.handle.after )
1061 cmdStr = "kubectl %s %s get pods %s %s %s %s --output=jsonpath='{.items..metadata.name}{\"\\n\"}'" % (
Jon Hall06fd0df2021-01-25 15:50:06 -08001062 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1063 "-n %s" % namespace if namespace else "",
1064 "-l app=%s" % app if app else "",
Jon Hallbe3a2ac2021-03-15 12:28:06 -07001065 "-l name=%s" % name if name else "",
Jon Hall22a3bcf2021-07-23 11:40:11 -07001066 "--field-selector=spec.nodeName=%s" % nodeName if nodeName else "",
1067 "--field-selector=status.phase=%s" % status if status else "" )
Jon Hall06fd0df2021-01-25 15:50:06 -08001068 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1069 self.handle.sendline( cmdStr )
Jon Hall2941fce2021-04-13 10:38:23 -07001070 i = self.handle.expect( [ "not found", "error", "The connection to the server", "Unable to find", "No resources found", self.prompt ] )
1071 if i == 4:
1072 # Command worked, but returned no pods
1073 output = self.handle.before + self.handle.after
1074 main.log.warn( self.name + ": " + output )
1075 return []
1076 elif i == 5:
1077 # Command returned pods
Jon Hall06fd0df2021-01-25 15:50:06 -08001078 output = self.handle.before + self.handle.after
1079 names = output.split( '\r\n' )[1].split()
1080 return names
1081 else:
Jon Hall2941fce2021-04-13 10:38:23 -07001082 # Some error occured
Jon Hall06fd0df2021-01-25 15:50:06 -08001083 main.log.error( self.name + ": Error executing command" )
1084 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1085 return main.FALSE
1086 except pexpect.EOF:
1087 main.log.error( self.name + ": EOF exception found" )
1088 main.log.error( self.name + ": " + self.handle.before )
1089 return main.FALSE
1090 except pexpect.TIMEOUT:
1091 main.log.exception( self.name + ": TIMEOUT exception found" )
1092 main.log.error( self.name + ": " + self.handle.before )
1093 return main.FALSE
1094 except Exception:
1095 main.log.exception( self.name + ": Uncaught exception!" )
1096 return main.FALSE
1097
1098 def kubectlDescribe( self, describeString, dstPath, kubeconfig=None, namespace=None ):
1099 """
1100 Use kubectl to get the logs from a pod
1101 Required Arguments:
1102 - describeString: The string passed to the cli. Example: "pods"
1103 - dstPath: The location to save the logs to
1104 Optional Arguments:
1105 - kubeconfig: The path to a kubeconfig file
1106 - namespace: The namespace to search in
1107 Returns main.TRUE or
1108 main.FALSE on Error
1109 """
1110
1111 try:
1112 cmdStr = "kubectl %s %s describe %s > %s " % (
1113 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1114 "-n %s" % namespace if namespace else "",
1115 describeString,
1116 dstPath )
1117 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1118 self.handle.sendline( cmdStr )
1119 i = self.handle.expect( [ "not found", "error", "The connection to the server", self.prompt ] )
1120 if i == 3:
1121 main.log.debug( self.name + ": " + self.handle.before )
1122 return main.TRUE
1123 else:
1124 main.log.error( self.name + ": Error executing command" )
1125 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1126 return main.FALSE
1127 except pexpect.EOF:
1128 main.log.error( self.name + ": EOF exception found" )
1129 main.log.error( self.name + ": " + self.handle.before )
1130 return main.FALSE
1131 except pexpect.TIMEOUT:
1132 main.log.exception( self.name + ": TIMEOUT exception found" )
1133 main.log.error( self.name + ": " + self.handle.before )
1134 return main.FALSE
1135 except Exception:
1136 main.log.exception( self.name + ": Uncaught exception!" )
1137 return main.FALSE
1138
1139 def kubectlPodNodes( self, dstPath=None, kubeconfig=None, namespace=None ):
1140 """
Jon Halla16b4db2021-10-20 14:11:59 -07001141 Use kubectl to get the pod to node mappings
Jon Hall06fd0df2021-01-25 15:50:06 -08001142 Optional Arguments:
1143 - dstPath: The location to save the logs to
1144 - kubeconfig: The path to a kubeconfig file
1145 - namespace: The namespace to search in
1146 Returns main.TRUE if dstPath is given, else the output of the command or
1147 main.FALSE on Error
1148 """
Jon Hall06fd0df2021-01-25 15:50:06 -08001149 try:
Jon Hall22a3bcf2021-07-23 11:40:11 -07001150 self.handle.sendline( "" )
1151 self.handle.expect( self.prompt )
1152 main.log.debug( self.handle.before + self.handle.after )
Jon Hall50a00012021-03-08 11:06:11 -08001153 cmdStr = "kubectl %s %s get pods -o wide %s " % (
Jon Hall06fd0df2021-01-25 15:50:06 -08001154 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1155 "-n %s" % namespace if namespace else "",
1156 " > %s" % dstPath if dstPath else "" )
1157 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1158 self.handle.sendline( cmdStr )
1159 i = self.handle.expect( [ "not found", "error", "The connection to the server", self.prompt ] )
1160 if i == 3:
1161 output = self.handle.before
1162 main.log.debug( self.name + ": " + output )
1163 return output if dstPath else main.TRUE
1164 else:
1165 main.log.error( self.name + ": Error executing command" )
1166 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1167 return main.FALSE
1168 except pexpect.EOF:
1169 main.log.error( self.name + ": EOF exception found" )
1170 main.log.error( self.name + ": " + self.handle.before )
1171 return main.FALSE
1172 except pexpect.TIMEOUT:
1173 main.log.exception( self.name + ": TIMEOUT exception found" )
1174 main.log.error( self.name + ": " + self.handle.before )
1175 return main.FALSE
1176 except Exception:
1177 main.log.exception( self.name + ": Uncaught exception!" )
1178 return main.FALSE
1179
Jon Halla16b4db2021-10-20 14:11:59 -07001180 def kubectlGetPodNode( self, podName, kubeconfig=None, namespace=None ):
1181 """
1182 Use kubectl to get the node a given pod is running on
1183 Arguments:
1184 - podName: The name of the pod
1185 Optional Arguments:
1186 - kubeconfig: The path to a kubeconfig file
1187 - namespace: The namespace to search in
1188 Returns a string of the node name or None
1189 """
1190 try:
1191 self.handle.sendline( "" )
1192 self.handle.expect( self.prompt )
1193 main.log.debug( self.handle.before + self.handle.after )
1194 cmdStr = "kubectl %s %s get pods %s --output=jsonpath='{.spec.nodeName}{\"\\n\"}'" % (
1195 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1196 "-n %s" % namespace if namespace else "",
1197 podName )
1198 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1199 self.handle.sendline( cmdStr )
1200 i = self.handle.expect( [ "not found", "error", "The connection to the server", self.prompt ] )
1201 if i == 3:
1202 output = self.handle.before
1203 main.log.debug( self.name + ": " + output )
1204 output = output.splitlines()
1205 main.log.warn( output )
1206 return output[1] if len( output ) == 3 else None
1207 else:
1208 main.log.error( self.name + ": Error executing command" )
1209 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1210 return None
1211 except pexpect.EOF:
1212 main.log.error( self.name + ": EOF exception found" )
1213 main.log.error( self.name + ": " + self.handle.before )
1214 return None
1215 except pexpect.TIMEOUT:
1216 main.log.exception( self.name + ": TIMEOUT exception found" )
1217 main.log.error( self.name + ": " + self.handle.before )
1218 return None
1219 except Exception:
1220 main.log.exception( self.name + ": Uncaught exception!" )
1221 return None
1222
Jon Halla7b27e62021-06-29 12:13:51 -07001223 def sternLogs( self, podString, dstPath, kubeconfig=None, namespace=None, since='1h', wait=60 ):
1224 """
1225 Use stern to get the logs from a pod
1226 Required Arguments:
1227 - podString: The name of the pod or partial name of the pods to get the logs of
1228 - dstPath: The location to save the logs to
1229 Optional Arguments:
1230 - kubeconfig: The path to a kubeconfig file
1231 - namespace: The namespace to search in
1232 - since: Return logs newer than a relative duration like 5s, 2m, or 3h. Defaults to 1h
1233 - wait: How long to wait, in seconds, before killing the process. Stern does not currently
Jon Halldd05bbc2022-01-27 12:14:50 -08001234 support a way to exit if caught up to present time. If negative, leaves the stern
1235 process running, tailing the logs and returns main.TRUE. Defaults to 60 seconds
Jon Halla7b27e62021-06-29 12:13:51 -07001236 Returns main.TRUE or
1237 main.FALSE on Error
1238 """
1239 import time
1240 try:
1241 cmdStr = "stern %s %s %s %s > %s " % (
1242 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1243 "-n %s" % namespace if namespace else "",
1244 "--since %s" % since if since else "",
1245 podString,
1246 dstPath )
1247 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1248 self.handle.sendline( cmdStr )
Jon Hall22a3bcf2021-07-23 11:40:11 -07001249 if int( wait ) >= 0:
Jon Halldd05bbc2022-01-27 12:14:50 -08001250 i = self.handle.expect( [ "not found",
1251 "Error: ",
1252 "The connection to the server",
1253 self.prompt,
1254 pexpect.TIMEOUT ], timeout=int( wait ) )
Jon Hall22a3bcf2021-07-23 11:40:11 -07001255 if i == 3:
1256 main.log.debug( self.name + ": " + self.handle.before )
1257 return main.TRUE
Jon Halldd05bbc2022-01-27 12:14:50 -08001258 if i == 4:
1259 self.handle.send( '\x03' ) # CTRL-C
1260 self.handle.expect( self.prompt, timeout=5 )
1261 main.log.debug( self.name + ": " + self.handle.before )
1262 return main.TRUE
Jon Hall22a3bcf2021-07-23 11:40:11 -07001263 else:
1264 main.log.error( self.name + ": Error executing command" )
1265 response = self.handle.before + str( self.handle.after )
1266 self.handle.expect( [ self.prompt, pexpect.TIMEOUT ], timeout=5 )
1267 response += self.handle.before + str( self.handle.after )
1268 main.log.debug( self.name + ": " + response )
1269 return main.FALSE
Jon Halla7b27e62021-06-29 12:13:51 -07001270 else:
Jon Hall22a3bcf2021-07-23 11:40:11 -07001271 self.preDisconnect = self.exitFromProcess
1272 return main.TRUE
Jon Halla7b27e62021-06-29 12:13:51 -07001273 except pexpect.EOF:
1274 main.log.error( self.name + ": EOF exception found" )
1275 main.log.error( self.name + ": " + self.handle.before )
1276 return main.FALSE
1277 except pexpect.TIMEOUT:
1278 main.log.exception( self.name + ": TIMEOUT exception found" )
1279 main.log.error( self.name + ": " + self.handle.before )
1280 return main.FALSE
1281 except Exception:
1282 main.log.exception( self.name + ": Uncaught exception!" )
1283 return main.FALSE
1284
Jon Hall06fd0df2021-01-25 15:50:06 -08001285 def kubectlLogs( self, podName, dstPath, kubeconfig=None, namespace=None, timeout=240 ):
1286 """
1287 Use kubectl to get the logs from a pod
1288 Required Arguments:
1289 - podName: The name of the pod to get the logs of
1290 - dstPath: The location to save the logs to
1291 Optional Arguments:
1292 - kubeconfig: The path to a kubeconfig file
1293 - namespace: The namespace to search in
1294 - timeout: Timeout for command to return. The longer the logs, the longer it will take to fetch them.
1295 Returns main.TRUE or
1296 main.FALSE on Error
1297 """
1298
1299 try:
1300 cmdStr = "kubectl %s %s logs %s > %s " % (
1301 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1302 "-n %s" % namespace if namespace else "",
1303 podName,
1304 dstPath )
1305 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1306 self.handle.sendline( cmdStr )
1307 i = self.handle.expect( [ "not found", "error", "The connection to the server", self.prompt ], timeout=timeout )
1308 if i == 3:
1309 main.log.debug( self.name + ": " + self.handle.before )
1310 return main.TRUE
1311 else:
1312 main.log.error( self.name + ": Error executing command" )
1313 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1314 return main.FALSE
1315 except pexpect.EOF:
1316 main.log.error( self.name + ": EOF exception found" )
1317 main.log.error( self.name + ": " + self.handle.before )
1318 return main.FALSE
1319 except pexpect.TIMEOUT:
1320 main.log.exception( self.name + ": TIMEOUT exception found" )
1321 main.log.error( self.name + ": " + self.handle.before )
1322 return main.FALSE
1323 except Exception:
1324 main.log.exception( self.name + ": Uncaught exception!" )
1325 return main.FALSE
1326
Jon Hallbe3a2ac2021-03-15 12:28:06 -07001327 def kubectlCp( self, podName, srcPath, dstPath, kubeconfig=None, namespace=None, timeout=240 ):
1328 """
1329 Use kubectl to get a file from a pod
1330 Required Arguments:
1331 - podName: The name of the pod to get the logs of
1332 - srcPath: The file to copy from the pod
1333 - dstPath: The location to save the file to locally
1334 Optional Arguments:
1335 - kubeconfig: The path to a kubeconfig file
1336 - namespace: The namespace to search in
1337 - timeout: Timeout for command to return. The longer the logs, the longer it will take to fetch them.
1338 Returns main.TRUE or
1339 main.FALSE on Error
1340 """
1341
1342 try:
1343 cmdStr = "kubectl %s %s cp %s:%s %s" % (
1344 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1345 "-n %s" % namespace if namespace else "",
1346 podName,
1347 srcPath,
1348 dstPath )
1349 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1350 self.handle.sendline( cmdStr )
1351 i = self.handle.expect( [ "not found", "error", "The connection to the server", self.prompt ], timeout=timeout )
1352 if i == 3:
1353 main.log.debug( self.name + ": " + self.handle.before )
1354 return main.TRUE
1355 else:
Jon Hall22a3bcf2021-07-23 11:40:11 -07001356 output = self.handle.before + str( self.handle.after )
Jon Hallbe3a2ac2021-03-15 12:28:06 -07001357 main.log.error( self.name + ": Error executing command" )
Jon Hall22a3bcf2021-07-23 11:40:11 -07001358 self.handle.expect( [ self.prompt, pexpect.TIMEOUT ] )
1359 output += self.handle.before + str( self.handle.after )
1360 main.log.debug( self.name + ": " + output )
Jon Hallbe3a2ac2021-03-15 12:28:06 -07001361 return main.FALSE
1362 except pexpect.EOF:
1363 main.log.error( self.name + ": EOF exception found" )
1364 main.log.error( self.name + ": " + self.handle.before )
1365 return main.FALSE
1366 except pexpect.TIMEOUT:
1367 main.log.exception( self.name + ": TIMEOUT exception found" )
1368 main.log.error( self.name + ": " + self.handle.before )
1369 return main.FALSE
1370 except Exception:
1371 main.log.exception( self.name + ": Uncaught exception!" )
1372 return main.FALSE
1373
Jon Halla16b4db2021-10-20 14:11:59 -07001374 def kubectlPortForward( self, podName, portsList, kubeconfig=None, namespace=None ):
Jon Hall06fd0df2021-01-25 15:50:06 -08001375 """
1376 Use kubectl to setup port forwarding from the local machine to the kubernetes pod
1377
Jon Halla16b4db2021-10-20 14:11:59 -07001378 Note: This cli command does not return until the port forwarding session is ended.
Jon Hall06fd0df2021-01-25 15:50:06 -08001379
1380 Required Arguments:
1381 - podName: The name of the pod as a string
1382 - portsList: The list of ports to forward, as a string. see kubectl help for details
1383 Optional Arguments:
1384 - kubeconfig: The path to a kubeconfig file
1385 - namespace: The namespace to search in
Jon Halla16b4db2021-10-20 14:11:59 -07001386 Returns main.TRUE if a port-forward session was created or main.FALSE on Error
Jon Hall06fd0df2021-01-25 15:50:06 -08001387
1388
1389 """
1390 try:
1391 cmdStr = "kubectl %s %s port-forward pod/%s %s" % (
1392 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1393 "-n %s" % namespace if namespace else "",
1394 podName,
1395 portsList )
Jon Halldecf0982022-02-03 13:57:11 -08001396
1397 self.clearBuffer()
Jon Hall06fd0df2021-01-25 15:50:06 -08001398 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1399 self.handle.sendline( cmdStr )
Jon Halla16b4db2021-10-20 14:11:59 -07001400 self.handle.expect( "pod/%s" % podName )
1401 output = self.handle.before + self.handle.after
Jon Hall06fd0df2021-01-25 15:50:06 -08001402 i = self.handle.expect( [ "not found", "error", "closed/timedout",
Jon Halldecf0982022-02-03 13:57:11 -08001403 self.prompt, "The connection to the server",
1404 "Forwarding from", pexpect.TIMEOUT ] )
Jon Halla16b4db2021-10-20 14:11:59 -07001405 output += self.handle.before + str( self.handle.after )
Jon Hall06fd0df2021-01-25 15:50:06 -08001406 # NOTE: This won't clear the buffer entirely, and each time the port forward
1407 # is used, another line will be added to the buffer. We need to make
1408 # sure we clear the buffer before using this component again.
1409
1410 if i == 5:
1411 # Setup preDisconnect function
1412 self.preDisconnect = self.exitFromProcess
Jon Halla16b4db2021-10-20 14:11:59 -07001413 self.portForwardList = portsList
Jon Hall06fd0df2021-01-25 15:50:06 -08001414 return main.TRUE
Jon Halldecf0982022-02-03 13:57:11 -08001415 elif i == 6:
1416 return self.checkPortForward( podName, portsList, kubeconfig, namespace )
Jon Hall06fd0df2021-01-25 15:50:06 -08001417 else:
1418 main.log.error( self.name + ": Error executing command" )
Jon Halla16b4db2021-10-20 14:11:59 -07001419 main.log.debug( self.name + ": " + output )
Jon Hall06fd0df2021-01-25 15:50:06 -08001420 return main.FALSE
1421 except pexpect.EOF:
1422 main.log.error( self.name + ": EOF exception found" )
1423 main.log.error( self.name + ": " + self.handle.before )
1424 return main.FALSE
1425 except pexpect.TIMEOUT:
1426 main.log.exception( self.name + ": TIMEOUT exception found" )
1427 main.log.error( self.name + ": " + self.handle.before )
1428 return main.FALSE
1429 except Exception:
1430 main.log.exception( self.name + ": Uncaught exception!" )
1431 return main.FALSE
Daniele Moroe1d05eb2021-09-23 19:52:30 +02001432
Jon Halla16b4db2021-10-20 14:11:59 -07001433 def checkPortForward( self, podName, portsList=None, kubeconfig=None, namespace=None ):
1434 """
1435 Check that kubectl port-forward session is still active and restarts it if it was closed.
1436
1437
1438 Required Arguments:
1439 - podName: The name of the pod as a string
1440 - portsList: The list of ports to forward, as a string. see kubectl help for details. Deafults to
1441 the last used string on this node.
1442 Optional Arguments:
1443 - kubeconfig: The path to a kubeconfig file
1444 - namespace: The namespace to search in
1445 Returns main.TRUE if a port-forward session was created or is still active, main.FALSE on Error
1446
1447
1448 """
1449 try:
Jon Halldecf0982022-02-03 13:57:11 -08001450 main.log.debug( "%s: Checking port-forward session to %s" % ( self.name, podName ) )
Jon Halla16b4db2021-10-20 14:11:59 -07001451 if not portsList:
1452 portsList = self.portForwardList
1453 self.handle.sendline( "" )
1454 i = self.handle.expect( [ self.prompt, pexpect.TIMEOUT ], timeout=5 )
1455 output = self.handle.before + str( self.handle.after )
1456 main.log.debug( "%s: %s" % ( self.name, output ) )
1457 if i == 0:
1458 # We are not currently in a port-forwarding session, try to re-establish.
Jon Halldecf0982022-02-03 13:57:11 -08001459 main.log.warn( "%s: port-forwarding session to %s closed, attempting to reestablish." % ( self.name, podName ) )
Jon Halla16b4db2021-10-20 14:11:59 -07001460 return self.kubectlPortForward( podName, portsList, kubeconfig, namespace )
1461 elif i == 1:
1462 # Still in a command, port-forward is probably still active
1463 return main.TRUE
1464 except pexpect.EOF:
1465 main.log.error( self.name + ": EOF exception found" )
1466 main.log.error( self.name + ": " + self.handle.before )
1467 return main.FALSE
1468 except pexpect.TIMEOUT:
1469 main.log.exception( self.name + ": TIMEOUT exception found" )
1470 main.log.error( self.name + ": " + self.handle.before )
1471 return main.FALSE
1472 except Exception:
1473 main.log.exception( self.name + ": Uncaught exception!" )
1474 return main.FALSE
1475
Daniele Moro80271cb2021-11-11 20:08:51 +01001476 def kubectlSetLabel( self, nodeName, label, value, kubeconfig=None, namespace=None,
1477 timeout=240, overwrite=True ):
1478 try:
1479 cmdStr = "kubectl %s %s label node %s %s %s=%s" % (
1480 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1481 "-n %s" % namespace if namespace else "",
1482 nodeName, "--overwrite" if overwrite else "",
1483 label, value )
1484 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1485 self.handle.sendline( cmdStr )
1486 i = self.handle.expect( [ "error",
1487 "The connection to the server",
1488 "node/%s not labeled" % nodeName,
1489 "node/%s labeled" % nodeName, ],
1490 timeout=timeout )
1491 if i == 3 or i == 4:
1492 output = self.handle.before + self.handle.after
1493 main.log.debug( self.name + ": " + output )
1494 self.clearBuffer()
1495 return main.TRUE
1496 else:
1497 main.log.error( self.name + ": Error executing command" )
1498 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1499 self.clearBuffer()
1500 return main.FALSE
1501 except pexpect.EOF:
1502 main.log.error( self.name + ": EOF exception found" )
1503 main.log.error( self.name + ": " + self.handle.before )
1504 return main.FALSE
1505 except pexpect.TIMEOUT:
1506 main.log.exception( self.name + ": TIMEOUT exception found" )
1507 main.log.error( self.name + ": " + self.handle.before )
1508 self.clearBuffer()
1509 return main.FALSE
1510 except Exception:
1511 main.log.exception( self.name + ": Uncaught exception!" )
1512 return main.FALSE
1513
Jon Halla16b4db2021-10-20 14:11:59 -07001514 def kubectlCordonNode( self, nodeName, kubeconfig=None, namespace=None, timeout=240, uncordonOnDisconnect=True ):
1515 try:
1516 cmdStr = "kubectl %s %s cordon %s" % (
1517 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1518 "-n %s" % namespace if namespace else "",
1519 nodeName )
1520 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1521 if uncordonOnDisconnect:
1522 self.nodeName = nodeName
1523 if kubeconfig:
1524 self.kubeconfig = kubeconfig
1525 if namespace:
1526 self.namespace = namespace
1527 self.preDisconnect = self.kubectlUncordonNode
1528 self.handle.sendline( cmdStr )
1529 i = self.handle.expect( [ "not found", "error",
1530 "The connection to the server",
1531 "node/%s cordoned" % nodeName,
1532 "node/%s already cordoned" % nodeName, ],
1533 timeout=timeout )
1534 if i == 3 or i == 4:
1535 output = self.handle.before + self.handle.after
1536 main.log.debug( self.name + ": " + output )
1537 self.clearBuffer()
1538 return main.TRUE
1539 else:
1540 main.log.error( self.name + ": Error executing command" )
1541 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1542 self.clearBuffer()
1543 return main.FALSE
1544 except pexpect.EOF:
1545 main.log.error( self.name + ": EOF exception found" )
1546 main.log.error( self.name + ": " + self.handle.before )
1547 return main.FALSE
1548 except pexpect.TIMEOUT:
1549 main.log.exception( self.name + ": TIMEOUT exception found" )
1550 main.log.error( self.name + ": " + self.handle.before )
1551 self.clearBuffer()
1552 return main.FALSE
1553 except Exception:
1554 main.log.exception( self.name + ": Uncaught exception!" )
1555 return main.FALSE
1556
1557 def kubectlUncordonNode( self, nodeName=None, kubeconfig=None, namespace=None, timeout=240 ):
1558 try:
1559 if not nodeName:
1560 nodeName = getattr( self, "nodeName" )
1561 if not kubeconfig:
1562 kubeconfig = getattr( self, "kubeconfig", None )
1563 if not kubeconfig:
1564 namespace = getattr( self, "namespace", None )
1565 cmdStr = "kubectl %s %s uncordon %s" % (
1566 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1567 "-n %s" % namespace if namespace else "",
1568 nodeName )
1569 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1570 self.handle.sendline( cmdStr )
1571 i = self.handle.expect( [ "not found", "error",
1572 "The connection to the server",
1573 "node/%s uncordoned" % nodeName,
1574 "node/%s already uncordoned" % nodeName, ],
1575 timeout=timeout )
1576 if i == 3 or i == 4:
1577 output = self.handle.before + self.handle.after
1578 main.log.debug( self.name + ": " + output )
1579 self.clearBuffer()
1580 return main.TRUE
1581 else:
1582 main.log.error( self.name + ": Error executing command" )
1583 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1584 self.clearBuffer()
1585 return main.FALSE
1586 except pexpect.EOF:
1587 main.log.error( self.name + ": EOF exception found" )
1588 main.log.error( self.name + ": " + self.handle.before )
1589 return main.FALSE
1590 except pexpect.TIMEOUT:
1591 main.log.exception( self.name + ": TIMEOUT exception found" )
1592 main.log.error( self.name + ": " + self.handle.before )
1593 self.clearBuffer()
1594 return main.FALSE
1595 except Exception:
1596 main.log.exception( self.name + ": Uncaught exception!" )
1597 return main.FALSE
1598
Daniele Moroe1d05eb2021-09-23 19:52:30 +02001599 def kubectlDeletePod( self, podName, kubeconfig=None, namespace=None, timeout=240 ):
1600 try:
1601 cmdStr = "kubectl %s %s delete pod %s" % (
1602 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1603 "-n %s" % namespace if namespace else "",
1604 podName )
1605 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1606 self.handle.sendline( cmdStr )
1607 i = self.handle.expect( [ "not found", "error",
1608 "The connection to the server",
1609 self.prompt ],
1610 timeout=timeout )
1611 if i == 3:
1612 main.log.debug( self.name + ": " + self.handle.before )
1613 self.clearBuffer()
1614 return main.TRUE
1615 else:
1616 main.log.error( self.name + ": Error executing command" )
1617 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1618 self.clearBuffer()
1619 return main.FALSE
1620 except pexpect.EOF:
1621 main.log.error( self.name + ": EOF exception found" )
1622 main.log.error( self.name + ": " + self.handle.before )
1623 return main.FALSE
1624 except pexpect.TIMEOUT:
1625 main.log.exception( self.name + ": TIMEOUT exception found" )
1626 main.log.error( self.name + ": " + self.handle.before )
1627 return main.FALSE
1628 except Exception:
1629 main.log.exception( self.name + ": Uncaught exception!" )
1630 return main.FALSE
1631
1632 def kubectlCheckPodReady( self, podName, kubeconfig=None, namespace=None, timeout=240 ):
1633 try:
1634 cmdStr = "kubectl %s %s get pods " \
1635 "-o go-template='{{range $index, $element := .items}}{{range .status.containerStatuses}}{{if .ready}}{{$element.metadata.name}}{{\" ready\\n\"}}{{end}}{{end}}{{end}}' | grep --color=never %s" % (
1636 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1637 "-n %s" % namespace if namespace else "",
1638 podName )
1639 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1640 self.handle.sendline( cmdStr )
1641 # Since the command contains the prompt ($), we first expect for the
1642 # last part of the command and then we expect the actual values
Jon Halla16b4db2021-10-20 14:11:59 -07001643 self.handle.expect( "grep --color=never %s" % podName, timeout=1 )
Daniele Moroe1d05eb2021-09-23 19:52:30 +02001644 i = self.handle.expect( [ podName + " ready",
1645 self.prompt ],
1646 timeout=timeout )
1647 if i == 0:
1648 main.log.debug( self.name + ": " + podName + " ready" )
1649 self.clearBuffer()
1650 return main.TRUE
1651 else:
1652 main.log.error( self.name + ": Error executing command" )
1653 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1654 self.clearBuffer()
1655 return main.FALSE
1656 except pexpect.EOF:
1657 main.log.error( self.name + ": EOF exception found" )
1658 main.log.error( self.name + ": " + self.handle.before )
1659 return main.FALSE
1660 except pexpect.TIMEOUT:
1661 main.log.exception( self.name + ": TIMEOUT exception found" )
1662 main.log.error( self.name + ": " + self.handle.before )
1663 return main.FALSE
1664 except Exception:
1665 main.log.exception( self.name + ": Uncaught exception!" )
1666 return main.FALSE
1667
1668 def clearBuffer(self):
1669 i = 0
1670 response = ''
1671 while True:
1672 try:
1673 i += 1
1674 self.handle.expect( self.prompt, timeout=5 )
1675 response += self.cleanOutput( self.handle.before )
1676 except pexpect.TIMEOUT:
Jon Halla16b4db2021-10-20 14:11:59 -07001677 return response