blob: 9525325b2f6c7de637131cd2fb34c5f58adf256d [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 Hallca319892017-06-15 15:25:22 -070038
Jeremy Ronquillo82705492017-10-18 14:19:55 -070039 def checkPrompt( self ):
Devin Limdc78e202017-06-09 18:30:07 -070040 for key in self.options:
Jeremy Ronquillo82705492017-10-18 14:19:55 -070041 if key == "prompt" and self.options[ 'prompt' ] is not None:
42 self.prompt = self.options[ 'prompt' ]
Devin Limdc78e202017-06-09 18:30:07 -070043 break
kelvin8ec71442015-01-15 16:57:00 -080044
45 def connect( self, **connectargs ):
46 """
adminbae64d82013-08-01 10:50:15 -070047 Connection will establish to the remote host using ssh.
48 It will take user_name ,ip_address and password as arguments<br>
kelvin8ec71442015-01-15 16:57:00 -080049 and will return the handle.
50 """
Jon Hall06fd0df2021-01-25 15:50:06 -080051 self.shell = "/bin/bash -l"
adminbae64d82013-08-01 10:50:15 -070052 for key in connectargs:
kelvin8ec71442015-01-15 16:57:00 -080053 vars( self )[ key ] = connectargs[ key ]
Devin Limdc78e202017-06-09 18:30:07 -070054 self.checkPrompt()
adminbae64d82013-08-01 10:50:15 -070055
kelvin8ec71442015-01-15 16:57:00 -080056 connect_result = super( CLI, self ).connect()
adminbae64d82013-08-01 10:50:15 -070057 ssh_newkey = 'Are you sure you want to continue connecting'
kelvin8ec71442015-01-15 16:57:00 -080058 refused = "ssh: connect to host " + \
59 self.ip_address + " port 22: Connection refused"
Jon Hall06fd0df2021-01-25 15:50:06 -080060 ssh_options = "-t -X -A -o ServerAliveInterval=120 -o TCPKeepAlive=yes"
61 ssh_destination = self.user_name + "@" + self.ip_address
62 envVars = { "TERM": "vt100" }
63 # TODO: Add option to specify which shell/command to use
64 jump_host = main.componentDictionary[ self.name ].get( 'jump_host' )
adminbae64d82013-08-01 10:50:15 -070065 if self.port:
Jon Hall06fd0df2021-01-25 15:50:06 -080066 ssh_option += " -p " + self.port
67 if jump_host:
68 jump_host = main.componentDictionary.get( jump_host )
69 ssh_options += " -J %s@%s" % ( jump_host.get( 'user' ), jump_host.get( 'host' ) )
70 ssh_auth = os.getenv('SSH_AUTH_SOCK')
71 if ssh_auth:
72 envVars[ 'SSH_AUTH_SOCK' ] = ssh_auth
73 self.handle = pexpect.spawn(
74 "ssh %s %s %s" % ( ssh_options, ssh_destination, self.shell ),
75 env=envVars,
76 maxread=1000000,
77 timeout=60 )
adminbae64d82013-08-01 10:50:15 -070078
Jon Hall73057ee2016-08-23 09:57:26 -070079 # set tty window size
80 self.handle.setwinsize( 24, 250 )
81
adminbae64d82013-08-01 10:50:15 -070082 self.handle.logfile = self.logfile_handler
kelvin8ec71442015-01-15 16:57:00 -080083 i = 5
84 while i == 5:
Jon Hall4173b242017-09-12 17:04:38 -070085 i = self.handle.expect( [ ssh_newkey,
86 'password:|Password:',
87 pexpect.EOF,
88 pexpect.TIMEOUT,
89 refused,
90 'teston>',
Jon Halld9066132018-03-01 14:52:53 -080091 'Permission denied, please try again.',
Jon Hall4173b242017-09-12 17:04:38 -070092 self.prompt ],
93 120 )
acsmars32de0bc2015-06-30 09:57:12 -070094 if i == 0: # Accept key, then expect either a password prompt or access
Jon Hall3c0114c2020-08-11 15:07:42 -070095 main.log.info( self.name + ": ssh key confirmation received, send yes" )
kelvin8ec71442015-01-15 16:57:00 -080096 self.handle.sendline( 'yes' )
acsmars32de0bc2015-06-30 09:57:12 -070097 i = 5 # Run the loop again
acsmars07f9d392015-07-15 10:30:58 -070098 continue
99 if i == 1: # Password required
Jon Hall63604932015-02-26 17:09:50 -0800100 if self.pwd:
101 main.log.info(
Jon Hall4173b242017-09-12 17:04:38 -0700102 "ssh connection asked for password, gave password" )
Jon Hall63604932015-02-26 17:09:50 -0800103 else:
Jon Hall3c0114c2020-08-11 15:07:42 -0700104 main.log.info( self.name + ": Server asked for password, but none was "
acsmars07f9d392015-07-15 10:30:58 -0700105 "given in the .topo file. Trying "
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700106 "no password." )
acsmars07f9d392015-07-15 10:30:58 -0700107 self.pwd = ""
108 self.handle.sendline( self.pwd )
109 j = self.handle.expect( [
acsmars07f9d392015-07-15 10:30:58 -0700110 'password:|Password:',
Jon Halld9066132018-03-01 14:52:53 -0800111 'Permission denied, please try again.',
112 self.prompt,
acsmars07f9d392015-07-15 10:30:58 -0700113 pexpect.EOF,
114 pexpect.TIMEOUT ],
115 120 )
Jon Halld9066132018-03-01 14:52:53 -0800116 if j != 2:
Jon Hall3c0114c2020-08-11 15:07:42 -0700117 main.log.error( self.name + ": Incorrect Password" )
acsmars07f9d392015-07-15 10:30:58 -0700118 return main.FALSE
kelvin8ec71442015-01-15 16:57:00 -0800119 elif i == 2:
Jon Hall3c0114c2020-08-11 15:07:42 -0700120 main.log.error( self.name + ": Connection timeout" )
Jon Hall06fd0df2021-01-25 15:50:06 -0800121 main.log.debug( self.handle.before )
kelvin8ec71442015-01-15 16:57:00 -0800122 return main.FALSE
123 elif i == 3: # timeout
124 main.log.error(
125 "No route to the Host " +
126 self.user_name +
127 "@" +
128 self.ip_address )
Jon Hall06fd0df2021-01-25 15:50:06 -0800129 main.log.debug( self.handle.before )
kelvin8ec71442015-01-15 16:57:00 -0800130 return main.FALSE
131 elif i == 4:
132 main.log.error(
133 "ssh: connect to host " +
134 self.ip_address +
135 " port 22: Connection refused" )
Jon Hall06fd0df2021-01-25 15:50:06 -0800136 main.log.debug( self.handle.before )
kelvin8ec71442015-01-15 16:57:00 -0800137 return main.FALSE
Jon Halld9066132018-03-01 14:52:53 -0800138 elif i == 6: # Incorrect Password
Jon Hall3c0114c2020-08-11 15:07:42 -0700139 main.log.error( self.name + ": Incorrect Password" )
Jon Hall06fd0df2021-01-25 15:50:06 -0800140 main.log.debug( self.handle.before )
Jon Halld9066132018-03-01 14:52:53 -0800141 return main.FALSE
142 elif i == 7: # Prompt
Jon Hall3c0114c2020-08-11 15:07:42 -0700143 main.log.info( self.name + ": Password not required logged in" )
adminbae64d82013-08-01 10:50:15 -0700144
kelvin8ec71442015-01-15 16:57:00 -0800145 self.handle.sendline( "" )
Devin Limdc78e202017-06-09 18:30:07 -0700146 self.handle.expect( self.prompt )
Jeremy Ronquillo0f2008a2017-06-23 15:32:51 -0700147 self.handle.sendline( "cd" )
148 self.handle.expect( self.prompt )
adminbae64d82013-08-01 10:50:15 -0700149 return self.handle
150
kelvin8ec71442015-01-15 16:57:00 -0800151 def disconnect( self ):
Jon Hall06fd0df2021-01-25 15:50:06 -0800152 result = self.preDisconnect()
kelvin8ec71442015-01-15 16:57:00 -0800153 result = super( CLI, self ).disconnect( self )
adminbae64d82013-08-01 10:50:15 -0700154 result = main.TRUE
Jon Hall3c0114c2020-08-11 15:07:42 -0700155
156 def Prompt( self ):
157 """
158 Returns the prompt to expect depending on what program we are in
159 """
160 return self.prompt if not self.inDocker else self.dockerPrompt
kelvin8ec71442015-01-15 16:57:00 -0800161
162 def execute( self, **execparams ):
163 """
adminbae64d82013-08-01 10:50:15 -0700164 It facilitates the command line execution of a given command. It has arguments as :
165 cmd => represents command to be executed,
166 prompt => represents expect command prompt or output,
167 timeout => timeout for command execution,
168 more => to provide a key press if it is on.
You Wang7d14d642019-01-23 15:10:08 -0800169 logCmd => log the command executed if True
adminbae64d82013-08-01 10:50:15 -0700170
171 It will return output of command exection.
kelvin8ec71442015-01-15 16:57:00 -0800172 """
173 result = super( CLI, self ).execute( self )
adminaef00552014-05-08 09:18:36 -0700174 defaultPrompt = '.*[$>\#]'
Jon Hall3b489db2015-10-05 14:38:37 -0700175 args = utilities.parse_args( [ "CMD",
176 "TIMEOUT",
177 "PROMPT",
You Wang7d14d642019-01-23 15:10:08 -0800178 "MORE",
179 "LOGCMD" ],
Jon Hall3b489db2015-10-05 14:38:37 -0700180 **execparams )
kelvin8ec71442015-01-15 16:57:00 -0800181
182 expectPrompt = args[ "PROMPT" ] if args[ "PROMPT" ] else defaultPrompt
adminbae64d82013-08-01 10:50:15 -0700183 self.LASTRSP = ""
kelvin8ec71442015-01-15 16:57:00 -0800184 timeoutVar = args[ "TIMEOUT" ] if args[ "TIMEOUT" ] else 10
adminbae64d82013-08-01 10:50:15 -0700185 cmd = ''
kelvin8ec71442015-01-15 16:57:00 -0800186 if args[ "CMD" ]:
187 cmd = args[ "CMD" ]
188 else:
adminbae64d82013-08-01 10:50:15 -0700189 return 0
kelvin8ec71442015-01-15 16:57:00 -0800190 if args[ "MORE" ] is None:
191 args[ "MORE" ] = " "
192 self.handle.sendline( cmd )
adminbae64d82013-08-01 10:50:15 -0700193 self.lastCommand = cmd
Jon Hall3b489db2015-10-05 14:38:37 -0700194 index = self.handle.expect( [ expectPrompt,
195 "--More--",
196 'Command not found.',
197 pexpect.TIMEOUT,
198 "^:$" ],
199 timeout=timeoutVar )
adminbae64d82013-08-01 10:50:15 -0700200 if index == 0:
kelvin8ec71442015-01-15 16:57:00 -0800201 self.LASTRSP = self.LASTRSP + \
202 self.handle.before + self.handle.after
You Wang7d14d642019-01-23 15:10:08 -0800203 if not args[ "LOGCMD" ] is False:
Jon Hall3c0114c2020-08-11 15:07:42 -0700204 main.log.info( self.name + ": Executed :" + str( cmd ) +
You Wang7d14d642019-01-23 15:10:08 -0800205 " \t\t Expected Prompt '" + str( expectPrompt ) +
206 "' Found" )
adminbae64d82013-08-01 10:50:15 -0700207 elif index == 1:
208 self.LASTRSP = self.LASTRSP + self.handle.before
kelvin8ec71442015-01-15 16:57:00 -0800209 self.handle.send( args[ "MORE" ] )
210 main.log.info(
211 "Found More screen to go , Sending a key to proceed" )
212 indexMore = self.handle.expect(
213 [ "--More--", expectPrompt ], timeout=timeoutVar )
adminbae64d82013-08-01 10:50:15 -0700214 while indexMore == 0:
kelvin8ec71442015-01-15 16:57:00 -0800215 main.log.info(
216 "Found anoother More screen to go , Sending a key to proceed" )
217 self.handle.send( args[ "MORE" ] )
218 indexMore = self.handle.expect(
219 [ "--More--", expectPrompt ], timeout=timeoutVar )
adminbae64d82013-08-01 10:50:15 -0700220 self.LASTRSP = self.LASTRSP + self.handle.before
kelvin8ec71442015-01-15 16:57:00 -0800221 elif index == 2:
Jon Hall3c0114c2020-08-11 15:07:42 -0700222 main.log.error( self.name + ": Command not found" )
adminbae64d82013-08-01 10:50:15 -0700223 self.LASTRSP = self.LASTRSP + self.handle.before
kelvin8ec71442015-01-15 16:57:00 -0800224 elif index == 3:
Jon Hall3c0114c2020-08-11 15:07:42 -0700225 main.log.error( self.name + ": Expected Prompt not found, Time Out!!" )
kelvin8ec71442015-01-15 16:57:00 -0800226 main.log.error( expectPrompt )
Jon Hall3b489db2015-10-05 14:38:37 -0700227 self.LASTRSP = self.LASTRSP + self.handle.before
228 return self.LASTRSP
adminbae64d82013-08-01 10:50:15 -0700229 elif index == 4:
230 self.LASTRSP = self.LASTRSP + self.handle.before
kelvin8ec71442015-01-15 16:57:00 -0800231 # self.handle.send( args[ "MORE" ] )
232 self.handle.sendcontrol( "D" )
233 main.log.info(
Jon Hall3b489db2015-10-05 14:38:37 -0700234 "Found More screen to go, Sending a key to proceed" )
kelvin8ec71442015-01-15 16:57:00 -0800235 indexMore = self.handle.expect(
236 [ "^:$", expectPrompt ], timeout=timeoutVar )
adminbae64d82013-08-01 10:50:15 -0700237 while indexMore == 0:
kelvin8ec71442015-01-15 16:57:00 -0800238 main.log.info(
Jon Hall3b489db2015-10-05 14:38:37 -0700239 "Found another More screen to go, Sending a key to proceed" )
kelvin8ec71442015-01-15 16:57:00 -0800240 self.handle.sendcontrol( "D" )
241 indexMore = self.handle.expect(
242 [ "^:$", expectPrompt ], timeout=timeoutVar )
adminbae64d82013-08-01 10:50:15 -0700243 self.LASTRSP = self.LASTRSP + self.handle.before
kelvin8ec71442015-01-15 16:57:00 -0800244 main.last_response = self.remove_contol_chars( self.LASTRSP )
adminbae64d82013-08-01 10:50:15 -0700245 return self.LASTRSP
kelvin8ec71442015-01-15 16:57:00 -0800246
247 def remove_contol_chars( self, response ):
248 # 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 ) )
249 # response = re.sub( RE_XML_ILLEGAL, "\n", response )
250 response = re.sub( r"[\x01-\x1F\x7F]", "", response )
251 # response = re.sub( r"\[\d+\;1H", "\n", response )
252 response = re.sub( r"\[\d+\;\d+H", "", response )
adminbae64d82013-08-01 10:50:15 -0700253 return response
adminbae64d82013-08-01 10:50:15 -0700254
kelvin8ec71442015-01-15 16:57:00 -0800255 def runAsSudoUser( self, handle, pwd, default ):
256
257 i = handle.expect( [ ".ssword:*", default, pexpect.EOF ] )
258 if i == 0:
259 handle.sendline( pwd )
Jon Hall5ec6b1b2015-09-17 18:20:14 -0700260 handle.sendline( "\n" )
kelvin8ec71442015-01-15 16:57:00 -0800261
262 if i == 1:
263 handle.expect( default )
264
265 if i == 2:
Jon Hall3c0114c2020-08-11 15:07:42 -0700266 main.log.error( self.name + ": Unable to run as Sudo user" )
kelvin8ec71442015-01-15 16:57:00 -0800267
adminbae64d82013-08-01 10:50:15 -0700268 return handle
adminbae64d82013-08-01 10:50:15 -0700269
kelvin8ec71442015-01-15 16:57:00 -0800270 def onfail( self ):
271 if 'onfail' in main.componentDictionary[ self.name ]:
272 commandList = main.componentDictionary[
273 self.name ][ 'onfail' ].split( "," )
274 for command in commandList:
275 response = self.execute(
276 cmd=command,
277 prompt="(.*)",
278 timeout=120 )
adminbae64d82013-08-01 10:50:15 -0700279
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700280 def secureCopy( self, userName, ipAddress, filePath, dstPath, pwd="",
Jon Hall39570262020-11-17 12:18:19 -0800281 direction="from", options="" ):
kelvin8ec71442015-01-15 16:57:00 -0800282 """
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700283 Definition:
284 Execute scp command in linux to copy to/from a remote host
285 Required:
286 str userName - User name of the remote host
287 str ipAddress - IP address of the remote host
288 str filePath - File path including the file it self
289 str dstPath - Destination path
290 Optional:
291 str pwd - Password of the host
292 str direction - Direction of the scp, default to "from" which means
293 copy "from" the remote machine to local machine,
294 while "to" means copy "to" the remote machine from
295 local machine
kelvin8ec71442015-01-15 16:57:00 -0800296 """
Jon Hall669bc862021-03-09 12:24:44 -0800297 returnVal = main.FALSE
adminbae64d82013-08-01 10:50:15 -0700298 ssh_newkey = 'Are you sure you want to continue connecting'
kelvin8ec71442015-01-15 16:57:00 -0800299 refused = "ssh: connect to host " + \
Jon Hall547e0582015-09-21 17:35:40 -0700300 ipAddress + " port 22: Connection refused"
Jon Hall39570262020-11-17 12:18:19 -0800301 cmd = "scp %s " % options
Jon Hall669bc862021-03-09 12:24:44 -0800302 try:
303 self.handle.sendline( "" )
304 self.handle.expect( self.prompt, timeout=5 )
305 except pexpect.TIMEOUT:
306 main.log.error( "%s: Component not ready for input" % self.name )
307 main.log.debug( "%s: %s%s" % ( self.name, self.handle.before, str( self.handle.after ) ) )
308 self.handle.send( "\x03" ) # CTRL-C
309 self.handle.expect( self.prompt, timeout=5 )
acsmars07f9d392015-07-15 10:30:58 -0700310
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700311 if direction == "from":
Jon Hall39570262020-11-17 12:18:19 -0800312 cmd = cmd + str( userName ) + '@' + str( ipAddress ) + ':' + \
Jon Hall547e0582015-09-21 17:35:40 -0700313 str( filePath ) + ' ' + str( dstPath )
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700314 elif direction == "to":
Jon Hall39570262020-11-17 12:18:19 -0800315 cmd = cmd + str( filePath ) + ' ' + str( userName ) + \
Jon Hall547e0582015-09-21 17:35:40 -0700316 '@' + str( ipAddress ) + ':' + str( dstPath )
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700317 else:
318 main.log.debug( "Wrong direction using secure copy command!" )
319 return main.FALSE
kelvin8ec71442015-01-15 16:57:00 -0800320
Jon Hall3c0114c2020-08-11 15:07:42 -0700321 main.log.info( self.name + ": Sending: " + cmd )
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700322 self.handle.sendline( cmd )
Jon Hall547e0582015-09-21 17:35:40 -0700323 i = 0
Jon Hall669bc862021-03-09 12:24:44 -0800324 timeout = 120
Jon Hall66ce22f2021-06-30 14:57:40 -0700325 hit = False
Jon Hall669bc862021-03-09 12:24:44 -0800326 while i <= 6 :
Jon Hall547e0582015-09-21 17:35:40 -0700327 i = self.handle.expect( [
328 ssh_newkey,
329 'password:',
330 "100%",
331 refused,
332 "No such file or directory",
Jon Hall53c5e662016-04-13 16:06:56 -0700333 "Permission denied",
Devin Limdc78e202017-06-09 18:30:07 -0700334 self.prompt,
Jon Hall547e0582015-09-21 17:35:40 -0700335 pexpect.EOF,
336 pexpect.TIMEOUT ],
Jon Hall669bc862021-03-09 12:24:44 -0800337 timeout=timeout )
Jon Hall547e0582015-09-21 17:35:40 -0700338 if i == 0: # ask for ssh key confirmation
Jon Hall66ce22f2021-06-30 14:57:40 -0700339 hit = True
Jon Hall3c0114c2020-08-11 15:07:42 -0700340 main.log.info( self.name + ": ssh key confirmation received, sending yes" )
Jon Hall547e0582015-09-21 17:35:40 -0700341 self.handle.sendline( 'yes' )
342 elif i == 1: # Asked for ssh password
Jon Hall66ce22f2021-06-30 14:57:40 -0700343 hit = True
Jon Hall669bc862021-03-09 12:24:44 -0800344 timeout = 120
Jon Hall3c0114c2020-08-11 15:07:42 -0700345 main.log.info( self.name + ": ssh connection asked for password, gave password" )
Jon Hall547e0582015-09-21 17:35:40 -0700346 self.handle.sendline( pwd )
347 elif i == 2: # File finished transfering
Jon Hall66ce22f2021-06-30 14:57:40 -0700348 hit = True
Jon Hall3c0114c2020-08-11 15:07:42 -0700349 main.log.info( self.name + ": Secure copy successful" )
Jon Hall669bc862021-03-09 12:24:44 -0800350 timeout = 10
Jon Hall547e0582015-09-21 17:35:40 -0700351 returnVal = main.TRUE
352 elif i == 3: # Connection refused
Jon Hall66ce22f2021-06-30 14:57:40 -0700353 hit = True
Jon Hall547e0582015-09-21 17:35:40 -0700354 main.log.error(
355 "ssh: connect to host " +
356 ipAddress +
357 " port 22: Connection refused" )
358 returnVal = main.FALSE
359 elif i == 4: # File Not found
Jon Hall66ce22f2021-06-30 14:57:40 -0700360 hit = True
Jon Hall3c0114c2020-08-11 15:07:42 -0700361 main.log.error( self.name + ": No such file found" )
Jon Hall39570262020-11-17 12:18:19 -0800362 main.log.debug( self.handle.before + self.handle.after )
Jon Hall547e0582015-09-21 17:35:40 -0700363 returnVal = main.FALSE
Jon Hall53c5e662016-04-13 16:06:56 -0700364 elif i == 5: # Permission denied
Jon Hall66ce22f2021-06-30 14:57:40 -0700365 hit = True
Jon Hall3c0114c2020-08-11 15:07:42 -0700366 main.log.error( self.name + ": Permission denied. Check folder permissions" )
Jon Hall39570262020-11-17 12:18:19 -0800367 main.log.debug( self.handle.before + self.handle.after )
Jon Hall53c5e662016-04-13 16:06:56 -0700368 returnVal = main.FALSE
369 elif i == 6: # prompt returned
Jon Hall66ce22f2021-06-30 14:57:40 -0700370 hit = True
Jon Hall669bc862021-03-09 12:24:44 -0800371 timeout = 10
372 main.log.debug( "%s: %s%s" % ( self.name, repr( self.handle.before ), repr( self.handle.after ) ) )
Jon Hall53c5e662016-04-13 16:06:56 -0700373 elif i == 7: # EOF
Jon Hall66ce22f2021-06-30 14:57:40 -0700374 hit = True
Jon Hall3c0114c2020-08-11 15:07:42 -0700375 main.log.error( self.name + ": Pexpect.EOF found!!!" )
Devin Lim44075962017-08-11 10:56:37 -0700376 main.cleanAndExit()
Jon Hall53c5e662016-04-13 16:06:56 -0700377 elif i == 8: # timeout
Jon Hall66ce22f2021-06-30 14:57:40 -0700378 if not hit:
Jon Hall669bc862021-03-09 12:24:44 -0800379 main.log.error(
380 "No route to the Host " +
381 userName +
382 "@" +
383 ipAddress )
384 return returnVal
385 self.handle.expect( [ self.prompt, pexpect.TIMEOUT ], timeout=5 )
386 main.log.debug( "%s: %s%s" % ( self.name, repr( self.handle.before ), repr( self.handle.after ) ) )
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700387 return returnVal
388
Jon Hall39570262020-11-17 12:18:19 -0800389 def scp( self, remoteHost, filePath, dstPath, direction="from", options="" ):
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700390 """
391 Definition:
392 Execute scp command in linux to copy to/from a remote host
393 Required:
394 * remoteHost - Test ON component to be parsed
395 str filePath - File path including the file it self
396 str dstPath - Destination path
397 Optional:
398 str direction - Direction of the scp, default to "from" which means
399 copy "from" the remote machine to local machine,
400 while "to" means copy "to" the remote machine from
401 local machine
402 """
Jon Hall06fd0df2021-01-25 15:50:06 -0800403 jump_host = main.componentDictionary[ remoteHost.name ].get( 'jump_host' )
404 if jump_host:
405 jump_host = main.componentDictionary.get( jump_host )
Jon Hall669bc862021-03-09 12:24:44 -0800406 options += " -o 'ProxyJump %s@%s' " % ( jump_host.get( 'user' ), jump_host.get( 'host' ) )
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700407 return self.secureCopy( remoteHost.user_name,
408 remoteHost.ip_address,
409 filePath,
410 dstPath,
411 pwd=remoteHost.pwd,
Jon Hall39570262020-11-17 12:18:19 -0800412 direction=direction,
413 options=options )
Devin Lim142b5342017-07-20 15:22:39 -0700414
415 def sshToNode( self, ipAddress, uName="sdn", pwd="rocks" ):
416 ssh_newkey = 'Are you sure you want to continue connecting'
417 refused = "ssh: connect to host " + ipAddress + " port 22: Connection refused"
418 handle = pexpect.spawn( 'ssh -X ' +
419 uName +
420 '@' +
421 ipAddress,
Jon Hall6c9e2da2018-11-06 12:01:23 -0800422 env={ "TERM": "vt100" },
Devin Lim142b5342017-07-20 15:22:39 -0700423 maxread=1000000,
424 timeout=60 )
425
426 # set tty window size
427 handle.setwinsize( 24, 250 )
428
429 i = 5
430 while i == 5:
Jon Hall4173b242017-09-12 17:04:38 -0700431 i = handle.expect( [ ssh_newkey,
432 'password:|Password:',
433 pexpect.EOF,
434 pexpect.TIMEOUT,
435 refused,
436 'teston>',
437 self.prompt ],
438 120 )
Devin Lim142b5342017-07-20 15:22:39 -0700439 if i == 0: # Accept key, then expect either a password prompt or access
Jon Hall3c0114c2020-08-11 15:07:42 -0700440 main.log.info( self.name + ": ssh key confirmation received, send yes" )
Devin Lim142b5342017-07-20 15:22:39 -0700441 handle.sendline( 'yes' )
442 i = 5 # Run the loop again
443 continue
444 if i == 1: # Password required
445 if pwd:
446 main.log.info(
Jon Hall4173b242017-09-12 17:04:38 -0700447 "ssh connection asked for password, gave password" )
Devin Lim142b5342017-07-20 15:22:39 -0700448 else:
Jon Hall3c0114c2020-08-11 15:07:42 -0700449 main.log.info( self.name + ": Server asked for password, but none was "
Devin Lim142b5342017-07-20 15:22:39 -0700450 "given in the .topo file. Trying "
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700451 "no password." )
Devin Lim142b5342017-07-20 15:22:39 -0700452 pwd = ""
453 handle.sendline( pwd )
454 j = handle.expect( [ self.prompt,
455 'password:|Password:',
456 pexpect.EOF,
457 pexpect.TIMEOUT ],
458 120 )
459 if j != 0:
Jon Hall3c0114c2020-08-11 15:07:42 -0700460 main.log.error( self.name + ": Incorrect Password" )
Devin Lim44075962017-08-11 10:56:37 -0700461 main.cleanAndExit()
Devin Lim142b5342017-07-20 15:22:39 -0700462 elif i == 2:
Jon Hall3c0114c2020-08-11 15:07:42 -0700463 main.log.error( self.name + ": Connection timeout" )
Devin Lim44075962017-08-11 10:56:37 -0700464 main.cleanAndExit()
Devin Lim142b5342017-07-20 15:22:39 -0700465 elif i == 3: # timeout
466 main.log.error(
467 "No route to the Host " +
468 uName +
469 "@" +
470 ipAddress )
Devin Lim44075962017-08-11 10:56:37 -0700471 main.cleanAndExit()
Devin Lim142b5342017-07-20 15:22:39 -0700472 elif i == 4:
473 main.log.error(
474 "ssh: connect to host " +
475 ipAddress +
476 " port 22: Connection refused" )
Devin Lim44075962017-08-11 10:56:37 -0700477 main.cleanAndExit()
Devin Lim142b5342017-07-20 15:22:39 -0700478 elif i == 6:
Jon Hall3c0114c2020-08-11 15:07:42 -0700479 main.log.info( self.name + ": Password not required logged in" )
Devin Lim142b5342017-07-20 15:22:39 -0700480
481 handle.sendline( "" )
482 handle.expect( self.prompt )
483 handle.sendline( "cd" )
484 handle.expect( self.prompt )
485
Jon Hall3c0114c2020-08-11 15:07:42 -0700486 main.log.info( self.name + ": Successfully ssh to " + ipAddress + "." )
Devin Lim142b5342017-07-20 15:22:39 -0700487 return handle
488
489 def exitFromSsh( self, handle, ipAddress ):
Devin Lim142b5342017-07-20 15:22:39 -0700490 try:
Jon Hall4f360bc2017-09-07 10:19:52 -0700491 handle.sendline( "logout" )
Devin Lim142b5342017-07-20 15:22:39 -0700492 handle.expect( "closed." )
Jon Hall3c0114c2020-08-11 15:07:42 -0700493 main.log.info( self.name + ": Successfully closed ssh connection from " + ipAddress )
Devin Lim142b5342017-07-20 15:22:39 -0700494 except pexpect.EOF:
Jon Hall3c0114c2020-08-11 15:07:42 -0700495 main.log.error( self.name + ": Failed to close the connection from " + ipAddress )
Jon Hall4f360bc2017-09-07 10:19:52 -0700496 try:
497 # check that this component handle still works
498 self.handle.sendline( "" )
499 self.handle.expect( self.prompt )
500 except pexpect.EOF:
501 main.log.error( self.handle.before )
Jon Hall3c0114c2020-08-11 15:07:42 -0700502 main.log.error( self.name + ": EOF after closing ssh connection" )
Jon Hall4173b242017-09-12 17:04:38 -0700503
504 def folderSize( self, path, size='10', unit='M', ignoreRoot=True ):
505 """
506 Run `du -h` on the folder path and verifies the folder(s) size is
507 less than the given size. Note that if multiple subdirectories are
508 present, the result will be the OR of all the individual subdirectories.
509
510 Arguments:
511 path - A string containing the path supplied to the du command
512 size - The number portion of the file size that the results will be compared to
513 unit - The unit portion of the file size that the results will be compared to
514 ignoreRoot - If True, will ignore the "root" of the path supplied to du. I.E. will ignore `.`
515
516 Returns True if the folder(s) size(s) are less than SIZE UNITS, else returns False
517 """
518 sizeRe = r'(?P<number>\d+\.*\d*)(?P<unit>\D)'
519 unitsList = [ 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y' ]
520 try:
521 # make sure we convert units if size is too big
522 size = float( size )
523 if size >= 1000:
524 size = size / 1000
525 unit = unitsList[ unitsList.index( unit + 1 ) ]
526 cmdStr = "du -h " + path
527 self.handle.sendline( cmdStr )
528 self.handle.expect( self.prompt )
529 output = self.handle.before
530 assert "cannot access" not in output
531 assert "command not found" not in output
532 main.log.debug( output )
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700533 lines = [ line for line in output.split( '\r\n' ) ]
Jon Hall4173b242017-09-12 17:04:38 -0700534 retValue = True
535 if ignoreRoot:
536 lastIndex = -2
537 else:
538 lastIndex = -1
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700539 for line in lines[ 1:lastIndex ]:
Jon Hall4173b242017-09-12 17:04:38 -0700540 parsed = line.split()
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700541 sizeMatch = parsed[ 0 ]
542 folder = parsed[ 1 ]
Jon Hall4173b242017-09-12 17:04:38 -0700543 match = re.search( sizeRe, sizeMatch )
544 num = match.group( 'number' )
545 unitMatch = match.group( 'unit' )
546 if unitsList.index( unitMatch ) < unitsList.index( unit ):
547 retValue &= True
548 elif unitsList.index( unitMatch ) == unitsList.index( unit ):
549 if float( num ) < float( size ):
550 retValue &= True
551 else:
552 retValue &= False
553 elif unitsList.index( unitMatch ) > unitsList.index( unit ):
554 retValue &= False
555 return retValue
556 except AssertionError:
557 main.log.error( self.name + ": Could not execute command: " + output )
558 return False
Jon Hall43060f62020-06-23 13:13:33 -0700559 except ValueError as e:
560 main.log.error( self.name + ": Error parsing output: " + output )
561 main.log.error( e )
562 return False
Jon Hall4173b242017-09-12 17:04:38 -0700563 except pexpect.TIMEOUT:
564 main.log.exception( self.name + ": TIMEOUT exception found" )
565 main.log.error( self.name + ": " + self.handle.before )
566 return False
567 except pexpect.EOF:
568 main.log.error( self.name + ": EOF exception found" )
569 main.log.error( self.name + ": " + self.handle.before )
570 main.cleanAndExit()
Jon Hall0e240372018-05-02 11:21:57 -0700571
572 def setEnv( self, variable, value=None ):
573 """
574 Sets the environment variable to the given value for the current shell session.
575 If value is None, will unset the variable.
576
577 Required Arguments:
578 variable - The name of the environment variable to set.
579
580 Optional Arguments:
581 value - The value to set the variable to. ( Defaults to None, which unsets the variable )
582
583 Returns True if no errors are detected else returns False
584 """
585 try:
586 if value:
587 cmd = "export {}={}".format( variable, value )
588 else:
589 cmd = "unset {}".format( variable )
590 self.handle.sendline( cmd )
591 self.handle.expect( self.prompt )
Jon Hall3c0114c2020-08-11 15:07:42 -0700592 output = self.handle.before
593 main.log.debug( output )
Jon Hall0e240372018-05-02 11:21:57 -0700594 return True
595 except AssertionError:
596 main.log.error( self.name + ": Could not execute command: " + output )
597 return False
598 except pexpect.TIMEOUT:
599 main.log.exception( self.name + ": TIMEOUT exception found" )
600 main.log.error( self.name + ": " + self.handle.before )
601 return False
602 except pexpect.EOF:
603 main.log.error( self.name + ": EOF exception found" )
604 main.log.error( self.name + ": " + self.handle.before )
605 main.cleanAndExit()
You Wangb65d2372018-08-17 15:37:59 -0700606
607 def exitFromCmd( self, expect, retry=10 ):
608 """
609 Call this function when sending ctrl+c is required to kill the current
610 command. It will retry multiple times until the running command is
611 completely killed and expected string is returned from the handle.
612 Required:
You Wangd4fae5c2018-08-22 13:56:49 -0700613 expect: expected string or list of strings which indicates that the
614 previous command was killed successfully.
You Wangb65d2372018-08-17 15:37:59 -0700615 Optional:
616 retry: maximum number of ctrl+c that will be sent.
617 """
You Wangd4fae5c2018-08-22 13:56:49 -0700618 expect = [ expect ] if isinstance( expect, str ) else expect
You Wangb65d2372018-08-17 15:37:59 -0700619 try:
620 while retry >= 0:
621 main.log.debug( self.name + ": sending ctrl+c to kill the command" )
622 self.handle.send( "\x03" )
You Wangd4fae5c2018-08-22 13:56:49 -0700623 i = self.handle.expect( expect + [ pexpect.TIMEOUT ], timeout=3 )
You Wangb65d2372018-08-17 15:37:59 -0700624 main.log.debug( self.handle.before )
You Wangd4fae5c2018-08-22 13:56:49 -0700625 if i < len( expect ):
You Wangb65d2372018-08-17 15:37:59 -0700626 main.log.debug( self.name + ": successfully killed the command" )
627 return main.TRUE
628 retry -= 1
629 main.log.warn( self.name + ": failed to kill the command" )
630 return main.FALSE
631 except pexpect.EOF:
632 main.log.error( self.name + ": EOF exception found" )
633 main.log.error( self.name + ": " + self.handle.before )
634 return main.FALSE
635 except Exception:
636 main.log.exception( self.name + ": Uncaught exception!" )
637 return main.FALSE
Jon Hall43060f62020-06-23 13:13:33 -0700638
639 def cleanOutput( self, output, debug=False ):
640 """
641 Clean ANSI characters from output
642 """
643 ansiEscape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
644 cleaned = ansiEscape.sub( '', output )
645 if debug:
646 main.log.debug( self.name + ": cleanOutput:" )
647 main.log.debug( self.name + ": " + repr( cleaned ) )
648 return cleaned
Jon Hall3c0114c2020-08-11 15:07:42 -0700649
650 def dockerPull( self, image, tag=None ):
651 """
652 Pull a docker image from a registry
653 """
654 try:
655 imgStr = "%s%s" % ( image, ":%s" % tag if tag else "" )
656 cmdStr = "docker pull %s" % imgStr
657 main.log.info( self.name + ": sending: " + cmdStr )
658 self.handle.sendline( cmdStr)
659 i = self.handle.expect( [ self.prompt,
660 "Error response from daemon",
661 pexpect.TIMEOUT ], 120 )
662 if i == 0:
663 return main.TRUE
664 else:
665 main.log.error( self.name + ": Error pulling docker image " + imgStr )
666 output = self.handle.before + str( self.handle.after )
667 if i == 1:
668 self.handle.expect( self.prompt )
669 output += self.handle.before + str( self.handle.after )
670 main.log.debug( self.name + ": " + output )
671 return main.FALSE
672 except pexpect.EOF:
673 main.log.error( self.name + ": EOF exception found" )
674 main.log.error( self.name + ": " + self.handle.before )
675 return main.FALSE
676 except Exception:
677 main.log.exception( self.name + ": Uncaught exception!" )
678 return main.FALSE
679
680 def dockerBuild( self, path, imageTag, pull=False, options="", timeout=600 ):
681 """
682 Build a docker image
683 Required Arguments:
684 - path: Path to the dockerfile, it is recommended to avoid relative paths
685 - imageTag: Give a tag to the built docker image
686 Optional Arguments:
687 - pull: Whether to attempt to pull latest images before building
688 - options: A string containing any addition optional arguments
689 for the docker build command
690 - timeout: How many seconds to wait for the build to complete
691 """
692 try:
693 response = main.TRUE
694 if pull:
695 options = "--pull " + options
696 cmdStr = "docker build -t %s %s %s" % ( imageTag, options, path )
697 main.log.info( self.name + ": sending: " + cmdStr )
698 self.handle.sendline( cmdStr)
699 i = self.handle.expect( [ "Successfully built",
700 "Error response from daemon",
701 pexpect.TIMEOUT ], timeout=timeout )
702 output = self.handle.before
703 if i == 0:
704 output += self.handle.after
705 self.handle.expect( self.prompt )
706 output += self.handle.before + self.handle.after
707 return response
708 elif i == 1:
709 response = main.FALSE
710 output += self.handle.after
711 self.handle.expect( self.prompt )
712 output += self.handle.before + self.handle.after
713 elif i == 2:
714 response = main.FALSE
715 main.log.error( self.name + ": Error building docker image" )
716 main.log.debug( self.name + ": " + output )
717 return response
718 except pexpect.EOF:
719 main.log.error( self.name + ": EOF exception found" )
720 main.log.error( self.name + ": " + self.handle.before )
721 return main.FALSE
722 except Exception:
723 main.log.exception( self.name + ": Uncaught exception!" )
724 return main.FALSE
725
726 def dockerStop( self, containerName ):
727 """
728 Stop a docker container
729 Required Arguments:
730 - containerName: Name of the container to stop
731 """
732 try:
733 cmdStr = "docker stop %s" % ( containerName )
734 main.log.info( self.name + ": sending: " + cmdStr )
735 self.handle.sendline( cmdStr)
736 i = self.handle.expect( [ self.prompt,
737 "Error response from daemon",
738 pexpect.TIMEOUT ], 120 )
739 output = self.handle.before
740 if i == 0:
741 return main.TRUE
742 elif i == 1:
743 output += self.handle.after
744 self.handle.expect( self.prompt )
745 output += self.handle.before
746 elif i == 2:
747 pass
748 main.log.debug( "%s: %s" % ( self.name, output ) )
749 if "No such container" in output:
750 return main.TRUE
751 main.log.error( self.name + ": Error stopping docker image" )
752 main.log.debug( self.name + ": " + output )
753 return main.FALSE
754 except pexpect.EOF:
755 main.log.error( self.name + ": EOF exception found" )
756 main.log.error( self.name + ": " + self.handle.before )
757 return main.FALSE
758 except Exception:
759 main.log.exception( self.name + ": Uncaught exception!" )
760 return main.FALSE
761
Jon Hall06fd0df2021-01-25 15:50:06 -0800762 def dockerRun( self, image, containerName, options="", imageArgs="", background=False ):
Jon Hall3c0114c2020-08-11 15:07:42 -0700763 """
764 Run a docker image
765 Required Arguments:
766 - containerName: Give a name to the container once its started
767 - image: Run the given image
768 Optional Arguments:
769 - options: A string containing any addition optional arguments
770 for the docker run command
771 - imageArgs: A string containing command line arguments for the
772 command run by docker
773 """
774 try:
775 cmdStr = "docker run --name %s %s %s %s" % ( containerName,
776 options if options else "",
777 image,
778 imageArgs )
Jon Hall06fd0df2021-01-25 15:50:06 -0800779 if background:
780 cmdStr += " &"
Jon Hall3c0114c2020-08-11 15:07:42 -0700781 main.log.info( self.name + ": sending: " + cmdStr )
782 self.handle.sendline( cmdStr)
783 i = self.handle.expect( [ self.prompt,
784 "Error response from daemon",
785 pexpect.TIMEOUT ], 120 )
786 if i == 0:
787 return main.TRUE
788 else:
789 output = self.handle.before
790 main.log.debug( self.name + ": " + output )
791 main.log.error( self.name + ": Error running docker image" )
792 if i == 1:
793 output += self.handle.after
794 self.handle.expect( self.prompt )
795 output += self.handle.before + self.handle.after
796 main.log.debug( self.name + ": " + output )
797 return main.FALSE
798 except pexpect.EOF:
799 main.log.error( self.name + ": EOF exception found" )
800 main.log.error( self.name + ": " + self.handle.before )
801 return main.FALSE
802 except Exception:
803 main.log.exception( self.name + ": Uncaught exception!" )
804 return main.FALSE
805
806 def dockerAttach( self, containerName, dockerPrompt="" ):
807 """
808 Attach to a docker image
809 Required Arguments:
810 - containerName: The name of the container to attach to
811 Optional Arguments:
812 - dockerPrompt: a regex for matching the docker shell prompt
813 """
814 try:
815 if dockerPrompt:
816 self.dockerPrompt = dockerPrompt
817 cmdStr = "docker attach %s" % containerName
818 main.log.info( self.name + ": sending: " + cmdStr )
819 self.handle.sendline( cmdStr)
820 i = self.handle.expect( [ self.dockerPrompt,
821 "Error response from daemon",
822 pexpect.TIMEOUT ] )
823 if i == 0:
824 self.inDocker = True
825 return main.TRUE
826 else:
827 main.log.error( self.name + ": Error connecting to docker container" )
828 output = self.handle.before + str( self.handle.after )
829 if i == 1:
830 self.handle.expect( self.prompt )
831 output += self.handle.before + str( self.handle.after )
832 main.log.debug( self.name + ": " + output )
833 return main.FALSE
834 except pexpect.EOF:
835 main.log.error( self.name + ": EOF exception found" )
836 main.log.error( self.name + ": " + self.handle.before )
837 return main.FALSE
838 except AttributeError as e:
839 main.log.exception( self.name + ": AttributeError - " + str( e ) )
840 main.log.warn( self.name + ": Make sure dockerPrompt is set" )
841 main.cleanup()
842 main.exit()
843 except Exception:
844 main.log.exception( self.name + ": Uncaught exception!" )
845 return main.FALSE
846
847 def dockerExec( self, containerName, command="/bin/bash", options="-it", dockerPrompt="" ):
848 """
849 Attach to a docker image
850 Required Arguments:
851 - containerName: The name of the container to attach to
852 Optional Arguments:
853 - command: Command to run in the docker container
854 - options: Docker exec options
855 - dockerPrompt: a regex for matching the docker shell prompt
856 """
857 try:
858 if dockerPrompt:
859 self.dockerPrompt = dockerPrompt
860 cmdStr = "docker exec %s %s %s" % ( options, containerName, command )
861 main.log.info( self.name + ": sending: " + cmdStr )
862 self.handle.sendline( cmdStr)
863 i = self.handle.expect( [ self.dockerPrompt,
864 "Error response from daemon",
865 pexpect.TIMEOUT ] )
866 if i == 0:
867 self.inDocker = True
868 return main.TRUE
869 else:
870 main.log.error( self.name + ": Error connecting to docker container" )
871 output = self.handle.before + str( self.handle.after )
872 if i == 1:
873 self.handle.expect( self.prompt )
874 output += self.handle.before + str( self.handle.after )
875 main.log.debug( self.name + ": " + output )
876 return main.FALSE
877 except pexpect.EOF:
878 main.log.error( self.name + ": EOF exception found" )
879 main.log.error( self.name + ": " + self.handle.before )
880 return main.FALSE
881 except AttributeError as e:
882 main.log.exception( self.name + ": AttributeError - " + str( e ) )
883 main.log.warn( self.name + ": Make sure dockerPrompt is set" )
884 main.cleanup()
885 main.exit()
886 except Exception:
887 main.log.exception( self.name + ": Uncaught exception!" )
888 return main.FALSE
889
890 def dockerCp( self, containerName, dockerPath, hostPath, direction="from" ):
891 """
892 Copy a file from/to a docker container to the host
893 Required Arguments:
894 - containerName: The name of the container to copy from/to
895 - dockerPath: the path in the container to copy from/to
896 - hostPath: the path on the host to copy to/from
897 Optional Arguments:
898 - direction: Choose whether to copy "from" the container or "to" the container
899 """
900 try:
901 cmdStr = "docker cp "
902 if direction == "from":
903 cmdStr += "%s:%s %s" % ( containerName, dockerPath, hostPath )
904 elif direction == "to":
905 cmdStr += "%s %s:%s" % ( hostPath, containerName, dockerPath )
906 main.log.info( self.name + ": sending: " + cmdStr )
907 self.handle.sendline( cmdStr)
908 i = self.handle.expect( [ self.prompt,
909 "Error",
910 pexpect.TIMEOUT ] )
911 if i == 0:
912 retValue = main.TRUE
913 else:
914 main.log.error( self.name + ": Error in docker cp" )
915 output = self.handle.before + str( self.handle.after )
916 if i == 1:
917 self.handle.expect( self.prompt )
918 output += self.handle.before + str( self.handle.after )
919 main.log.debug( self.name + ": " + output )
920 retValue = main.FALSE
921 return retValue
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 dockerDisconnect( self ):
936 """
937 Send ctrl-c, ctrl-d to session, which should close and exit the
938 attached docker session. This will likely exit the running program
939 in the container and also stop the container.
940 """
941 try:
942 cmdStr = "\x03"
943 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
944 self.handle.send( cmdStr)
945 cmdStr = "\x04"
946 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
947 self.handle.send( cmdStr)
948 i = self.handle.expect( [ self.prompt, pexpect.TIMEOUT ] )
949 if i == 0:
950 self.inDocker = False
951 return main.TRUE
952 else:
953 main.log.error( self.name + ": Error disconnecting from docker image" )
954 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
955 return main.FALSE
956 except pexpect.EOF:
957 main.log.error( self.name + ": EOF exception found" )
958 main.log.error( self.name + ": " + self.handle.before )
959 return main.FALSE
960 except Exception:
961 main.log.exception( self.name + ": Uncaught exception!" )
962 return main.FALSE
Jon Hall06fd0df2021-01-25 15:50:06 -0800963
964# TODO: How is this different from exitFromCmd used elsewhere?
965 def exitFromProcess( self ):
966 """
967 Send ctrl-c, which should close and exit the program
968 """
969 try:
970 cmdStr = "\x03"
971 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
972 self.handle.send( cmdStr)
973 i = self.handle.expect( [ self.prompt, pexpect.TIMEOUT ] )
974 if i == 0:
975 return main.TRUE
976 else:
977 main.log.error( self.name + ": Error exiting process" )
978 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
979 return main.FALSE
980 except pexpect.EOF:
981 main.log.error( self.name + ": EOF exception found" )
982 main.log.error( self.name + ": " + self.handle.before )
983 return main.FALSE
984 except Exception:
985 main.log.exception( self.name + ": Uncaught exception!" )
986 return main.FALSE
987
988 def preDisconnect( self ):
989 """
990 A Stub for a function that will be called before disconnect.
991 This can be set if for instance, the shell is running a program
992 and needs to exit the program before disconnecting from the component
993 """
994 print "preDisconnect"
995 return main.TRUE
996
Jon Hallbe3a2ac2021-03-15 12:28:06 -0700997 def kubectlGetPodNames( self, kubeconfig=None, namespace=None, app=None, name=None, nodeName=None ):
Jon Hall06fd0df2021-01-25 15:50:06 -0800998 """
999 Use kubectl to get the names of pods
1000 Optional Arguments:
1001 - kubeconfig: The path to a kubeconfig file
1002 - namespace: The namespace to search in
1003 - app: Get pods belonging to a specific app
1004 - name: Get pods with a specific name label
Jon Hallbe3a2ac2021-03-15 12:28:06 -07001005 - nodeName: Get pods on a specific node
Jon Hall06fd0df2021-01-25 15:50:06 -08001006 Returns a list containing the names of the pods or
1007 main.FALSE on Error
1008 """
1009
1010 try:
Jon Hallbe3a2ac2021-03-15 12:28:06 -07001011 cmdStr = "kubectl %s %s get pods %s %s %s --output=jsonpath='{.items..metadata.name}{\"\\n\"}'" % (
Jon Hall06fd0df2021-01-25 15:50:06 -08001012 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1013 "-n %s" % namespace if namespace else "",
1014 "-l app=%s" % app if app else "",
Jon Hallbe3a2ac2021-03-15 12:28:06 -07001015 "-l name=%s" % name if name else "",
1016 "--field-selector=spec.nodeName=%s" % nodeName if nodeName else "" )
Jon Hall06fd0df2021-01-25 15:50:06 -08001017 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1018 self.handle.sendline( cmdStr )
Jon Hall2941fce2021-04-13 10:38:23 -07001019 i = self.handle.expect( [ "not found", "error", "The connection to the server", "Unable to find", "No resources found", self.prompt ] )
1020 if i == 4:
1021 # Command worked, but returned no pods
1022 output = self.handle.before + self.handle.after
1023 main.log.warn( self.name + ": " + output )
1024 return []
1025 elif i == 5:
1026 # Command returned pods
Jon Hall06fd0df2021-01-25 15:50:06 -08001027 output = self.handle.before + self.handle.after
1028 names = output.split( '\r\n' )[1].split()
1029 return names
1030 else:
Jon Hall2941fce2021-04-13 10:38:23 -07001031 # Some error occured
Jon Hall06fd0df2021-01-25 15:50:06 -08001032 main.log.error( self.name + ": Error executing command" )
1033 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1034 return main.FALSE
1035 except pexpect.EOF:
1036 main.log.error( self.name + ": EOF exception found" )
1037 main.log.error( self.name + ": " + self.handle.before )
1038 return main.FALSE
1039 except pexpect.TIMEOUT:
1040 main.log.exception( self.name + ": TIMEOUT exception found" )
1041 main.log.error( self.name + ": " + self.handle.before )
1042 return main.FALSE
1043 except Exception:
1044 main.log.exception( self.name + ": Uncaught exception!" )
1045 return main.FALSE
1046
1047 def kubectlDescribe( self, describeString, dstPath, kubeconfig=None, namespace=None ):
1048 """
1049 Use kubectl to get the logs from a pod
1050 Required Arguments:
1051 - describeString: The string passed to the cli. Example: "pods"
1052 - dstPath: The location to save the logs to
1053 Optional Arguments:
1054 - kubeconfig: The path to a kubeconfig file
1055 - namespace: The namespace to search in
1056 Returns main.TRUE or
1057 main.FALSE on Error
1058 """
1059
1060 try:
1061 cmdStr = "kubectl %s %s describe %s > %s " % (
1062 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1063 "-n %s" % namespace if namespace else "",
1064 describeString,
1065 dstPath )
1066 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1067 self.handle.sendline( cmdStr )
1068 i = self.handle.expect( [ "not found", "error", "The connection to the server", self.prompt ] )
1069 if i == 3:
1070 main.log.debug( self.name + ": " + self.handle.before )
1071 return main.TRUE
1072 else:
1073 main.log.error( self.name + ": Error executing command" )
1074 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1075 return main.FALSE
1076 except pexpect.EOF:
1077 main.log.error( self.name + ": EOF exception found" )
1078 main.log.error( self.name + ": " + self.handle.before )
1079 return main.FALSE
1080 except pexpect.TIMEOUT:
1081 main.log.exception( self.name + ": TIMEOUT exception found" )
1082 main.log.error( self.name + ": " + self.handle.before )
1083 return main.FALSE
1084 except Exception:
1085 main.log.exception( self.name + ": Uncaught exception!" )
1086 return main.FALSE
1087
1088 def kubectlPodNodes( self, dstPath=None, kubeconfig=None, namespace=None ):
1089 """
1090 Use kubectl to get the logs from a pod
1091 Optional Arguments:
1092 - dstPath: The location to save the logs to
1093 - kubeconfig: The path to a kubeconfig file
1094 - namespace: The namespace to search in
1095 Returns main.TRUE if dstPath is given, else the output of the command or
1096 main.FALSE on Error
1097 """
1098
1099 try:
Jon Hall50a00012021-03-08 11:06:11 -08001100 cmdStr = "kubectl %s %s get pods -o wide %s " % (
Jon Hall06fd0df2021-01-25 15:50:06 -08001101 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1102 "-n %s" % namespace if namespace else "",
1103 " > %s" % dstPath if dstPath else "" )
1104 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1105 self.handle.sendline( cmdStr )
1106 i = self.handle.expect( [ "not found", "error", "The connection to the server", self.prompt ] )
1107 if i == 3:
1108 output = self.handle.before
1109 main.log.debug( self.name + ": " + output )
1110 return output if dstPath else main.TRUE
1111 else:
1112 main.log.error( self.name + ": Error executing command" )
1113 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1114 return main.FALSE
1115 except pexpect.EOF:
1116 main.log.error( self.name + ": EOF exception found" )
1117 main.log.error( self.name + ": " + self.handle.before )
1118 return main.FALSE
1119 except pexpect.TIMEOUT:
1120 main.log.exception( self.name + ": TIMEOUT exception found" )
1121 main.log.error( self.name + ": " + self.handle.before )
1122 return main.FALSE
1123 except Exception:
1124 main.log.exception( self.name + ": Uncaught exception!" )
1125 return main.FALSE
1126
Jon Halla7b27e62021-06-29 12:13:51 -07001127 def sternLogs( self, podString, dstPath, kubeconfig=None, namespace=None, since='1h', wait=60 ):
1128 """
1129 Use stern to get the logs from a pod
1130 Required Arguments:
1131 - podString: The name of the pod or partial name of the pods to get the logs of
1132 - dstPath: The location to save the logs to
1133 Optional Arguments:
1134 - kubeconfig: The path to a kubeconfig file
1135 - namespace: The namespace to search in
1136 - since: Return logs newer than a relative duration like 5s, 2m, or 3h. Defaults to 1h
1137 - wait: How long to wait, in seconds, before killing the process. Stern does not currently
1138 support a way to exit if cought up to present time. Defaults to 60 seconds
1139 Returns main.TRUE or
1140 main.FALSE on Error
1141 """
1142 import time
1143 try:
1144 cmdStr = "stern %s %s %s %s > %s " % (
1145 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1146 "-n %s" % namespace if namespace else "",
1147 "--since %s" % since if since else "",
1148 podString,
1149 dstPath )
1150 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1151 self.handle.sendline( cmdStr )
1152 time.sleep( int( wait ) )
1153 self.handle.send( '\x03' ) # CTRL-C
1154 i = self.handle.expect( [ "not found", "Error: ", "The connection to the server", self.prompt ] )
1155 if i == 3:
1156 main.log.debug( self.name + ": " + self.handle.before )
1157 return main.TRUE
1158 else:
1159 main.log.error( self.name + ": Error executing command" )
1160 response = self.handle.before + str( self.handle.after )
1161 self.handle.expect( [ self.prompt, pexpect.TIMEOUT ], timeout=5 )
1162 response += self.handle.before + str( self.handle.after )
1163 main.log.debug( self.name + ": " + response )
1164 return main.FALSE
1165 except pexpect.EOF:
1166 main.log.error( self.name + ": EOF exception found" )
1167 main.log.error( self.name + ": " + self.handle.before )
1168 return main.FALSE
1169 except pexpect.TIMEOUT:
1170 main.log.exception( self.name + ": TIMEOUT exception found" )
1171 main.log.error( self.name + ": " + self.handle.before )
1172 return main.FALSE
1173 except Exception:
1174 main.log.exception( self.name + ": Uncaught exception!" )
1175 return main.FALSE
1176
Jon Hall06fd0df2021-01-25 15:50:06 -08001177 def kubectlLogs( self, podName, dstPath, kubeconfig=None, namespace=None, timeout=240 ):
1178 """
1179 Use kubectl to get the logs from a pod
1180 Required Arguments:
1181 - podName: The name of the pod to get the logs of
1182 - dstPath: The location to save the logs to
1183 Optional Arguments:
1184 - kubeconfig: The path to a kubeconfig file
1185 - namespace: The namespace to search in
1186 - timeout: Timeout for command to return. The longer the logs, the longer it will take to fetch them.
1187 Returns main.TRUE or
1188 main.FALSE on Error
1189 """
1190
1191 try:
1192 cmdStr = "kubectl %s %s logs %s > %s " % (
1193 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1194 "-n %s" % namespace if namespace else "",
1195 podName,
1196 dstPath )
1197 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1198 self.handle.sendline( cmdStr )
1199 i = self.handle.expect( [ "not found", "error", "The connection to the server", self.prompt ], timeout=timeout )
1200 if i == 3:
1201 main.log.debug( self.name + ": " + self.handle.before )
1202 return main.TRUE
1203 else:
1204 main.log.error( self.name + ": Error executing command" )
1205 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1206 return main.FALSE
1207 except pexpect.EOF:
1208 main.log.error( self.name + ": EOF exception found" )
1209 main.log.error( self.name + ": " + self.handle.before )
1210 return main.FALSE
1211 except pexpect.TIMEOUT:
1212 main.log.exception( self.name + ": TIMEOUT exception found" )
1213 main.log.error( self.name + ": " + self.handle.before )
1214 return main.FALSE
1215 except Exception:
1216 main.log.exception( self.name + ": Uncaught exception!" )
1217 return main.FALSE
1218
Jon Hallbe3a2ac2021-03-15 12:28:06 -07001219 def kubectlCp( self, podName, srcPath, dstPath, kubeconfig=None, namespace=None, timeout=240 ):
1220 """
1221 Use kubectl to get a file from a pod
1222 Required Arguments:
1223 - podName: The name of the pod to get the logs of
1224 - srcPath: The file to copy from the pod
1225 - dstPath: The location to save the file to locally
1226 Optional Arguments:
1227 - kubeconfig: The path to a kubeconfig file
1228 - namespace: The namespace to search in
1229 - timeout: Timeout for command to return. The longer the logs, the longer it will take to fetch them.
1230 Returns main.TRUE or
1231 main.FALSE on Error
1232 """
1233
1234 try:
1235 cmdStr = "kubectl %s %s cp %s:%s %s" % (
1236 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1237 "-n %s" % namespace if namespace else "",
1238 podName,
1239 srcPath,
1240 dstPath )
1241 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1242 self.handle.sendline( cmdStr )
1243 i = self.handle.expect( [ "not found", "error", "The connection to the server", self.prompt ], timeout=timeout )
1244 if i == 3:
1245 main.log.debug( self.name + ": " + self.handle.before )
1246 return main.TRUE
1247 else:
1248 main.log.error( self.name + ": Error executing command" )
1249 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1250 return main.FALSE
1251 except pexpect.EOF:
1252 main.log.error( self.name + ": EOF exception found" )
1253 main.log.error( self.name + ": " + self.handle.before )
1254 return main.FALSE
1255 except pexpect.TIMEOUT:
1256 main.log.exception( self.name + ": TIMEOUT exception found" )
1257 main.log.error( self.name + ": " + self.handle.before )
1258 return main.FALSE
1259 except Exception:
1260 main.log.exception( self.name + ": Uncaught exception!" )
1261 return main.FALSE
1262
Jon Hall06fd0df2021-01-25 15:50:06 -08001263 def kubectlPortForward( self, podName, portsList, kubeconfig=None, namespace=None, ):
1264 """
1265 Use kubectl to setup port forwarding from the local machine to the kubernetes pod
1266
1267 Note: This command does not return until the port forwarding session is ended.
1268
1269 Required Arguments:
1270 - podName: The name of the pod as a string
1271 - portsList: The list of ports to forward, as a string. see kubectl help for details
1272 Optional Arguments:
1273 - kubeconfig: The path to a kubeconfig file
1274 - namespace: The namespace to search in
1275 - app: Get pods belonging to a specific app
1276 Returns a list containing the names of the pods or
1277 main.FALSE on Error
1278
1279
1280 """
1281 try:
1282 cmdStr = "kubectl %s %s port-forward pod/%s %s" % (
1283 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1284 "-n %s" % namespace if namespace else "",
1285 podName,
1286 portsList )
1287 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1288 self.handle.sendline( cmdStr )
1289 i = self.handle.expect( [ "not found", "error", "closed/timedout",
1290 self.prompt, "The connection to the server", "Forwarding from" ] )
1291 # NOTE: This won't clear the buffer entirely, and each time the port forward
1292 # is used, another line will be added to the buffer. We need to make
1293 # sure we clear the buffer before using this component again.
1294
1295 if i == 5:
1296 # Setup preDisconnect function
1297 self.preDisconnect = self.exitFromProcess
1298 return main.TRUE
1299 else:
1300 main.log.error( self.name + ": Error executing command" )
1301 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1302 return main.FALSE
1303 except pexpect.EOF:
1304 main.log.error( self.name + ": EOF exception found" )
1305 main.log.error( self.name + ": " + self.handle.before )
1306 return main.FALSE
1307 except pexpect.TIMEOUT:
1308 main.log.exception( self.name + ": TIMEOUT exception found" )
1309 main.log.error( self.name + ": " + self.handle.before )
1310 return main.FALSE
1311 except Exception:
1312 main.log.exception( self.name + ": Uncaught exception!" )
1313 return main.FALSE