blob: 2951ec309f88749bc289bd64e27e199c8cc2b5ec [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
325 while i <= 6 :
Jon Hall547e0582015-09-21 17:35:40 -0700326 i = self.handle.expect( [
327 ssh_newkey,
328 'password:',
329 "100%",
330 refused,
331 "No such file or directory",
Jon Hall53c5e662016-04-13 16:06:56 -0700332 "Permission denied",
Devin Limdc78e202017-06-09 18:30:07 -0700333 self.prompt,
Jon Hall547e0582015-09-21 17:35:40 -0700334 pexpect.EOF,
335 pexpect.TIMEOUT ],
Jon Hall669bc862021-03-09 12:24:44 -0800336 timeout=timeout )
Jon Hall547e0582015-09-21 17:35:40 -0700337 if i == 0: # ask for ssh key confirmation
Jon Hall3c0114c2020-08-11 15:07:42 -0700338 main.log.info( self.name + ": ssh key confirmation received, sending yes" )
Jon Hall547e0582015-09-21 17:35:40 -0700339 self.handle.sendline( 'yes' )
340 elif i == 1: # Asked for ssh password
Jon Hall669bc862021-03-09 12:24:44 -0800341 timeout = 120
Jon Hall3c0114c2020-08-11 15:07:42 -0700342 main.log.info( self.name + ": ssh connection asked for password, gave password" )
Jon Hall547e0582015-09-21 17:35:40 -0700343 self.handle.sendline( pwd )
344 elif i == 2: # File finished transfering
Jon Hall3c0114c2020-08-11 15:07:42 -0700345 main.log.info( self.name + ": Secure copy successful" )
Jon Hall669bc862021-03-09 12:24:44 -0800346 timeout = 10
Jon Hall547e0582015-09-21 17:35:40 -0700347 returnVal = main.TRUE
348 elif i == 3: # Connection refused
349 main.log.error(
350 "ssh: connect to host " +
351 ipAddress +
352 " port 22: Connection refused" )
353 returnVal = main.FALSE
354 elif i == 4: # File Not found
Jon Hall3c0114c2020-08-11 15:07:42 -0700355 main.log.error( self.name + ": No such file found" )
Jon Hall39570262020-11-17 12:18:19 -0800356 main.log.debug( self.handle.before + self.handle.after )
Jon Hall547e0582015-09-21 17:35:40 -0700357 returnVal = main.FALSE
Jon Hall53c5e662016-04-13 16:06:56 -0700358 elif i == 5: # Permission denied
Jon Hall3c0114c2020-08-11 15:07:42 -0700359 main.log.error( self.name + ": Permission denied. Check folder permissions" )
Jon Hall39570262020-11-17 12:18:19 -0800360 main.log.debug( self.handle.before + self.handle.after )
Jon Hall53c5e662016-04-13 16:06:56 -0700361 returnVal = main.FALSE
362 elif i == 6: # prompt returned
Jon Hall669bc862021-03-09 12:24:44 -0800363 timeout = 10
364 main.log.debug( "%s: %s%s" % ( self.name, repr( self.handle.before ), repr( self.handle.after ) ) )
Jon Hall53c5e662016-04-13 16:06:56 -0700365 elif i == 7: # EOF
Jon Hall3c0114c2020-08-11 15:07:42 -0700366 main.log.error( self.name + ": Pexpect.EOF found!!!" )
Devin Lim44075962017-08-11 10:56:37 -0700367 main.cleanAndExit()
Jon Hall53c5e662016-04-13 16:06:56 -0700368 elif i == 8: # timeout
Jon Hall669bc862021-03-09 12:24:44 -0800369 if returnVal != main.TRUE:
370 main.log.error(
371 "No route to the Host " +
372 userName +
373 "@" +
374 ipAddress )
375 return returnVal
376 self.handle.expect( [ self.prompt, pexpect.TIMEOUT ], timeout=5 )
377 main.log.debug( "%s: %s%s" % ( self.name, repr( self.handle.before ), repr( self.handle.after ) ) )
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700378 return returnVal
379
Jon Hall39570262020-11-17 12:18:19 -0800380 def scp( self, remoteHost, filePath, dstPath, direction="from", options="" ):
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700381 """
382 Definition:
383 Execute scp command in linux to copy to/from a remote host
384 Required:
385 * remoteHost - Test ON component to be parsed
386 str filePath - File path including the file it self
387 str dstPath - Destination path
388 Optional:
389 str direction - Direction of the scp, default to "from" which means
390 copy "from" the remote machine to local machine,
391 while "to" means copy "to" the remote machine from
392 local machine
393 """
Jon Hall06fd0df2021-01-25 15:50:06 -0800394 jump_host = main.componentDictionary[ remoteHost.name ].get( 'jump_host' )
395 if jump_host:
396 jump_host = main.componentDictionary.get( jump_host )
Jon Hall669bc862021-03-09 12:24:44 -0800397 options += " -o 'ProxyJump %s@%s' " % ( jump_host.get( 'user' ), jump_host.get( 'host' ) )
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700398 return self.secureCopy( remoteHost.user_name,
399 remoteHost.ip_address,
400 filePath,
401 dstPath,
402 pwd=remoteHost.pwd,
Jon Hall39570262020-11-17 12:18:19 -0800403 direction=direction,
404 options=options )
Devin Lim142b5342017-07-20 15:22:39 -0700405
406 def sshToNode( self, ipAddress, uName="sdn", pwd="rocks" ):
407 ssh_newkey = 'Are you sure you want to continue connecting'
408 refused = "ssh: connect to host " + ipAddress + " port 22: Connection refused"
409 handle = pexpect.spawn( 'ssh -X ' +
410 uName +
411 '@' +
412 ipAddress,
Jon Hall6c9e2da2018-11-06 12:01:23 -0800413 env={ "TERM": "vt100" },
Devin Lim142b5342017-07-20 15:22:39 -0700414 maxread=1000000,
415 timeout=60 )
416
417 # set tty window size
418 handle.setwinsize( 24, 250 )
419
420 i = 5
421 while i == 5:
Jon Hall4173b242017-09-12 17:04:38 -0700422 i = handle.expect( [ ssh_newkey,
423 'password:|Password:',
424 pexpect.EOF,
425 pexpect.TIMEOUT,
426 refused,
427 'teston>',
428 self.prompt ],
429 120 )
Devin Lim142b5342017-07-20 15:22:39 -0700430 if i == 0: # Accept key, then expect either a password prompt or access
Jon Hall3c0114c2020-08-11 15:07:42 -0700431 main.log.info( self.name + ": ssh key confirmation received, send yes" )
Devin Lim142b5342017-07-20 15:22:39 -0700432 handle.sendline( 'yes' )
433 i = 5 # Run the loop again
434 continue
435 if i == 1: # Password required
436 if pwd:
437 main.log.info(
Jon Hall4173b242017-09-12 17:04:38 -0700438 "ssh connection asked for password, gave password" )
Devin Lim142b5342017-07-20 15:22:39 -0700439 else:
Jon Hall3c0114c2020-08-11 15:07:42 -0700440 main.log.info( self.name + ": Server asked for password, but none was "
Devin Lim142b5342017-07-20 15:22:39 -0700441 "given in the .topo file. Trying "
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700442 "no password." )
Devin Lim142b5342017-07-20 15:22:39 -0700443 pwd = ""
444 handle.sendline( pwd )
445 j = handle.expect( [ self.prompt,
446 'password:|Password:',
447 pexpect.EOF,
448 pexpect.TIMEOUT ],
449 120 )
450 if j != 0:
Jon Hall3c0114c2020-08-11 15:07:42 -0700451 main.log.error( self.name + ": Incorrect Password" )
Devin Lim44075962017-08-11 10:56:37 -0700452 main.cleanAndExit()
Devin Lim142b5342017-07-20 15:22:39 -0700453 elif i == 2:
Jon Hall3c0114c2020-08-11 15:07:42 -0700454 main.log.error( self.name + ": Connection timeout" )
Devin Lim44075962017-08-11 10:56:37 -0700455 main.cleanAndExit()
Devin Lim142b5342017-07-20 15:22:39 -0700456 elif i == 3: # timeout
457 main.log.error(
458 "No route to the Host " +
459 uName +
460 "@" +
461 ipAddress )
Devin Lim44075962017-08-11 10:56:37 -0700462 main.cleanAndExit()
Devin Lim142b5342017-07-20 15:22:39 -0700463 elif i == 4:
464 main.log.error(
465 "ssh: connect to host " +
466 ipAddress +
467 " port 22: Connection refused" )
Devin Lim44075962017-08-11 10:56:37 -0700468 main.cleanAndExit()
Devin Lim142b5342017-07-20 15:22:39 -0700469 elif i == 6:
Jon Hall3c0114c2020-08-11 15:07:42 -0700470 main.log.info( self.name + ": Password not required logged in" )
Devin Lim142b5342017-07-20 15:22:39 -0700471
472 handle.sendline( "" )
473 handle.expect( self.prompt )
474 handle.sendline( "cd" )
475 handle.expect( self.prompt )
476
Jon Hall3c0114c2020-08-11 15:07:42 -0700477 main.log.info( self.name + ": Successfully ssh to " + ipAddress + "." )
Devin Lim142b5342017-07-20 15:22:39 -0700478 return handle
479
480 def exitFromSsh( self, handle, ipAddress ):
Devin Lim142b5342017-07-20 15:22:39 -0700481 try:
Jon Hall4f360bc2017-09-07 10:19:52 -0700482 handle.sendline( "logout" )
Devin Lim142b5342017-07-20 15:22:39 -0700483 handle.expect( "closed." )
Jon Hall3c0114c2020-08-11 15:07:42 -0700484 main.log.info( self.name + ": Successfully closed ssh connection from " + ipAddress )
Devin Lim142b5342017-07-20 15:22:39 -0700485 except pexpect.EOF:
Jon Hall3c0114c2020-08-11 15:07:42 -0700486 main.log.error( self.name + ": Failed to close the connection from " + ipAddress )
Jon Hall4f360bc2017-09-07 10:19:52 -0700487 try:
488 # check that this component handle still works
489 self.handle.sendline( "" )
490 self.handle.expect( self.prompt )
491 except pexpect.EOF:
492 main.log.error( self.handle.before )
Jon Hall3c0114c2020-08-11 15:07:42 -0700493 main.log.error( self.name + ": EOF after closing ssh connection" )
Jon Hall4173b242017-09-12 17:04:38 -0700494
495 def folderSize( self, path, size='10', unit='M', ignoreRoot=True ):
496 """
497 Run `du -h` on the folder path and verifies the folder(s) size is
498 less than the given size. Note that if multiple subdirectories are
499 present, the result will be the OR of all the individual subdirectories.
500
501 Arguments:
502 path - A string containing the path supplied to the du command
503 size - The number portion of the file size that the results will be compared to
504 unit - The unit portion of the file size that the results will be compared to
505 ignoreRoot - If True, will ignore the "root" of the path supplied to du. I.E. will ignore `.`
506
507 Returns True if the folder(s) size(s) are less than SIZE UNITS, else returns False
508 """
509 sizeRe = r'(?P<number>\d+\.*\d*)(?P<unit>\D)'
510 unitsList = [ 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y' ]
511 try:
512 # make sure we convert units if size is too big
513 size = float( size )
514 if size >= 1000:
515 size = size / 1000
516 unit = unitsList[ unitsList.index( unit + 1 ) ]
517 cmdStr = "du -h " + path
518 self.handle.sendline( cmdStr )
519 self.handle.expect( self.prompt )
520 output = self.handle.before
521 assert "cannot access" not in output
522 assert "command not found" not in output
523 main.log.debug( output )
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700524 lines = [ line for line in output.split( '\r\n' ) ]
Jon Hall4173b242017-09-12 17:04:38 -0700525 retValue = True
526 if ignoreRoot:
527 lastIndex = -2
528 else:
529 lastIndex = -1
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700530 for line in lines[ 1:lastIndex ]:
Jon Hall4173b242017-09-12 17:04:38 -0700531 parsed = line.split()
Jeremy Ronquillo82705492017-10-18 14:19:55 -0700532 sizeMatch = parsed[ 0 ]
533 folder = parsed[ 1 ]
Jon Hall4173b242017-09-12 17:04:38 -0700534 match = re.search( sizeRe, sizeMatch )
535 num = match.group( 'number' )
536 unitMatch = match.group( 'unit' )
537 if unitsList.index( unitMatch ) < unitsList.index( unit ):
538 retValue &= True
539 elif unitsList.index( unitMatch ) == unitsList.index( unit ):
540 if float( num ) < float( size ):
541 retValue &= True
542 else:
543 retValue &= False
544 elif unitsList.index( unitMatch ) > unitsList.index( unit ):
545 retValue &= False
546 return retValue
547 except AssertionError:
548 main.log.error( self.name + ": Could not execute command: " + output )
549 return False
Jon Hall43060f62020-06-23 13:13:33 -0700550 except ValueError as e:
551 main.log.error( self.name + ": Error parsing output: " + output )
552 main.log.error( e )
553 return False
Jon Hall4173b242017-09-12 17:04:38 -0700554 except pexpect.TIMEOUT:
555 main.log.exception( self.name + ": TIMEOUT exception found" )
556 main.log.error( self.name + ": " + self.handle.before )
557 return False
558 except pexpect.EOF:
559 main.log.error( self.name + ": EOF exception found" )
560 main.log.error( self.name + ": " + self.handle.before )
561 main.cleanAndExit()
Jon Hall0e240372018-05-02 11:21:57 -0700562
563 def setEnv( self, variable, value=None ):
564 """
565 Sets the environment variable to the given value for the current shell session.
566 If value is None, will unset the variable.
567
568 Required Arguments:
569 variable - The name of the environment variable to set.
570
571 Optional Arguments:
572 value - The value to set the variable to. ( Defaults to None, which unsets the variable )
573
574 Returns True if no errors are detected else returns False
575 """
576 try:
577 if value:
578 cmd = "export {}={}".format( variable, value )
579 else:
580 cmd = "unset {}".format( variable )
581 self.handle.sendline( cmd )
582 self.handle.expect( self.prompt )
Jon Hall3c0114c2020-08-11 15:07:42 -0700583 output = self.handle.before
584 main.log.debug( output )
Jon Hall0e240372018-05-02 11:21:57 -0700585 return True
586 except AssertionError:
587 main.log.error( self.name + ": Could not execute command: " + output )
588 return False
589 except pexpect.TIMEOUT:
590 main.log.exception( self.name + ": TIMEOUT exception found" )
591 main.log.error( self.name + ": " + self.handle.before )
592 return False
593 except pexpect.EOF:
594 main.log.error( self.name + ": EOF exception found" )
595 main.log.error( self.name + ": " + self.handle.before )
596 main.cleanAndExit()
You Wangb65d2372018-08-17 15:37:59 -0700597
598 def exitFromCmd( self, expect, retry=10 ):
599 """
600 Call this function when sending ctrl+c is required to kill the current
601 command. It will retry multiple times until the running command is
602 completely killed and expected string is returned from the handle.
603 Required:
You Wangd4fae5c2018-08-22 13:56:49 -0700604 expect: expected string or list of strings which indicates that the
605 previous command was killed successfully.
You Wangb65d2372018-08-17 15:37:59 -0700606 Optional:
607 retry: maximum number of ctrl+c that will be sent.
608 """
You Wangd4fae5c2018-08-22 13:56:49 -0700609 expect = [ expect ] if isinstance( expect, str ) else expect
You Wangb65d2372018-08-17 15:37:59 -0700610 try:
611 while retry >= 0:
612 main.log.debug( self.name + ": sending ctrl+c to kill the command" )
613 self.handle.send( "\x03" )
You Wangd4fae5c2018-08-22 13:56:49 -0700614 i = self.handle.expect( expect + [ pexpect.TIMEOUT ], timeout=3 )
You Wangb65d2372018-08-17 15:37:59 -0700615 main.log.debug( self.handle.before )
You Wangd4fae5c2018-08-22 13:56:49 -0700616 if i < len( expect ):
You Wangb65d2372018-08-17 15:37:59 -0700617 main.log.debug( self.name + ": successfully killed the command" )
618 return main.TRUE
619 retry -= 1
620 main.log.warn( self.name + ": failed to kill the command" )
621 return main.FALSE
622 except pexpect.EOF:
623 main.log.error( self.name + ": EOF exception found" )
624 main.log.error( self.name + ": " + self.handle.before )
625 return main.FALSE
626 except Exception:
627 main.log.exception( self.name + ": Uncaught exception!" )
628 return main.FALSE
Jon Hall43060f62020-06-23 13:13:33 -0700629
630 def cleanOutput( self, output, debug=False ):
631 """
632 Clean ANSI characters from output
633 """
634 ansiEscape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
635 cleaned = ansiEscape.sub( '', output )
636 if debug:
637 main.log.debug( self.name + ": cleanOutput:" )
638 main.log.debug( self.name + ": " + repr( cleaned ) )
639 return cleaned
Jon Hall3c0114c2020-08-11 15:07:42 -0700640
641 def dockerPull( self, image, tag=None ):
642 """
643 Pull a docker image from a registry
644 """
645 try:
646 imgStr = "%s%s" % ( image, ":%s" % tag if tag else "" )
647 cmdStr = "docker pull %s" % imgStr
648 main.log.info( self.name + ": sending: " + cmdStr )
649 self.handle.sendline( cmdStr)
650 i = self.handle.expect( [ self.prompt,
651 "Error response from daemon",
652 pexpect.TIMEOUT ], 120 )
653 if i == 0:
654 return main.TRUE
655 else:
656 main.log.error( self.name + ": Error pulling docker image " + imgStr )
657 output = self.handle.before + str( self.handle.after )
658 if i == 1:
659 self.handle.expect( self.prompt )
660 output += self.handle.before + str( self.handle.after )
661 main.log.debug( self.name + ": " + output )
662 return main.FALSE
663 except pexpect.EOF:
664 main.log.error( self.name + ": EOF exception found" )
665 main.log.error( self.name + ": " + self.handle.before )
666 return main.FALSE
667 except Exception:
668 main.log.exception( self.name + ": Uncaught exception!" )
669 return main.FALSE
670
671 def dockerBuild( self, path, imageTag, pull=False, options="", timeout=600 ):
672 """
673 Build a docker image
674 Required Arguments:
675 - path: Path to the dockerfile, it is recommended to avoid relative paths
676 - imageTag: Give a tag to the built docker image
677 Optional Arguments:
678 - pull: Whether to attempt to pull latest images before building
679 - options: A string containing any addition optional arguments
680 for the docker build command
681 - timeout: How many seconds to wait for the build to complete
682 """
683 try:
684 response = main.TRUE
685 if pull:
686 options = "--pull " + options
687 cmdStr = "docker build -t %s %s %s" % ( imageTag, options, path )
688 main.log.info( self.name + ": sending: " + cmdStr )
689 self.handle.sendline( cmdStr)
690 i = self.handle.expect( [ "Successfully built",
691 "Error response from daemon",
692 pexpect.TIMEOUT ], timeout=timeout )
693 output = self.handle.before
694 if i == 0:
695 output += self.handle.after
696 self.handle.expect( self.prompt )
697 output += self.handle.before + self.handle.after
698 return response
699 elif i == 1:
700 response = main.FALSE
701 output += self.handle.after
702 self.handle.expect( self.prompt )
703 output += self.handle.before + self.handle.after
704 elif i == 2:
705 response = main.FALSE
706 main.log.error( self.name + ": Error building docker image" )
707 main.log.debug( self.name + ": " + output )
708 return response
709 except pexpect.EOF:
710 main.log.error( self.name + ": EOF exception found" )
711 main.log.error( self.name + ": " + self.handle.before )
712 return main.FALSE
713 except Exception:
714 main.log.exception( self.name + ": Uncaught exception!" )
715 return main.FALSE
716
717 def dockerStop( self, containerName ):
718 """
719 Stop a docker container
720 Required Arguments:
721 - containerName: Name of the container to stop
722 """
723 try:
724 cmdStr = "docker stop %s" % ( containerName )
725 main.log.info( self.name + ": sending: " + cmdStr )
726 self.handle.sendline( cmdStr)
727 i = self.handle.expect( [ self.prompt,
728 "Error response from daemon",
729 pexpect.TIMEOUT ], 120 )
730 output = self.handle.before
731 if i == 0:
732 return main.TRUE
733 elif i == 1:
734 output += self.handle.after
735 self.handle.expect( self.prompt )
736 output += self.handle.before
737 elif i == 2:
738 pass
739 main.log.debug( "%s: %s" % ( self.name, output ) )
740 if "No such container" in output:
741 return main.TRUE
742 main.log.error( self.name + ": Error stopping docker image" )
743 main.log.debug( self.name + ": " + output )
744 return main.FALSE
745 except pexpect.EOF:
746 main.log.error( self.name + ": EOF exception found" )
747 main.log.error( self.name + ": " + self.handle.before )
748 return main.FALSE
749 except Exception:
750 main.log.exception( self.name + ": Uncaught exception!" )
751 return main.FALSE
752
Jon Hall06fd0df2021-01-25 15:50:06 -0800753 def dockerRun( self, image, containerName, options="", imageArgs="", background=False ):
Jon Hall3c0114c2020-08-11 15:07:42 -0700754 """
755 Run a docker image
756 Required Arguments:
757 - containerName: Give a name to the container once its started
758 - image: Run the given image
759 Optional Arguments:
760 - options: A string containing any addition optional arguments
761 for the docker run command
762 - imageArgs: A string containing command line arguments for the
763 command run by docker
764 """
765 try:
766 cmdStr = "docker run --name %s %s %s %s" % ( containerName,
767 options if options else "",
768 image,
769 imageArgs )
Jon Hall06fd0df2021-01-25 15:50:06 -0800770 if background:
771 cmdStr += " &"
Jon Hall3c0114c2020-08-11 15:07:42 -0700772 main.log.info( self.name + ": sending: " + cmdStr )
773 self.handle.sendline( cmdStr)
774 i = self.handle.expect( [ self.prompt,
775 "Error response from daemon",
776 pexpect.TIMEOUT ], 120 )
777 if i == 0:
778 return main.TRUE
779 else:
780 output = self.handle.before
781 main.log.debug( self.name + ": " + output )
782 main.log.error( self.name + ": Error running docker image" )
783 if i == 1:
784 output += self.handle.after
785 self.handle.expect( self.prompt )
786 output += self.handle.before + self.handle.after
787 main.log.debug( self.name + ": " + output )
788 return main.FALSE
789 except pexpect.EOF:
790 main.log.error( self.name + ": EOF exception found" )
791 main.log.error( self.name + ": " + self.handle.before )
792 return main.FALSE
793 except Exception:
794 main.log.exception( self.name + ": Uncaught exception!" )
795 return main.FALSE
796
797 def dockerAttach( self, containerName, dockerPrompt="" ):
798 """
799 Attach to a docker image
800 Required Arguments:
801 - containerName: The name of the container to attach to
802 Optional Arguments:
803 - dockerPrompt: a regex for matching the docker shell prompt
804 """
805 try:
806 if dockerPrompt:
807 self.dockerPrompt = dockerPrompt
808 cmdStr = "docker attach %s" % containerName
809 main.log.info( self.name + ": sending: " + cmdStr )
810 self.handle.sendline( cmdStr)
811 i = self.handle.expect( [ self.dockerPrompt,
812 "Error response from daemon",
813 pexpect.TIMEOUT ] )
814 if i == 0:
815 self.inDocker = True
816 return main.TRUE
817 else:
818 main.log.error( self.name + ": Error connecting to docker container" )
819 output = self.handle.before + str( self.handle.after )
820 if i == 1:
821 self.handle.expect( self.prompt )
822 output += self.handle.before + str( self.handle.after )
823 main.log.debug( self.name + ": " + output )
824 return main.FALSE
825 except pexpect.EOF:
826 main.log.error( self.name + ": EOF exception found" )
827 main.log.error( self.name + ": " + self.handle.before )
828 return main.FALSE
829 except AttributeError as e:
830 main.log.exception( self.name + ": AttributeError - " + str( e ) )
831 main.log.warn( self.name + ": Make sure dockerPrompt is set" )
832 main.cleanup()
833 main.exit()
834 except Exception:
835 main.log.exception( self.name + ": Uncaught exception!" )
836 return main.FALSE
837
838 def dockerExec( self, containerName, command="/bin/bash", options="-it", dockerPrompt="" ):
839 """
840 Attach to a docker image
841 Required Arguments:
842 - containerName: The name of the container to attach to
843 Optional Arguments:
844 - command: Command to run in the docker container
845 - options: Docker exec options
846 - dockerPrompt: a regex for matching the docker shell prompt
847 """
848 try:
849 if dockerPrompt:
850 self.dockerPrompt = dockerPrompt
851 cmdStr = "docker exec %s %s %s" % ( options, containerName, command )
852 main.log.info( self.name + ": sending: " + cmdStr )
853 self.handle.sendline( cmdStr)
854 i = self.handle.expect( [ self.dockerPrompt,
855 "Error response from daemon",
856 pexpect.TIMEOUT ] )
857 if i == 0:
858 self.inDocker = True
859 return main.TRUE
860 else:
861 main.log.error( self.name + ": Error connecting to docker container" )
862 output = self.handle.before + str( self.handle.after )
863 if i == 1:
864 self.handle.expect( self.prompt )
865 output += self.handle.before + str( self.handle.after )
866 main.log.debug( self.name + ": " + output )
867 return main.FALSE
868 except pexpect.EOF:
869 main.log.error( self.name + ": EOF exception found" )
870 main.log.error( self.name + ": " + self.handle.before )
871 return main.FALSE
872 except AttributeError as e:
873 main.log.exception( self.name + ": AttributeError - " + str( e ) )
874 main.log.warn( self.name + ": Make sure dockerPrompt is set" )
875 main.cleanup()
876 main.exit()
877 except Exception:
878 main.log.exception( self.name + ": Uncaught exception!" )
879 return main.FALSE
880
881 def dockerCp( self, containerName, dockerPath, hostPath, direction="from" ):
882 """
883 Copy a file from/to a docker container to the host
884 Required Arguments:
885 - containerName: The name of the container to copy from/to
886 - dockerPath: the path in the container to copy from/to
887 - hostPath: the path on the host to copy to/from
888 Optional Arguments:
889 - direction: Choose whether to copy "from" the container or "to" the container
890 """
891 try:
892 cmdStr = "docker cp "
893 if direction == "from":
894 cmdStr += "%s:%s %s" % ( containerName, dockerPath, hostPath )
895 elif direction == "to":
896 cmdStr += "%s %s:%s" % ( hostPath, containerName, dockerPath )
897 main.log.info( self.name + ": sending: " + cmdStr )
898 self.handle.sendline( cmdStr)
899 i = self.handle.expect( [ self.prompt,
900 "Error",
901 pexpect.TIMEOUT ] )
902 if i == 0:
903 retValue = main.TRUE
904 else:
905 main.log.error( self.name + ": Error in docker cp" )
906 output = self.handle.before + str( self.handle.after )
907 if i == 1:
908 self.handle.expect( self.prompt )
909 output += self.handle.before + str( self.handle.after )
910 main.log.debug( self.name + ": " + output )
911 retValue = main.FALSE
912 return retValue
913 except pexpect.EOF:
914 main.log.error( self.name + ": EOF exception found" )
915 main.log.error( self.name + ": " + self.handle.before )
916 return main.FALSE
917 except AttributeError as e:
918 main.log.exception( self.name + ": AttributeError - " + str( e ) )
919 main.log.warn( self.name + ": Make sure dockerPrompt is set" )
920 main.cleanup()
921 main.exit()
922 except Exception:
923 main.log.exception( self.name + ": Uncaught exception!" )
924 return main.FALSE
925
926 def dockerDisconnect( self ):
927 """
928 Send ctrl-c, ctrl-d to session, which should close and exit the
929 attached docker session. This will likely exit the running program
930 in the container and also stop the container.
931 """
932 try:
933 cmdStr = "\x03"
934 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
935 self.handle.send( cmdStr)
936 cmdStr = "\x04"
937 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
938 self.handle.send( cmdStr)
939 i = self.handle.expect( [ self.prompt, pexpect.TIMEOUT ] )
940 if i == 0:
941 self.inDocker = False
942 return main.TRUE
943 else:
944 main.log.error( self.name + ": Error disconnecting from docker image" )
945 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
946 return main.FALSE
947 except pexpect.EOF:
948 main.log.error( self.name + ": EOF exception found" )
949 main.log.error( self.name + ": " + self.handle.before )
950 return main.FALSE
951 except Exception:
952 main.log.exception( self.name + ": Uncaught exception!" )
953 return main.FALSE
Jon Hall06fd0df2021-01-25 15:50:06 -0800954
955# TODO: How is this different from exitFromCmd used elsewhere?
956 def exitFromProcess( self ):
957 """
958 Send ctrl-c, which should close and exit the program
959 """
960 try:
961 cmdStr = "\x03"
962 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
963 self.handle.send( cmdStr)
964 i = self.handle.expect( [ self.prompt, pexpect.TIMEOUT ] )
965 if i == 0:
966 return main.TRUE
967 else:
968 main.log.error( self.name + ": Error exiting process" )
969 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
970 return main.FALSE
971 except pexpect.EOF:
972 main.log.error( self.name + ": EOF exception found" )
973 main.log.error( self.name + ": " + self.handle.before )
974 return main.FALSE
975 except Exception:
976 main.log.exception( self.name + ": Uncaught exception!" )
977 return main.FALSE
978
979 def preDisconnect( self ):
980 """
981 A Stub for a function that will be called before disconnect.
982 This can be set if for instance, the shell is running a program
983 and needs to exit the program before disconnecting from the component
984 """
985 print "preDisconnect"
986 return main.TRUE
987
Jon Hallbe3a2ac2021-03-15 12:28:06 -0700988 def kubectlGetPodNames( self, kubeconfig=None, namespace=None, app=None, name=None, nodeName=None ):
Jon Hall06fd0df2021-01-25 15:50:06 -0800989 """
990 Use kubectl to get the names of pods
991 Optional Arguments:
992 - kubeconfig: The path to a kubeconfig file
993 - namespace: The namespace to search in
994 - app: Get pods belonging to a specific app
995 - name: Get pods with a specific name label
Jon Hallbe3a2ac2021-03-15 12:28:06 -0700996 - nodeName: Get pods on a specific node
Jon Hall06fd0df2021-01-25 15:50:06 -0800997 Returns a list containing the names of the pods or
998 main.FALSE on Error
999 """
1000
1001 try:
Jon Hallbe3a2ac2021-03-15 12:28:06 -07001002 cmdStr = "kubectl %s %s get pods %s %s %s --output=jsonpath='{.items..metadata.name}{\"\\n\"}'" % (
Jon Hall06fd0df2021-01-25 15:50:06 -08001003 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1004 "-n %s" % namespace if namespace else "",
1005 "-l app=%s" % app if app else "",
Jon Hallbe3a2ac2021-03-15 12:28:06 -07001006 "-l name=%s" % name if name else "",
1007 "--field-selector=spec.nodeName=%s" % nodeName if nodeName else "" )
Jon Hall06fd0df2021-01-25 15:50:06 -08001008 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1009 self.handle.sendline( cmdStr )
1010 i = self.handle.expect( [ "not found", "error", "The connection to the server", self.prompt ] )
1011 if i == 3:
1012 output = self.handle.before + self.handle.after
1013 names = output.split( '\r\n' )[1].split()
1014 return names
1015 else:
1016 main.log.error( self.name + ": Error executing command" )
1017 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1018 return main.FALSE
1019 except pexpect.EOF:
1020 main.log.error( self.name + ": EOF exception found" )
1021 main.log.error( self.name + ": " + self.handle.before )
1022 return main.FALSE
1023 except pexpect.TIMEOUT:
1024 main.log.exception( self.name + ": TIMEOUT exception found" )
1025 main.log.error( self.name + ": " + self.handle.before )
1026 return main.FALSE
1027 except Exception:
1028 main.log.exception( self.name + ": Uncaught exception!" )
1029 return main.FALSE
1030
1031 def kubectlDescribe( self, describeString, dstPath, kubeconfig=None, namespace=None ):
1032 """
1033 Use kubectl to get the logs from a pod
1034 Required Arguments:
1035 - describeString: The string passed to the cli. Example: "pods"
1036 - dstPath: The location to save the logs to
1037 Optional Arguments:
1038 - kubeconfig: The path to a kubeconfig file
1039 - namespace: The namespace to search in
1040 Returns main.TRUE or
1041 main.FALSE on Error
1042 """
1043
1044 try:
1045 cmdStr = "kubectl %s %s describe %s > %s " % (
1046 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1047 "-n %s" % namespace if namespace else "",
1048 describeString,
1049 dstPath )
1050 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1051 self.handle.sendline( cmdStr )
1052 i = self.handle.expect( [ "not found", "error", "The connection to the server", self.prompt ] )
1053 if i == 3:
1054 main.log.debug( self.name + ": " + self.handle.before )
1055 return main.TRUE
1056 else:
1057 main.log.error( self.name + ": Error executing command" )
1058 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1059 return main.FALSE
1060 except pexpect.EOF:
1061 main.log.error( self.name + ": EOF exception found" )
1062 main.log.error( self.name + ": " + self.handle.before )
1063 return main.FALSE
1064 except pexpect.TIMEOUT:
1065 main.log.exception( self.name + ": TIMEOUT exception found" )
1066 main.log.error( self.name + ": " + self.handle.before )
1067 return main.FALSE
1068 except Exception:
1069 main.log.exception( self.name + ": Uncaught exception!" )
1070 return main.FALSE
1071
1072 def kubectlPodNodes( self, dstPath=None, kubeconfig=None, namespace=None ):
1073 """
1074 Use kubectl to get the logs from a pod
1075 Optional Arguments:
1076 - dstPath: The location to save the logs to
1077 - kubeconfig: The path to a kubeconfig file
1078 - namespace: The namespace to search in
1079 Returns main.TRUE if dstPath is given, else the output of the command or
1080 main.FALSE on Error
1081 """
1082
1083 try:
Jon Hall50a00012021-03-08 11:06:11 -08001084 cmdStr = "kubectl %s %s get pods -o wide %s " % (
Jon Hall06fd0df2021-01-25 15:50:06 -08001085 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1086 "-n %s" % namespace if namespace else "",
1087 " > %s" % dstPath if dstPath else "" )
1088 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1089 self.handle.sendline( cmdStr )
1090 i = self.handle.expect( [ "not found", "error", "The connection to the server", self.prompt ] )
1091 if i == 3:
1092 output = self.handle.before
1093 main.log.debug( self.name + ": " + output )
1094 return output if dstPath else main.TRUE
1095 else:
1096 main.log.error( self.name + ": Error executing command" )
1097 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1098 return main.FALSE
1099 except pexpect.EOF:
1100 main.log.error( self.name + ": EOF exception found" )
1101 main.log.error( self.name + ": " + self.handle.before )
1102 return main.FALSE
1103 except pexpect.TIMEOUT:
1104 main.log.exception( self.name + ": TIMEOUT exception found" )
1105 main.log.error( self.name + ": " + self.handle.before )
1106 return main.FALSE
1107 except Exception:
1108 main.log.exception( self.name + ": Uncaught exception!" )
1109 return main.FALSE
1110
1111 def kubectlLogs( self, podName, dstPath, kubeconfig=None, namespace=None, timeout=240 ):
1112 """
1113 Use kubectl to get the logs from a pod
1114 Required Arguments:
1115 - podName: The name of the pod to get the logs of
1116 - dstPath: The location to save the logs to
1117 Optional Arguments:
1118 - kubeconfig: The path to a kubeconfig file
1119 - namespace: The namespace to search in
1120 - timeout: Timeout for command to return. The longer the logs, the longer it will take to fetch them.
1121 Returns main.TRUE or
1122 main.FALSE on Error
1123 """
1124
1125 try:
1126 cmdStr = "kubectl %s %s logs %s > %s " % (
1127 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1128 "-n %s" % namespace if namespace else "",
1129 podName,
1130 dstPath )
1131 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1132 self.handle.sendline( cmdStr )
1133 i = self.handle.expect( [ "not found", "error", "The connection to the server", self.prompt ], timeout=timeout )
1134 if i == 3:
1135 main.log.debug( self.name + ": " + self.handle.before )
1136 return main.TRUE
1137 else:
1138 main.log.error( self.name + ": Error executing command" )
1139 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1140 return main.FALSE
1141 except pexpect.EOF:
1142 main.log.error( self.name + ": EOF exception found" )
1143 main.log.error( self.name + ": " + self.handle.before )
1144 return main.FALSE
1145 except pexpect.TIMEOUT:
1146 main.log.exception( self.name + ": TIMEOUT exception found" )
1147 main.log.error( self.name + ": " + self.handle.before )
1148 return main.FALSE
1149 except Exception:
1150 main.log.exception( self.name + ": Uncaught exception!" )
1151 return main.FALSE
1152
Jon Hallbe3a2ac2021-03-15 12:28:06 -07001153 def kubectlCp( self, podName, srcPath, dstPath, kubeconfig=None, namespace=None, timeout=240 ):
1154 """
1155 Use kubectl to get a file from a pod
1156 Required Arguments:
1157 - podName: The name of the pod to get the logs of
1158 - srcPath: The file to copy from the pod
1159 - dstPath: The location to save the file to locally
1160 Optional Arguments:
1161 - kubeconfig: The path to a kubeconfig file
1162 - namespace: The namespace to search in
1163 - timeout: Timeout for command to return. The longer the logs, the longer it will take to fetch them.
1164 Returns main.TRUE or
1165 main.FALSE on Error
1166 """
1167
1168 try:
1169 cmdStr = "kubectl %s %s cp %s:%s %s" % (
1170 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1171 "-n %s" % namespace if namespace else "",
1172 podName,
1173 srcPath,
1174 dstPath )
1175 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1176 self.handle.sendline( cmdStr )
1177 i = self.handle.expect( [ "not found", "error", "The connection to the server", self.prompt ], timeout=timeout )
1178 if i == 3:
1179 main.log.debug( self.name + ": " + self.handle.before )
1180 return main.TRUE
1181 else:
1182 main.log.error( self.name + ": Error executing command" )
1183 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1184 return main.FALSE
1185 except pexpect.EOF:
1186 main.log.error( self.name + ": EOF exception found" )
1187 main.log.error( self.name + ": " + self.handle.before )
1188 return main.FALSE
1189 except pexpect.TIMEOUT:
1190 main.log.exception( self.name + ": TIMEOUT exception found" )
1191 main.log.error( self.name + ": " + self.handle.before )
1192 return main.FALSE
1193 except Exception:
1194 main.log.exception( self.name + ": Uncaught exception!" )
1195 return main.FALSE
1196
Jon Hall06fd0df2021-01-25 15:50:06 -08001197 def kubectlPortForward( self, podName, portsList, kubeconfig=None, namespace=None, ):
1198 """
1199 Use kubectl to setup port forwarding from the local machine to the kubernetes pod
1200
1201 Note: This command does not return until the port forwarding session is ended.
1202
1203 Required Arguments:
1204 - podName: The name of the pod as a string
1205 - portsList: The list of ports to forward, as a string. see kubectl help for details
1206 Optional Arguments:
1207 - kubeconfig: The path to a kubeconfig file
1208 - namespace: The namespace to search in
1209 - app: Get pods belonging to a specific app
1210 Returns a list containing the names of the pods or
1211 main.FALSE on Error
1212
1213
1214 """
1215 try:
1216 cmdStr = "kubectl %s %s port-forward pod/%s %s" % (
1217 "--kubeconfig %s" % kubeconfig if kubeconfig else "",
1218 "-n %s" % namespace if namespace else "",
1219 podName,
1220 portsList )
1221 main.log.info( self.name + ": sending: " + repr( cmdStr ) )
1222 self.handle.sendline( cmdStr )
1223 i = self.handle.expect( [ "not found", "error", "closed/timedout",
1224 self.prompt, "The connection to the server", "Forwarding from" ] )
1225 # NOTE: This won't clear the buffer entirely, and each time the port forward
1226 # is used, another line will be added to the buffer. We need to make
1227 # sure we clear the buffer before using this component again.
1228
1229 if i == 5:
1230 # Setup preDisconnect function
1231 self.preDisconnect = self.exitFromProcess
1232 return main.TRUE
1233 else:
1234 main.log.error( self.name + ": Error executing command" )
1235 main.log.debug( self.name + ": " + self.handle.before + str( self.handle.after ) )
1236 return main.FALSE
1237 except pexpect.EOF:
1238 main.log.error( self.name + ": EOF exception found" )
1239 main.log.error( self.name + ": " + self.handle.before )
1240 return main.FALSE
1241 except pexpect.TIMEOUT:
1242 main.log.exception( self.name + ": TIMEOUT exception found" )
1243 main.log.error( self.name + ": " + self.handle.before )
1244 return main.FALSE
1245 except Exception:
1246 main.log.exception( self.name + ": Uncaught exception!" )
1247 return main.FALSE