blob: aa1e9f9b9d5ced9749af524f9776feedb2486263 [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 """
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700297 returnVal = main.TRUE
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
acsmars07f9d392015-07-15 10:30:58 -0700302
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700303 if direction == "from":
Jon Hall39570262020-11-17 12:18:19 -0800304 cmd = cmd + str( userName ) + '@' + str( ipAddress ) + ':' + \
Jon Hall547e0582015-09-21 17:35:40 -0700305 str( filePath ) + ' ' + str( dstPath )
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700306 elif direction == "to":
Jon Hall39570262020-11-17 12:18:19 -0800307 cmd = cmd + str( filePath ) + ' ' + str( userName ) + \
Jon Hall547e0582015-09-21 17:35:40 -0700308 '@' + str( ipAddress ) + ':' + str( dstPath )
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700309 else:
310 main.log.debug( "Wrong direction using secure copy command!" )
311 return main.FALSE
kelvin8ec71442015-01-15 16:57:00 -0800312
Jon Hall3c0114c2020-08-11 15:07:42 -0700313 main.log.info( self.name + ": Sending: " + cmd )
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700314 self.handle.sendline( cmd )
Jon Hall547e0582015-09-21 17:35:40 -0700315 i = 0
316 while i < 2:
317 i = self.handle.expect( [
318 ssh_newkey,
319 'password:',
320 "100%",
321 refused,
322 "No such file or directory",
Jon Hall53c5e662016-04-13 16:06:56 -0700323 "Permission denied",
Devin Limdc78e202017-06-09 18:30:07 -0700324 self.prompt,
Jon Hall547e0582015-09-21 17:35:40 -0700325 pexpect.EOF,
326 pexpect.TIMEOUT ],
327 120 )
Jon Hall547e0582015-09-21 17:35:40 -0700328 if i == 0: # ask for ssh key confirmation
Jon Hall3c0114c2020-08-11 15:07:42 -0700329 main.log.info( self.name + ": ssh key confirmation received, sending yes" )
Jon Hall547e0582015-09-21 17:35:40 -0700330 self.handle.sendline( 'yes' )
331 elif i == 1: # Asked for ssh password
Jon Hall3c0114c2020-08-11 15:07:42 -0700332 main.log.info( self.name + ": ssh connection asked for password, gave password" )
Jon Hall547e0582015-09-21 17:35:40 -0700333 self.handle.sendline( pwd )
334 elif i == 2: # File finished transfering
Jon Hall3c0114c2020-08-11 15:07:42 -0700335 main.log.info( self.name + ": Secure copy successful" )
Jon Hall547e0582015-09-21 17:35:40 -0700336 returnVal = main.TRUE
337 elif i == 3: # Connection refused
338 main.log.error(
339 "ssh: connect to host " +
340 ipAddress +
341 " port 22: Connection refused" )
342 returnVal = main.FALSE
343 elif i == 4: # File Not found
Jon Hall3c0114c2020-08-11 15:07:42 -0700344 main.log.error( self.name + ": No such file found" )
Jon Hall39570262020-11-17 12:18:19 -0800345 main.log.debug( self.handle.before + self.handle.after )
Jon Hall547e0582015-09-21 17:35:40 -0700346 returnVal = main.FALSE
Jon Hall53c5e662016-04-13 16:06:56 -0700347 elif i == 5: # Permission denied
Jon Hall3c0114c2020-08-11 15:07:42 -0700348 main.log.error( self.name + ": Permission denied. Check folder permissions" )
Jon Hall39570262020-11-17 12:18:19 -0800349 main.log.debug( self.handle.before + self.handle.after )
Jon Hall53c5e662016-04-13 16:06:56 -0700350 returnVal = main.FALSE
351 elif i == 6: # prompt returned
Jon Hall4173b242017-09-12 17:04:38 -0700352 return returnVal
Jon Hall53c5e662016-04-13 16:06:56 -0700353 elif i == 7: # EOF
Jon Hall3c0114c2020-08-11 15:07:42 -0700354 main.log.error( self.name + ": Pexpect.EOF found!!!" )
Devin Lim44075962017-08-11 10:56:37 -0700355 main.cleanAndExit()
Jon Hall53c5e662016-04-13 16:06:56 -0700356 elif i == 8: # timeout
Jon Hall547e0582015-09-21 17:35:40 -0700357 main.log.error(
358 "No route to the Host " +
359 userName +
360 "@" +
361 ipAddress )
362 returnVal = main.FALSE
Devin Limdc78e202017-06-09 18:30:07 -0700363 self.handle.expect( self.prompt )
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700364 return returnVal
365
Jon Hall39570262020-11-17 12:18:19 -0800366 def scp( self, remoteHost, filePath, dstPath, direction="from", options="" ):
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700367 """
368 Definition:
369 Execute scp command in linux to copy to/from a remote host
370 Required:
371 * remoteHost - Test ON component to be parsed
372 str filePath - File path including the file it self
373 str dstPath - Destination path
374 Optional:
375 str direction - Direction of the scp, default to "from" which means
376 copy "from" the remote machine to local machine,
377 while "to" means copy "to" the remote machine from
378 local machine
379 """
Jon Hall06fd0df2021-01-25 15:50:06 -0800380 jump_host = main.componentDictionary[ remoteHost.name ].get( 'jump_host' )
381 if jump_host:
382 jump_host = main.componentDictionary.get( jump_host )
383 options += " -J %s@%s " % ( jump_host.get( 'user' ), jump_host.get( 'host' ) )
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700384 return self.secureCopy( remoteHost.user_name,
385 remoteHost.ip_address,
386 filePath,
387 dstPath,
388 pwd=remoteHost.pwd,
Jon Hall39570262020-11-17 12:18:19 -0800389 direction=direction,
390 options=options )
Devin Lim142b5342017-07-20 15:22:39 -0700391
392 def sshToNode( self, ipAddress, uName="sdn", pwd="rocks" ):
393 ssh_newkey = 'Are you sure you want to continue connecting'
394 refused = "ssh: connect to host " + ipAddress + " port 22: Connection refused"
395 handle = pexpect.spawn( 'ssh -X ' +
396 uName +
397 '@' +
398 ipAddress,
Jon Hall6c9e2da2018-11-06 12:01:23 -0800399 env={ "TERM": "vt100" },
Devin Lim142b5342017-07-20 15:22:39 -0700400 maxread=1000000,
401 timeout=60 )
402
403 # set tty window size
404 handle.setwinsize( 24, 250 )
405
406 i = 5
407 while i == 5:
Jon Hall4173b242017-09-12 17:04:38 -0700408 i = handle.expect( [ ssh_newkey,
409 'password:|Password:',
410 pexpect.EOF,
411 pexpect.TIMEOUT,
412 refused,
413 'teston>',
414 self.prompt ],
415 120 )
Devin Lim142b5342017-07-20 15:22:39 -0700416 if i == 0: # Accept key, then expect either a password prompt or access
Jon Hall3c0114c2020-08-11 15:07:42 -0700417 main.log.info( self.name + ": ssh key confirmation received, send yes" )
Devin Lim142b5342017-07-20 15:22:39 -0700418 handle.sendline( 'yes' )
419 i = 5 # Run the loop again
420 continue
421 if i == 1: # Password required
422 if pwd:
423 main.log.info(
Jon Hall4173b242017-09-12 17:04:38 -0700424 "ssh connection asked for password, gave password" )
Devin Lim142b5342017-07-20 15:22:39 -0700425 else:
Jon Hall3c0114c2020-08-11 15:07:42 -0700426 main.log.info( self.name + ": Server asked for password, but none was "
Devin Lim142b5342017-07-20 15:22:39 -0700427 "given in the .topo file. Trying "
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700428 "no password." )
Devin Lim142b5342017-07-20 15:22:39 -0700429 pwd = ""
430 handle.sendline( pwd )
431 j = handle.expect( [ self.prompt,
432 'password:|Password:',
433 pexpect.EOF,
434 pexpect.TIMEOUT ],
435 120 )
436 if j != 0:
Jon Hall3c0114c2020-08-11 15:07:42 -0700437 main.log.error( self.name + ": Incorrect Password" )
Devin Lim44075962017-08-11 10:56:37 -0700438 main.cleanAndExit()
Devin Lim142b5342017-07-20 15:22:39 -0700439 elif i == 2:
Jon Hall3c0114c2020-08-11 15:07:42 -0700440 main.log.error( self.name + ": Connection timeout" )
Devin Lim44075962017-08-11 10:56:37 -0700441 main.cleanAndExit()
Devin Lim142b5342017-07-20 15:22:39 -0700442 elif i == 3: # timeout
443 main.log.error(
444 "No route to the Host " +
445 uName +
446 "@" +
447 ipAddress )
Devin Lim44075962017-08-11 10:56:37 -0700448 main.cleanAndExit()
Devin Lim142b5342017-07-20 15:22:39 -0700449 elif i == 4:
450 main.log.error(
451 "ssh: connect to host " +
452 ipAddress +
453 " port 22: Connection refused" )
Devin Lim44075962017-08-11 10:56:37 -0700454 main.cleanAndExit()
Devin Lim142b5342017-07-20 15:22:39 -0700455 elif i == 6:
Jon Hall3c0114c2020-08-11 15:07:42 -0700456 main.log.info( self.name + ": Password not required logged in" )
Devin Lim142b5342017-07-20 15:22:39 -0700457
458 handle.sendline( "" )
459 handle.expect( self.prompt )
460 handle.sendline( "cd" )
461 handle.expect( self.prompt )
462
Jon Hall3c0114c2020-08-11 15:07:42 -0700463 main.log.info( self.name + ": Successfully ssh to " + ipAddress + "." )
Devin Lim142b5342017-07-20 15:22:39 -0700464 return handle
465
466 def exitFromSsh( self, handle, ipAddress ):
Devin Lim142b5342017-07-20 15:22:39 -0700467 try:
Jon Hall4f360bc2017-09-07 10:19:52 -0700468 handle.sendline( "logout" )
Devin Lim142b5342017-07-20 15:22:39 -0700469 handle.expect( "closed." )
Jon Hall3c0114c2020-08-11 15:07:42 -0700470 main.log.info( self.name + ": Successfully closed ssh connection from " + ipAddress )
Devin Lim142b5342017-07-20 15:22:39 -0700471 except pexpect.EOF:
Jon Hall3c0114c2020-08-11 15:07:42 -0700472 main.log.error( self.name + ": Failed to close the connection from " + ipAddress )
Jon Hall4f360bc2017-09-07 10:19:52 -0700473 try:
474 # check that this component handle still works
475 self.handle.sendline( "" )
476 self.handle.expect( self.prompt )
477 except pexpect.EOF:
478 main.log.error( self.handle.before )
Jon Hall3c0114c2020-08-11 15:07:42 -0700479 main.log.error( self.name + ": EOF after closing ssh connection" )
Jon Hall4173b242017-09-12 17:04:38 -0700480
481 def folderSize( self, path, size='10', unit='M', ignoreRoot=True ):
482 """
483 Run `du -h` on the folder path and verifies the folder(s) size is
484 less than the given size. Note that if multiple subdirectories are
485 present, the result will be the OR of all the individual subdirectories.
486
487 Arguments:
488 path - A string containing the path supplied to the du command
489 size - The number portion of the file size that the results will be compared to
490 unit - The unit portion of the file size that the results will be compared to
491 ignoreRoot - If True, will ignore the "root" of the path supplied to du. I.E. will ignore `.`
492
493 Returns True if the folder(s) size(s) are less than SIZE UNITS, else returns False
494 """
495 sizeRe = r'(?P<number>\d+\.*\d*)(?P<unit>\D)'
496 unitsList = [ 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y' ]
497 try:
498 # make sure we convert units if size is too big
499 size = float( size )
500 if size >= 1000:
501 size = size / 1000
502 unit = unitsList[ unitsList.index( unit + 1 ) ]
503 cmdStr = "du -h " + path
504 self.handle.sendline( cmdStr )
505 self.handle.expect( self.prompt )
506 output = self.handle.before
507 assert "cannot access" not in output
508 assert "command not found" not in output
509 main.log.debug( output )
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700510 lines = [ line for line in output.split( '\r\n' ) ]
Jon Hall4173b242017-09-12 17:04:38 -0700511 retValue = True
512 if ignoreRoot:
513 lastIndex = -2
514 else:
515 lastIndex = -1
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700516 for line in lines[ 1:lastIndex ]:
Jon Hall4173b242017-09-12 17:04:38 -0700517 parsed = line.split()
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700518 sizeMatch = parsed[ 0 ]
519 folder = parsed[ 1 ]
Jon Hall4173b242017-09-12 17:04:38 -0700520 match = re.search( sizeRe, sizeMatch )
521 num = match.group( 'number' )
522 unitMatch = match.group( 'unit' )
523 if unitsList.index( unitMatch ) < unitsList.index( unit ):
524 retValue &= True
525 elif unitsList.index( unitMatch ) == unitsList.index( unit ):
526 if float( num ) < float( size ):
527 retValue &= True
528 else:
529 retValue &= False
530 elif unitsList.index( unitMatch ) > unitsList.index( unit ):
531 retValue &= False
532 return retValue
533 except AssertionError:
534 main.log.error( self.name + ": Could not execute command: " + output )
535 return False
Jon Hall43060f62020-06-23 13:13:33 -0700536 except ValueError as e:
537 main.log.error( self.name + ": Error parsing output: " + output )
538 main.log.error( e )
539 return False
Jon Hall4173b242017-09-12 17:04:38 -0700540 except pexpect.TIMEOUT:
541 main.log.exception( self.name + ": TIMEOUT exception found" )
542 main.log.error( self.name + ": " + self.handle.before )
543 return False
544 except pexpect.EOF:
545 main.log.error( self.name + ": EOF exception found" )
546 main.log.error( self.name + ": " + self.handle.before )
547 main.cleanAndExit()
Jon Hall0e240372018-05-02 11:21:57 -0700548
549 def setEnv( self, variable, value=None ):
550 """
551 Sets the environment variable to the given value for the current shell session.
552 If value is None, will unset the variable.
553
554 Required Arguments:
555 variable - The name of the environment variable to set.
556
557 Optional Arguments:
558 value - The value to set the variable to. ( Defaults to None, which unsets the variable )
559
560 Returns True if no errors are detected else returns False
561 """
562 try:
563 if value:
564 cmd = "export {}={}".format( variable, value )
565 else:
566 cmd = "unset {}".format( variable )
567 self.handle.sendline( cmd )
568 self.handle.expect( self.prompt )
Jon Hall3c0114c2020-08-11 15:07:42 -0700569 output = self.handle.before
570 main.log.debug( output )
Jon Hall0e240372018-05-02 11:21:57 -0700571 return True
572 except AssertionError:
573 main.log.error( self.name + ": Could not execute command: " + output )
574 return False
575 except pexpect.TIMEOUT:
576 main.log.exception( self.name + ": TIMEOUT exception found" )
577 main.log.error( self.name + ": " + self.handle.before )
578 return False
579 except pexpect.EOF:
580 main.log.error( self.name + ": EOF exception found" )
581 main.log.error( self.name + ": " + self.handle.before )
582 main.cleanAndExit()
You Wangb65d2372018-08-17 15:37:59 -0700583
584 def exitFromCmd( self, expect, retry=10 ):
585 """
586 Call this function when sending ctrl+c is required to kill the current
587 command. It will retry multiple times until the running command is
588 completely killed and expected string is returned from the handle.
589 Required:
You Wangd4fae5c2018-08-22 13:56:49 -0700590 expect: expected string or list of strings which indicates that the
591 previous command was killed successfully.
You Wangb65d2372018-08-17 15:37:59 -0700592 Optional:
593 retry: maximum number of ctrl+c that will be sent.
594 """
You Wangd4fae5c2018-08-22 13:56:49 -0700595 expect = [ expect ] if isinstance( expect, str ) else expect
You Wangb65d2372018-08-17 15:37:59 -0700596 try:
597 while retry >= 0:
598 main.log.debug( self.name + ": sending ctrl+c to kill the command" )
599 self.handle.send( "\x03" )
You Wangd4fae5c2018-08-22 13:56:49 -0700600 i = self.handle.expect( expect + [ pexpect.TIMEOUT ], timeout=3 )
You Wangb65d2372018-08-17 15:37:59 -0700601 main.log.debug( self.handle.before )
You Wangd4fae5c2018-08-22 13:56:49 -0700602 if i < len( expect ):
You Wangb65d2372018-08-17 15:37:59 -0700603 main.log.debug( self.name + ": successfully killed the command" )
604 return main.TRUE
605 retry -= 1
606 main.log.warn( self.name + ": failed to kill the command" )
607 return main.FALSE
608 except pexpect.EOF:
609 main.log.error( self.name + ": EOF exception found" )
610 main.log.error( self.name + ": " + self.handle.before )
611 return main.FALSE
612 except Exception:
613 main.log.exception( self.name + ": Uncaught exception!" )
614 return main.FALSE
Jon Hall43060f62020-06-23 13:13:33 -0700615
616 def cleanOutput( self, output, debug=False ):
617 """
618 Clean ANSI characters from output
619 """
620 ansiEscape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
621 cleaned = ansiEscape.sub( '', output )
622 if debug:
623 main.log.debug( self.name + ": cleanOutput:" )
624 main.log.debug( self.name + ": " + repr( cleaned ) )
625 return cleaned
Jon Hall3c0114c2020-08-11 15:07:42 -0700626
627 def dockerPull( self, image, tag=None ):
628 """
629 Pull a docker image from a registry
630 """
631 try:
632 imgStr = "%s%s" % ( image, ":%s" % tag if tag else "" )
633 cmdStr = "docker pull %s" % imgStr
634 main.log.info( self.name + ": sending: " + cmdStr )
635 self.handle.sendline( cmdStr)
636 i = self.handle.expect( [ self.prompt,
637 "Error response from daemon",
638 pexpect.TIMEOUT ], 120 )
639 if i == 0:
640 return main.TRUE
641 else:
642 main.log.error( self.name + ": Error pulling docker image " + imgStr )
643 output = self.handle.before + str( self.handle.after )
644 if i == 1:
645 self.handle.expect( self.prompt )
646 output += self.handle.before + str( self.handle.after )
647 main.log.debug( self.name + ": " + output )
648 return main.FALSE
649 except pexpect.EOF:
650 main.log.error( self.name + ": EOF exception found" )
651 main.log.error( self.name + ": " + self.handle.before )
652 return main.FALSE
653 except Exception:
654 main.log.exception( self.name + ": Uncaught exception!" )
655 return main.FALSE
656
657 def dockerBuild( self, path, imageTag, pull=False, options="", timeout=600 ):
658 """
659 Build a docker image
660 Required Arguments:
661 - path: Path to the dockerfile, it is recommended to avoid relative paths
662 - imageTag: Give a tag to the built docker image
663 Optional Arguments:
664 - pull: Whether to attempt to pull latest images before building
665 - options: A string containing any addition optional arguments
666 for the docker build command
667 - timeout: How many seconds to wait for the build to complete
668 """
669 try:
670 response = main.TRUE
671 if pull:
672 options = "--pull " + options
673 cmdStr = "docker build -t %s %s %s" % ( imageTag, options, path )
674 main.log.info( self.name + ": sending: " + cmdStr )
675 self.handle.sendline( cmdStr)
676 i = self.handle.expect( [ "Successfully built",
677 "Error response from daemon",
678 pexpect.TIMEOUT ], timeout=timeout )
679 output = self.handle.before
680 if i == 0:
681 output += self.handle.after
682 self.handle.expect( self.prompt )
683 output += self.handle.before + self.handle.after
684 return response
685 elif i == 1:
686 response = main.FALSE
687 output += self.handle.after
688 self.handle.expect( self.prompt )
689 output += self.handle.before + self.handle.after
690 elif i == 2:
691 response = main.FALSE
692 main.log.error( self.name + ": Error building docker image" )
693 main.log.debug( self.name + ": " + output )
694 return response
695 except pexpect.EOF:
696 main.log.error( self.name + ": EOF exception found" )
697 main.log.error( self.name + ": " + self.handle.before )
698 return main.FALSE
699 except Exception:
700 main.log.exception( self.name + ": Uncaught exception!" )
701 return main.FALSE
702
703 def dockerStop( self, containerName ):
704 """
705 Stop a docker container
706 Required Arguments:
707 - containerName: Name of the container to stop
708 """
709 try:
710 cmdStr = "docker stop %s" % ( containerName )
711 main.log.info( self.name + ": sending: " + cmdStr )
712 self.handle.sendline( cmdStr)
713 i = self.handle.expect( [ self.prompt,
714 "Error response from daemon",
715 pexpect.TIMEOUT ], 120 )
716 output = self.handle.before
717 if i == 0:
718 return main.TRUE
719 elif i == 1:
720 output += self.handle.after
721 self.handle.expect( self.prompt )
722 output += self.handle.before
723 elif i == 2:
724 pass
725 main.log.debug( "%s: %s" % ( self.name, output ) )
726 if "No such container" in output:
727 return main.TRUE
728 main.log.error( self.name + ": Error stopping docker image" )
729 main.log.debug( self.name + ": " + output )
730 return main.FALSE
731 except pexpect.EOF:
732 main.log.error( self.name + ": EOF exception found" )
733 main.log.error( self.name + ": " + self.handle.before )
734 return main.FALSE
735 except Exception:
736 main.log.exception( self.name + ": Uncaught exception!" )
737 return main.FALSE
738
Jon Hall06fd0df2021-01-25 15:50:06 -0800739 def dockerRun( self, image, containerName, options="", imageArgs="", background=False ):
Jon Hall3c0114c2020-08-11 15:07:42 -0700740 """
741 Run a docker image
742 Required Arguments:
743 - containerName: Give a name to the container once its started
744 - image: Run the given image
745 Optional Arguments:
746 - options: A string containing any addition optional arguments
747 for the docker run command
748 - imageArgs: A string containing command line arguments for the
749 command run by docker
750 """
751 try:
752 cmdStr = "docker run --name %s %s %s %s" % ( containerName,
753 options if options else "",
754 image,
755 imageArgs )
Jon Hall06fd0df2021-01-25 15:50:06 -0800756 if background:
757 cmdStr += " &"
Jon Hall3c0114c2020-08-11 15:07:42 -0700758 main.log.info( self.name + ": sending: " + cmdStr )
759 self.handle.sendline( cmdStr)
760 i = self.handle.expect( [ self.prompt,
761 "Error response from daemon",
762 pexpect.TIMEOUT ], 120 )
763 if i == 0:
764 return main.TRUE
765 else:
766 output = self.handle.before
767 main.log.debug( self.name + ": " + output )
768 main.log.error( self.name + ": Error running docker image" )
769 if i == 1:
770 output += self.handle.after
771 self.handle.expect( self.prompt )
772 output += self.handle.before + self.handle.after
773 main.log.debug( self.name + ": " + output )
774 return main.FALSE
775 except pexpect.EOF:
776 main.log.error( self.name + ": EOF exception found" )
777 main.log.error( self.name + ": " + self.handle.before )
778 return main.FALSE
779 except Exception:
780 main.log.exception( self.name + ": Uncaught exception!" )
781 return main.FALSE
782
783 def dockerAttach( self, containerName, dockerPrompt="" ):
784 """
785 Attach to a docker image
786 Required Arguments:
787 - containerName: The name of the container to attach to
788 Optional Arguments:
789 - dockerPrompt: a regex for matching the docker shell prompt
790 """
791 try:
792 if dockerPrompt:
793 self.dockerPrompt = dockerPrompt
794 cmdStr = "docker attach %s" % containerName
795 main.log.info( self.name + ": sending: " + cmdStr )
796 self.handle.sendline( cmdStr)
797 i = self.handle.expect( [ self.dockerPrompt,
798 "Error response from daemon",
799 pexpect.TIMEOUT ] )
800 if i == 0:
801 self.inDocker = True
802 return main.TRUE
803 else:
804 main.log.error( self.name + ": Error connecting to docker container" )
805 output = self.handle.before + str( self.handle.after )
806 if i == 1:
807 self.handle.expect( self.prompt )
808 output += self.handle.before + str( self.handle.after )
809 main.log.debug( self.name + ": " + output )
810 return main.FALSE
811 except pexpect.EOF:
812 main.log.error( self.name + ": EOF exception found" )
813 main.log.error( self.name + ": " + self.handle.before )
814 return main.FALSE
815 except AttributeError as e:
816 main.log.exception( self.name + ": AttributeError - " + str( e ) )
817 main.log.warn( self.name + ": Make sure dockerPrompt is set" )
818 main.cleanup()
819 main.exit()
820 except Exception:
821 main.log.exception( self.name + ": Uncaught exception!" )
822 return main.FALSE
823
824 def dockerExec( self, containerName, command="/bin/bash", options="-it", dockerPrompt="" ):
825 """
826 Attach to a docker image
827 Required Arguments:
828 - containerName: The name of the container to attach to
829 Optional Arguments:
830 - command: Command to run in the docker container
831 - options: Docker exec options
832 - dockerPrompt: a regex for matching the docker shell prompt
833 """
834 try:
835 if dockerPrompt:
836 self.dockerPrompt = dockerPrompt
837 cmdStr = "docker exec %s %s %s" % ( options, containerName, command )
838 main.log.info( self.name + ": sending: " + cmdStr )
839 self.handle.sendline( cmdStr)
840 i = self.handle.expect( [ self.dockerPrompt,
841 "Error response from daemon",
842 pexpect.TIMEOUT ] )
843 if i == 0:
844 self.inDocker = True
845 return main.TRUE
846 else:
847 main.log.error( self.name + ": Error connecting to docker container" )
848 output = self.handle.before + str( self.handle.after )
849 if i == 1:
850 self.handle.expect( self.prompt )
851 output += self.handle.before + str( self.handle.after )
852 main.log.debug( self.name + ": " + output )
853 return main.FALSE
854 except pexpect.EOF:
855 main.log.error( self.name + ": EOF exception found" )
856 main.log.error( self.name + ": " + self.handle.before )
857 return main.FALSE
858 except AttributeError as e:
859 main.log.exception( self.name + ": AttributeError - " + str( e ) )
860 main.log.warn( self.name + ": Make sure dockerPrompt is set" )
861 main.cleanup()
862 main.exit()
863 except Exception:
864 main.log.exception( self.name + ": Uncaught exception!" )
865 return main.FALSE
866
867 def dockerCp( self, containerName, dockerPath, hostPath, direction="from" ):
868 """
869 Copy a file from/to a docker container to the host
870 Required Arguments:
871 - containerName: The name of the container to copy from/to
872 - dockerPath: the path in the container to copy from/to
873 - hostPath: the path on the host to copy to/from
874 Optional Arguments:
875 - direction: Choose whether to copy "from" the container or "to" the container
876 """
877 try:
878 cmdStr = "docker cp "
879 if direction == "from":
880 cmdStr += "%s:%s %s" % ( containerName, dockerPath, hostPath )
881 elif direction == "to":
882 cmdStr += "%s %s:%s" % ( hostPath, containerName, dockerPath )
883 main.log.info( self.name + ": sending: " + cmdStr )
884 self.handle.sendline( cmdStr)
885 i = self.handle.expect( [ self.prompt,
886 "Error",
887 pexpect.TIMEOUT ] )
888 if i == 0:
889 retValue = main.TRUE
890 else:
891 main.log.error( self.name + ": Error in docker cp" )
892 output = self.handle.before + str( self.handle.after )
893 if i == 1:
894 self.handle.expect( self.prompt )
895 output += self.handle.before + str( self.handle.after )
896 main.log.debug( self.name + ": " + output )
897 retValue = main.FALSE
898 return retValue
899 except pexpect.EOF:
900 main.log.error( self.name + ": EOF exception found" )
901 main.log.error( self.name + ": " + self.handle.before )
902 return main.FALSE
903 except AttributeError as e:
904 main.log.exception( self.name + ": AttributeError - " + str( e ) )
905 main.log.warn( self.name + ": Make sure dockerPrompt is set" )
906 main.cleanup()
907 main.exit()
908 except Exception:
909 main.log.exception( self.name + ": Uncaught exception!" )
910 return main.FALSE
911
912 def dockerDisconnect( self ):
913 """
914 Send ctrl-c, ctrl-d to session, which should close and exit the
915 attached docker session. This will likely exit the running program
916 in the container and also stop the container.
917 """
918 try:
919 cmdStr = "\x03"
920 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
921 self.handle.send( cmdStr)
922 cmdStr = "\x04"
923 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
924 self.handle.send( cmdStr)
925 i = self.handle.expect( [ self.prompt, pexpect.TIMEOUT ] )
926 if i == 0:
927 self.inDocker = False
928 return main.TRUE
929 else:
930 main.log.error( self.name + ": Error disconnecting from docker image" )
931 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
932 return main.FALSE
933 except pexpect.EOF:
934 main.log.error( self.name + ": EOF exception found" )
935 main.log.error( self.name + ": " + self.handle.before )
936 return main.FALSE
937 except Exception:
938 main.log.exception( self.name + ": Uncaught exception!" )
939 return main.FALSE
Jon Hall06fd0df2021-01-25 15:50:06 -0800940
941# TODO: How is this different from exitFromCmd used elsewhere?
942 def exitFromProcess( self ):
943 """
944 Send ctrl-c, which should close and exit the program
945 """
946 try:
947 cmdStr = "\x03"
948 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
949 self.handle.send( cmdStr)
950 i = self.handle.expect( [ self.prompt, pexpect.TIMEOUT ] )
951 if i == 0:
952 return main.TRUE
953 else:
954 main.log.error( self.name + ": Error exiting process" )
955 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
956 return main.FALSE
957 except pexpect.EOF:
958 main.log.error( self.name + ": EOF exception found" )
959 main.log.error( self.name + ": " + self.handle.before )
960 return main.FALSE
961 except Exception:
962 main.log.exception( self.name + ": Uncaught exception!" )
963 return main.FALSE
964
965 def preDisconnect( self ):
966 """
967 A Stub for a function that will be called before disconnect.
968 This can be set if for instance, the shell is running a program
969 and needs to exit the program before disconnecting from the component
970 """
971 print "preDisconnect"
972 return main.TRUE
973
974 def kubectlGetPodNames( self, kubeconfig=None, namespace=None, app=None, name=None ):
975 """
976 Use kubectl to get the names of pods
977 Optional Arguments:
978 - kubeconfig: The path to a kubeconfig file
979 - namespace: The namespace to search in
980 - app: Get pods belonging to a specific app
981 - name: Get pods with a specific name label
982 Returns a list containing the names of the pods or
983 main.FALSE on Error
984 """
985
986 try:
987 cmdStr = "kubectl %s %s get pods %s %s --output=jsonpath='{.items..metadata.name}{\"\\n\"}'" % (
988 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
989 "-n %s" % namespace if namespace else "",
990 "-l app=%s" % app if app else "",
991 "-l name=%s" % name if name else "" )
992 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
993 self.handle.sendline( cmdStr )
994 i = self.handle.expect( [ "not found", "error", "The connection to the server", self.prompt ] )
995 if i == 3:
996 output = self.handle.before + self.handle.after
997 names = output.split( '\r\n' )[1].split()
998 return names
999 else:
1000 main.log.error( self.name + ": Error executing command" )
1001 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1002 return main.FALSE
1003 except pexpect.EOF:
1004 main.log.error( self.name + ": EOF exception found" )
1005 main.log.error( self.name + ": " + self.handle.before )
1006 return main.FALSE
1007 except pexpect.TIMEOUT:
1008 main.log.exception( self.name + ": TIMEOUT exception found" )
1009 main.log.error( self.name + ": " + self.handle.before )
1010 return main.FALSE
1011 except Exception:
1012 main.log.exception( self.name + ": Uncaught exception!" )
1013 return main.FALSE
1014
1015 def kubectlDescribe( self, describeString, dstPath, kubeconfig=None, namespace=None ):
1016 """
1017 Use kubectl to get the logs from a pod
1018 Required Arguments:
1019 - describeString: The string passed to the cli. Example: "pods"
1020 - dstPath: The location to save the logs to
1021 Optional Arguments:
1022 - kubeconfig: The path to a kubeconfig file
1023 - namespace: The namespace to search in
1024 Returns main.TRUE or
1025 main.FALSE on Error
1026 """
1027
1028 try:
1029 cmdStr = "kubectl %s %s describe %s > %s " % (
1030 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1031 "-n %s" % namespace if namespace else "",
1032 describeString,
1033 dstPath )
1034 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1035 self.handle.sendline( cmdStr )
1036 i = self.handle.expect( [ "not found", "error", "The connection to the server", self.prompt ] )
1037 if i == 3:
1038 main.log.debug( self.name + ": " + self.handle.before )
1039 return main.TRUE
1040 else:
1041 main.log.error( self.name + ": Error executing command" )
1042 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1043 return main.FALSE
1044 except pexpect.EOF:
1045 main.log.error( self.name + ": EOF exception found" )
1046 main.log.error( self.name + ": " + self.handle.before )
1047 return main.FALSE
1048 except pexpect.TIMEOUT:
1049 main.log.exception( self.name + ": TIMEOUT exception found" )
1050 main.log.error( self.name + ": " + self.handle.before )
1051 return main.FALSE
1052 except Exception:
1053 main.log.exception( self.name + ": Uncaught exception!" )
1054 return main.FALSE
1055
1056 def kubectlPodNodes( self, dstPath=None, kubeconfig=None, namespace=None ):
1057 """
1058 Use kubectl to get the logs from a pod
1059 Optional Arguments:
1060 - dstPath: The location to save the logs to
1061 - kubeconfig: The path to a kubeconfig file
1062 - namespace: The namespace to search in
1063 Returns main.TRUE if dstPath is given, else the output of the command or
1064 main.FALSE on Error
1065 """
1066
1067 try:
1068 cmdStr = "kubectl %s %s get pods -o=custom-columns=NAME:.metadata.name,NODE:.spec.nodeName %s " % (
1069 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1070 "-n %s" % namespace if namespace else "",
1071 " > %s" % dstPath if dstPath else "" )
1072 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1073 self.handle.sendline( cmdStr )
1074 i = self.handle.expect( [ "not found", "error", "The connection to the server", self.prompt ] )
1075 if i == 3:
1076 output = self.handle.before
1077 main.log.debug( self.name + ": " + output )
1078 return output if dstPath else main.TRUE
1079 else:
1080 main.log.error( self.name + ": Error executing command" )
1081 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1082 return main.FALSE
1083 except pexpect.EOF:
1084 main.log.error( self.name + ": EOF exception found" )
1085 main.log.error( self.name + ": " + self.handle.before )
1086 return main.FALSE
1087 except pexpect.TIMEOUT:
1088 main.log.exception( self.name + ": TIMEOUT exception found" )
1089 main.log.error( self.name + ": " + self.handle.before )
1090 return main.FALSE
1091 except Exception:
1092 main.log.exception( self.name + ": Uncaught exception!" )
1093 return main.FALSE
1094
1095 def kubectlLogs( self, podName, dstPath, kubeconfig=None, namespace=None, timeout=240 ):
1096 """
1097 Use kubectl to get the logs from a pod
1098 Required Arguments:
1099 - podName: The name of the pod to get the logs of
1100 - dstPath: The location to save the logs to
1101 Optional Arguments:
1102 - kubeconfig: The path to a kubeconfig file
1103 - namespace: The namespace to search in
1104 - timeout: Timeout for command to return. The longer the logs, the longer it will take to fetch them.
1105 Returns main.TRUE or
1106 main.FALSE on Error
1107 """
1108
1109 try:
1110 cmdStr = "kubectl %s %s logs %s > %s " % (
1111 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1112 "-n %s" % namespace if namespace else "",
1113 podName,
1114 dstPath )
1115 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1116 self.handle.sendline( cmdStr )
1117 i = self.handle.expect( [ "not found", "error", "The connection to the server", self.prompt ], timeout=timeout )
1118 if i == 3:
1119 main.log.debug( self.name + ": " + self.handle.before )
1120 return main.TRUE
1121 else:
1122 main.log.error( self.name + ": Error executing command" )
1123 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1124 return main.FALSE
1125 except pexpect.EOF:
1126 main.log.error( self.name + ": EOF exception found" )
1127 main.log.error( self.name + ": " + self.handle.before )
1128 return main.FALSE
1129 except pexpect.TIMEOUT:
1130 main.log.exception( self.name + ": TIMEOUT exception found" )
1131 main.log.error( self.name + ": " + self.handle.before )
1132 return main.FALSE
1133 except Exception:
1134 main.log.exception( self.name + ": Uncaught exception!" )
1135 return main.FALSE
1136
1137 def kubectlPortForward( self, podName, portsList, kubeconfig=None, namespace=None, ):
1138 """
1139 Use kubectl to setup port forwarding from the local machine to the kubernetes pod
1140
1141 Note: This command does not return until the port forwarding session is ended.
1142
1143 Required Arguments:
1144 - podName: The name of the pod as a string
1145 - portsList: The list of ports to forward, as a string. see kubectl help for details
1146 Optional Arguments:
1147 - kubeconfig: The path to a kubeconfig file
1148 - namespace: The namespace to search in
1149 - app: Get pods belonging to a specific app
1150 Returns a list containing the names of the pods or
1151 main.FALSE on Error
1152
1153
1154 """
1155 try:
1156 cmdStr = "kubectl %s %s port-forward pod/%s %s" % (
1157 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1158 "-n %s" % namespace if namespace else "",
1159 podName,
1160 portsList )
1161 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1162 self.handle.sendline( cmdStr )
1163 i = self.handle.expect( [ "not found", "error", "closed/timedout",
1164 self.prompt, "The connection to the server", "Forwarding from" ] )
1165 # NOTE: This won't clear the buffer entirely, and each time the port forward
1166 # is used, another line will be added to the buffer. We need to make
1167 # sure we clear the buffer before using this component again.
1168
1169 if i == 5:
1170 # Setup preDisconnect function
1171 self.preDisconnect = self.exitFromProcess
1172 return main.TRUE
1173 else:
1174 main.log.error( self.name + ": Error executing command" )
1175 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1176 return main.FALSE
1177 except pexpect.EOF:
1178 main.log.error( self.name + ": EOF exception found" )
1179 main.log.error( self.name + ": " + self.handle.before )
1180 return main.FALSE
1181 except pexpect.TIMEOUT:
1182 main.log.exception( self.name + ": TIMEOUT exception found" )
1183 main.log.error( self.name + ": " + self.handle.before )
1184 return main.FALSE
1185 except Exception:
1186 main.log.exception( self.name + ": Uncaught exception!" )
1187 return main.FALSE