blob: 40dfd0546118d4502a6b8021a97c215742e6b994 [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
1234 support a way to exit if cought up to present time. Defaults to 60 seconds
1235 Returns main.TRUE or
1236 main.FALSE on Error
1237 """
1238 import time
1239 try:
1240 cmdStr = "stern %s %s %s %s > %s " % (
1241 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1242 "-n %s" % namespace if namespace else "",
1243 "--since %s" % since if since else "",
1244 podString,
1245 dstPath )
1246 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1247 self.handle.sendline( cmdStr )
Jon Hall22a3bcf2021-07-23 11:40:11 -07001248 if int( wait ) >= 0:
1249 time.sleep( int( wait ) )
1250 self.handle.send( '\x03' ) # CTRL-C
1251 i = self.handle.expect( [ "not found", "Error: ", "The connection to the server", self.prompt ] )
1252 if i == 3:
1253 main.log.debug( self.name + ": " + self.handle.before )
1254 return main.TRUE
1255 else:
1256 main.log.error( self.name + ": Error executing command" )
1257 response = self.handle.before + str( self.handle.after )
1258 self.handle.expect( [ self.prompt, pexpect.TIMEOUT ], timeout=5 )
1259 response += self.handle.before + str( self.handle.after )
1260 main.log.debug( self.name + ": " + response )
1261 return main.FALSE
Jon Halla7b27e62021-06-29 12:13:51 -07001262 else:
Jon Hall22a3bcf2021-07-23 11:40:11 -07001263 self.preDisconnect = self.exitFromProcess
1264 return main.TRUE
Jon Halla7b27e62021-06-29 12:13:51 -07001265 except pexpect.EOF:
1266 main.log.error( self.name + ": EOF exception found" )
1267 main.log.error( self.name + ": " + self.handle.before )
1268 return main.FALSE
1269 except pexpect.TIMEOUT:
1270 main.log.exception( self.name + ": TIMEOUT exception found" )
1271 main.log.error( self.name + ": " + self.handle.before )
1272 return main.FALSE
1273 except Exception:
1274 main.log.exception( self.name + ": Uncaught exception!" )
1275 return main.FALSE
1276
Jon Hall06fd0df2021-01-25 15:50:06 -08001277 def kubectlLogs( self, podName, dstPath, kubeconfig=None, namespace=None, timeout=240 ):
1278 """
1279 Use kubectl to get the logs from a pod
1280 Required Arguments:
1281 - podName: The name of the pod to get the logs of
1282 - dstPath: The location to save the logs to
1283 Optional Arguments:
1284 - kubeconfig: The path to a kubeconfig file
1285 - namespace: The namespace to search in
1286 - timeout: Timeout for command to return. The longer the logs, the longer it will take to fetch them.
1287 Returns main.TRUE or
1288 main.FALSE on Error
1289 """
1290
1291 try:
1292 cmdStr = "kubectl %s %s logs %s > %s " % (
1293 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1294 "-n %s" % namespace if namespace else "",
1295 podName,
1296 dstPath )
1297 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1298 self.handle.sendline( cmdStr )
1299 i = self.handle.expect( [ "not found", "error", "The connection to the server", self.prompt ], timeout=timeout )
1300 if i == 3:
1301 main.log.debug( self.name + ": " + self.handle.before )
1302 return main.TRUE
1303 else:
1304 main.log.error( self.name + ": Error executing command" )
1305 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1306 return main.FALSE
1307 except pexpect.EOF:
1308 main.log.error( self.name + ": EOF exception found" )
1309 main.log.error( self.name + ": " + self.handle.before )
1310 return main.FALSE
1311 except pexpect.TIMEOUT:
1312 main.log.exception( self.name + ": TIMEOUT exception found" )
1313 main.log.error( self.name + ": " + self.handle.before )
1314 return main.FALSE
1315 except Exception:
1316 main.log.exception( self.name + ": Uncaught exception!" )
1317 return main.FALSE
1318
Jon Hallbe3a2ac2021-03-15 12:28:06 -07001319 def kubectlCp( self, podName, srcPath, dstPath, kubeconfig=None, namespace=None, timeout=240 ):
1320 """
1321 Use kubectl to get a file from a pod
1322 Required Arguments:
1323 - podName: The name of the pod to get the logs of
1324 - srcPath: The file to copy from the pod
1325 - dstPath: The location to save the file to locally
1326 Optional Arguments:
1327 - kubeconfig: The path to a kubeconfig file
1328 - namespace: The namespace to search in
1329 - timeout: Timeout for command to return. The longer the logs, the longer it will take to fetch them.
1330 Returns main.TRUE or
1331 main.FALSE on Error
1332 """
1333
1334 try:
1335 cmdStr = "kubectl %s %s cp %s:%s %s" % (
1336 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1337 "-n %s" % namespace if namespace else "",
1338 podName,
1339 srcPath,
1340 dstPath )
1341 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1342 self.handle.sendline( cmdStr )
1343 i = self.handle.expect( [ "not found", "error", "The connection to the server", self.prompt ], timeout=timeout )
1344 if i == 3:
1345 main.log.debug( self.name + ": " + self.handle.before )
1346 return main.TRUE
1347 else:
Jon Hall22a3bcf2021-07-23 11:40:11 -07001348 output = self.handle.before + str( self.handle.after )
Jon Hallbe3a2ac2021-03-15 12:28:06 -07001349 main.log.error( self.name + ": Error executing command" )
Jon Hall22a3bcf2021-07-23 11:40:11 -07001350 self.handle.expect( [ self.prompt, pexpect.TIMEOUT ] )
1351 output += self.handle.before + str( self.handle.after )
1352 main.log.debug( self.name + ": " + output )
Jon Hallbe3a2ac2021-03-15 12:28:06 -07001353 return main.FALSE
1354 except pexpect.EOF:
1355 main.log.error( self.name + ": EOF exception found" )
1356 main.log.error( self.name + ": " + self.handle.before )
1357 return main.FALSE
1358 except pexpect.TIMEOUT:
1359 main.log.exception( self.name + ": TIMEOUT exception found" )
1360 main.log.error( self.name + ": " + self.handle.before )
1361 return main.FALSE
1362 except Exception:
1363 main.log.exception( self.name + ": Uncaught exception!" )
1364 return main.FALSE
1365
Jon Halla16b4db2021-10-20 14:11:59 -07001366 def kubectlPortForward( self, podName, portsList, kubeconfig=None, namespace=None ):
Jon Hall06fd0df2021-01-25 15:50:06 -08001367 """
1368 Use kubectl to setup port forwarding from the local machine to the kubernetes pod
1369
Jon Halla16b4db2021-10-20 14:11:59 -07001370 Note: This cli command does not return until the port forwarding session is ended.
Jon Hall06fd0df2021-01-25 15:50:06 -08001371
1372 Required Arguments:
1373 - podName: The name of the pod as a string
1374 - portsList: The list of ports to forward, as a string. see kubectl help for details
1375 Optional Arguments:
1376 - kubeconfig: The path to a kubeconfig file
1377 - namespace: The namespace to search in
Jon Halla16b4db2021-10-20 14:11:59 -07001378 Returns main.TRUE if a port-forward session was created or main.FALSE on Error
Jon Hall06fd0df2021-01-25 15:50:06 -08001379
1380
1381 """
1382 try:
1383 cmdStr = "kubectl %s %s port-forward pod/%s %s" % (
1384 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1385 "-n %s" % namespace if namespace else "",
1386 podName,
1387 portsList )
1388 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1389 self.handle.sendline( cmdStr )
Jon Halla16b4db2021-10-20 14:11:59 -07001390 self.handle.expect( "pod/%s" % podName )
1391 output = self.handle.before + self.handle.after
Jon Hall06fd0df2021-01-25 15:50:06 -08001392 i = self.handle.expect( [ "not found", "error", "closed/timedout",
1393 self.prompt, "The connection to the server", "Forwarding from" ] )
Jon Halla16b4db2021-10-20 14:11:59 -07001394 output += self.handle.before + str( self.handle.after )
Jon Hall06fd0df2021-01-25 15:50:06 -08001395 # NOTE: This won't clear the buffer entirely, and each time the port forward
1396 # is used, another line will be added to the buffer. We need to make
1397 # sure we clear the buffer before using this component again.
1398
1399 if i == 5:
1400 # Setup preDisconnect function
1401 self.preDisconnect = self.exitFromProcess
Jon Halla16b4db2021-10-20 14:11:59 -07001402 self.portForwardList = portsList
Jon Hall06fd0df2021-01-25 15:50:06 -08001403 return main.TRUE
1404 else:
1405 main.log.error( self.name + ": Error executing command" )
Jon Halla16b4db2021-10-20 14:11:59 -07001406 main.log.debug( self.name + ": " + output )
Jon Hall06fd0df2021-01-25 15:50:06 -08001407 return main.FALSE
1408 except pexpect.EOF:
1409 main.log.error( self.name + ": EOF exception found" )
1410 main.log.error( self.name + ": " + self.handle.before )
1411 return main.FALSE
1412 except pexpect.TIMEOUT:
1413 main.log.exception( self.name + ": TIMEOUT exception found" )
1414 main.log.error( self.name + ": " + self.handle.before )
1415 return main.FALSE
1416 except Exception:
1417 main.log.exception( self.name + ": Uncaught exception!" )
1418 return main.FALSE
Daniele Moroe1d05eb2021-09-23 19:52:30 +02001419
Jon Halla16b4db2021-10-20 14:11:59 -07001420 def checkPortForward( self, podName, portsList=None, kubeconfig=None, namespace=None ):
1421 """
1422 Check that kubectl port-forward session is still active and restarts it if it was closed.
1423
1424
1425 Required Arguments:
1426 - podName: The name of the pod as a string
1427 - portsList: The list of ports to forward, as a string. see kubectl help for details. Deafults to
1428 the last used string on this node.
1429 Optional Arguments:
1430 - kubeconfig: The path to a kubeconfig file
1431 - namespace: The namespace to search in
1432 Returns main.TRUE if a port-forward session was created or is still active, main.FALSE on Error
1433
1434
1435 """
1436 try:
1437 if not portsList:
1438 portsList = self.portForwardList
1439 self.handle.sendline( "" )
1440 i = self.handle.expect( [ self.prompt, pexpect.TIMEOUT ], timeout=5 )
1441 output = self.handle.before + str( self.handle.after )
1442 main.log.debug( "%s: %s" % ( self.name, output ) )
1443 if i == 0:
1444 # We are not currently in a port-forwarding session, try to re-establish.
1445 return self.kubectlPortForward( podName, portsList, kubeconfig, namespace )
1446 elif i == 1:
1447 # Still in a command, port-forward is probably still active
1448 return main.TRUE
1449 except pexpect.EOF:
1450 main.log.error( self.name + ": EOF exception found" )
1451 main.log.error( self.name + ": " + self.handle.before )
1452 return main.FALSE
1453 except pexpect.TIMEOUT:
1454 main.log.exception( self.name + ": TIMEOUT exception found" )
1455 main.log.error( self.name + ": " + self.handle.before )
1456 return main.FALSE
1457 except Exception:
1458 main.log.exception( self.name + ": Uncaught exception!" )
1459 return main.FALSE
1460
Daniele Moro80271cb2021-11-11 20:08:51 +01001461 def kubectlSetLabel( self, nodeName, label, value, kubeconfig=None, namespace=None,
1462 timeout=240, overwrite=True ):
1463 try:
1464 cmdStr = "kubectl %s %s label node %s %s %s=%s" % (
1465 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1466 "-n %s" % namespace if namespace else "",
1467 nodeName, "--overwrite" if overwrite else "",
1468 label, value )
1469 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1470 self.handle.sendline( cmdStr )
1471 i = self.handle.expect( [ "error",
1472 "The connection to the server",
1473 "node/%s not labeled" % nodeName,
1474 "node/%s labeled" % 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
Jon Halla16b4db2021-10-20 14:11:59 -07001499 def kubectlCordonNode( self, nodeName, kubeconfig=None, namespace=None, timeout=240, uncordonOnDisconnect=True ):
1500 try:
1501 cmdStr = "kubectl %s %s cordon %s" % (
1502 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1503 "-n %s" % namespace if namespace else "",
1504 nodeName )
1505 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1506 if uncordonOnDisconnect:
1507 self.nodeName = nodeName
1508 if kubeconfig:
1509 self.kubeconfig = kubeconfig
1510 if namespace:
1511 self.namespace = namespace
1512 self.preDisconnect = self.kubectlUncordonNode
1513 self.handle.sendline( cmdStr )
1514 i = self.handle.expect( [ "not found", "error",
1515 "The connection to the server",
1516 "node/%s cordoned" % nodeName,
1517 "node/%s already cordoned" % nodeName, ],
1518 timeout=timeout )
1519 if i == 3 or i == 4:
1520 output = self.handle.before + self.handle.after
1521 main.log.debug( self.name + ": " + output )
1522 self.clearBuffer()
1523 return main.TRUE
1524 else:
1525 main.log.error( self.name + ": Error executing command" )
1526 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1527 self.clearBuffer()
1528 return main.FALSE
1529 except pexpect.EOF:
1530 main.log.error( self.name + ": EOF exception found" )
1531 main.log.error( self.name + ": " + self.handle.before )
1532 return main.FALSE
1533 except pexpect.TIMEOUT:
1534 main.log.exception( self.name + ": TIMEOUT exception found" )
1535 main.log.error( self.name + ": " + self.handle.before )
1536 self.clearBuffer()
1537 return main.FALSE
1538 except Exception:
1539 main.log.exception( self.name + ": Uncaught exception!" )
1540 return main.FALSE
1541
1542 def kubectlUncordonNode( self, nodeName=None, kubeconfig=None, namespace=None, timeout=240 ):
1543 try:
1544 if not nodeName:
1545 nodeName = getattr( self, "nodeName" )
1546 if not kubeconfig:
1547 kubeconfig = getattr( self, "kubeconfig", None )
1548 if not kubeconfig:
1549 namespace = getattr( self, "namespace", None )
1550 cmdStr = "kubectl %s %s uncordon %s" % (
1551 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1552 "-n %s" % namespace if namespace else "",
1553 nodeName )
1554 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1555 self.handle.sendline( cmdStr )
1556 i = self.handle.expect( [ "not found", "error",
1557 "The connection to the server",
1558 "node/%s uncordoned" % nodeName,
1559 "node/%s already uncordoned" % nodeName, ],
1560 timeout=timeout )
1561 if i == 3 or i == 4:
1562 output = self.handle.before + self.handle.after
1563 main.log.debug( self.name + ": " + output )
1564 self.clearBuffer()
1565 return main.TRUE
1566 else:
1567 main.log.error( self.name + ": Error executing command" )
1568 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1569 self.clearBuffer()
1570 return main.FALSE
1571 except pexpect.EOF:
1572 main.log.error( self.name + ": EOF exception found" )
1573 main.log.error( self.name + ": " + self.handle.before )
1574 return main.FALSE
1575 except pexpect.TIMEOUT:
1576 main.log.exception( self.name + ": TIMEOUT exception found" )
1577 main.log.error( self.name + ": " + self.handle.before )
1578 self.clearBuffer()
1579 return main.FALSE
1580 except Exception:
1581 main.log.exception( self.name + ": Uncaught exception!" )
1582 return main.FALSE
1583
Daniele Moroe1d05eb2021-09-23 19:52:30 +02001584 def kubectlDeletePod( self, podName, kubeconfig=None, namespace=None, timeout=240 ):
1585 try:
1586 cmdStr = "kubectl %s %s delete pod %s" % (
1587 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1588 "-n %s" % namespace if namespace else "",
1589 podName )
1590 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1591 self.handle.sendline( cmdStr )
1592 i = self.handle.expect( [ "not found", "error",
1593 "The connection to the server",
1594 self.prompt ],
1595 timeout=timeout )
1596 if i == 3:
1597 main.log.debug( self.name + ": " + self.handle.before )
1598 self.clearBuffer()
1599 return main.TRUE
1600 else:
1601 main.log.error( self.name + ": Error executing command" )
1602 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1603 self.clearBuffer()
1604 return main.FALSE
1605 except pexpect.EOF:
1606 main.log.error( self.name + ": EOF exception found" )
1607 main.log.error( self.name + ": " + self.handle.before )
1608 return main.FALSE
1609 except pexpect.TIMEOUT:
1610 main.log.exception( self.name + ": TIMEOUT exception found" )
1611 main.log.error( self.name + ": " + self.handle.before )
1612 return main.FALSE
1613 except Exception:
1614 main.log.exception( self.name + ": Uncaught exception!" )
1615 return main.FALSE
1616
1617 def kubectlCheckPodReady( self, podName, kubeconfig=None, namespace=None, timeout=240 ):
1618 try:
1619 cmdStr = "kubectl %s %s get pods " \
1620 "-o go-template='{{range $index, $element := .items}}{{range .status.containerStatuses}}{{if .ready}}{{$element.metadata.name}}{{\" ready\\n\"}}{{end}}{{end}}{{end}}' | grep --color=never %s" % (
1621 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1622 "-n %s" % namespace if namespace else "",
1623 podName )
1624 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1625 self.handle.sendline( cmdStr )
1626 # Since the command contains the prompt ($), we first expect for the
1627 # last part of the command and then we expect the actual values
Jon Halla16b4db2021-10-20 14:11:59 -07001628 self.handle.expect( "grep --color=never %s" % podName, timeout=1 )
Daniele Moroe1d05eb2021-09-23 19:52:30 +02001629 i = self.handle.expect( [ podName + " ready",
1630 self.prompt ],
1631 timeout=timeout )
1632 if i == 0:
1633 main.log.debug( self.name + ": " + podName + " ready" )
1634 self.clearBuffer()
1635 return main.TRUE
1636 else:
1637 main.log.error( self.name + ": Error executing command" )
1638 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1639 self.clearBuffer()
1640 return main.FALSE
1641 except pexpect.EOF:
1642 main.log.error( self.name + ": EOF exception found" )
1643 main.log.error( self.name + ": " + self.handle.before )
1644 return main.FALSE
1645 except pexpect.TIMEOUT:
1646 main.log.exception( self.name + ": TIMEOUT exception found" )
1647 main.log.error( self.name + ": " + self.handle.before )
1648 return main.FALSE
1649 except Exception:
1650 main.log.exception( self.name + ": Uncaught exception!" )
1651 return main.FALSE
1652
1653 def clearBuffer(self):
1654 i = 0
1655 response = ''
1656 while True:
1657 try:
1658 i += 1
1659 self.handle.expect( self.prompt, timeout=5 )
1660 response += self.cleanOutput( self.handle.before )
1661 except pexpect.TIMEOUT:
Jon Halla16b4db2021-10-20 14:11:59 -07001662 return response