blob: c9a19bba0dd85223bc0552b2ffb05a94230e0e5d [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 Songsterae01bba2016-07-11 15:39:17 -07004Modified 2016 by ON.Lab
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
kelvin8ec71442015-01-15 16:57:00 -080013 ( 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
adminbae64d82013-08-01 10:50:15 -070025
26from drivers.component import Component
kelvin8ec71442015-01-15 16:57:00 -080027
28
29class CLI( Component ):
30
31 """
adminbae64d82013-08-01 10:50:15 -070032 This will define common functions for CLI included.
kelvin8ec71442015-01-15 16:57:00 -080033 """
34 def __init__( self ):
35 super( Component, self ).__init__()
36
37 def connect( self, **connectargs ):
38 """
adminbae64d82013-08-01 10:50:15 -070039 Connection will establish to the remote host using ssh.
40 It will take user_name ,ip_address and password as arguments<br>
kelvin8ec71442015-01-15 16:57:00 -080041 and will return the handle.
42 """
adminbae64d82013-08-01 10:50:15 -070043 for key in connectargs:
kelvin8ec71442015-01-15 16:57:00 -080044 vars( self )[ key ] = connectargs[ key ]
adminbae64d82013-08-01 10:50:15 -070045
kelvin8ec71442015-01-15 16:57:00 -080046 connect_result = super( CLI, self ).connect()
adminbae64d82013-08-01 10:50:15 -070047 ssh_newkey = 'Are you sure you want to continue connecting'
kelvin8ec71442015-01-15 16:57:00 -080048 refused = "ssh: connect to host " + \
49 self.ip_address + " port 22: Connection refused"
adminbae64d82013-08-01 10:50:15 -070050 if self.port:
kelvin8ec71442015-01-15 16:57:00 -080051 self.handle = pexpect.spawn(
52 'ssh -p ' +
53 self.port +
54 ' ' +
55 self.user_name +
56 '@' +
57 self.ip_address,
Jon Hall9aaba882015-01-19 15:05:15 -080058 env={ "TERM": "xterm-mono" },
kelvin8ec71442015-01-15 16:57:00 -080059 maxread=50000 )
60 else:
61 self.handle = pexpect.spawn(
62 'ssh -X ' +
63 self.user_name +
64 '@' +
65 self.ip_address,
Jon Hall9aaba882015-01-19 15:05:15 -080066 env={ "TERM": "xterm-mono" },
kelvin8ec71442015-01-15 16:57:00 -080067 maxread=1000000,
68 timeout=60 )
adminbae64d82013-08-01 10:50:15 -070069
Jon Hall73057ee2016-08-23 09:57:26 -070070 # set tty window size
71 self.handle.setwinsize( 24, 250 )
72
adminbae64d82013-08-01 10:50:15 -070073 self.handle.logfile = self.logfile_handler
kelvin8ec71442015-01-15 16:57:00 -080074 i = 5
75 while i == 5:
76 i = self.handle.expect( [
acsmarse2be32c2015-06-29 16:16:28 -070077 ssh_newkey,
Jon Hall05f88682015-06-09 14:57:53 -070078 'password:|Password:',
kelvin8ec71442015-01-15 16:57:00 -080079 pexpect.EOF,
80 pexpect.TIMEOUT,
81 refused,
82 'teston>',
83 '>|#|\$' ],
acsmars07f9d392015-07-15 10:30:58 -070084 120 )
acsmars32de0bc2015-06-30 09:57:12 -070085 if i == 0: # Accept key, then expect either a password prompt or access
kelvin8ec71442015-01-15 16:57:00 -080086 main.log.info( "ssh key confirmation received, send yes" )
87 self.handle.sendline( 'yes' )
acsmars32de0bc2015-06-30 09:57:12 -070088 i = 5 # Run the loop again
acsmars07f9d392015-07-15 10:30:58 -070089 continue
90 if i == 1: # Password required
Jon Hall63604932015-02-26 17:09:50 -080091 if self.pwd:
92 main.log.info(
acsmars07f9d392015-07-15 10:30:58 -070093 "ssh connection asked for password, gave password" )
Jon Hall63604932015-02-26 17:09:50 -080094 else:
acsmars07f9d392015-07-15 10:30:58 -070095 main.log.info( "Server asked for password, but none was "
96 "given in the .topo file. Trying "
97 "no password.")
98 self.pwd = ""
99 self.handle.sendline( self.pwd )
100 j = self.handle.expect( [
101 '>|#|\$',
102 'password:|Password:',
103 pexpect.EOF,
104 pexpect.TIMEOUT ],
105 120 )
106 if j != 0:
107 main.log.error( "Incorrect Password" )
108 return main.FALSE
kelvin8ec71442015-01-15 16:57:00 -0800109 elif i == 2:
110 main.log.error( "Connection timeout" )
111 return main.FALSE
112 elif i == 3: # timeout
113 main.log.error(
114 "No route to the Host " +
115 self.user_name +
116 "@" +
117 self.ip_address )
118 return main.FALSE
119 elif i == 4:
120 main.log.error(
121 "ssh: connect to host " +
122 self.ip_address +
123 " port 22: Connection refused" )
124 return main.FALSE
125 elif i == 6:
126 main.log.info( "Password not required logged in" )
adminbae64d82013-08-01 10:50:15 -0700127
kelvin8ec71442015-01-15 16:57:00 -0800128 self.handle.sendline( "" )
129 self.handle.expect( '>|#|\$' )
adminbae64d82013-08-01 10:50:15 -0700130 return self.handle
131
kelvin8ec71442015-01-15 16:57:00 -0800132 def disconnect( self ):
133 result = super( CLI, self ).disconnect( self )
adminbae64d82013-08-01 10:50:15 -0700134 result = main.TRUE
kelvin8ec71442015-01-15 16:57:00 -0800135 # self.execute( cmd="exit",timeout=120,prompt="(.*)" )
136
137 def execute( self, **execparams ):
138 """
adminbae64d82013-08-01 10:50:15 -0700139 It facilitates the command line execution of a given command. It has arguments as :
140 cmd => represents command to be executed,
141 prompt => represents expect command prompt or output,
142 timeout => timeout for command execution,
143 more => to provide a key press if it is on.
144
145 It will return output of command exection.
kelvin8ec71442015-01-15 16:57:00 -0800146 """
147 result = super( CLI, self ).execute( self )
adminaef00552014-05-08 09:18:36 -0700148 defaultPrompt = '.*[$>\#]'
Jon Hall3b489db2015-10-05 14:38:37 -0700149 args = utilities.parse_args( [ "CMD",
150 "TIMEOUT",
151 "PROMPT",
152 "MORE" ],
153 **execparams )
kelvin8ec71442015-01-15 16:57:00 -0800154
155 expectPrompt = args[ "PROMPT" ] if args[ "PROMPT" ] else defaultPrompt
adminbae64d82013-08-01 10:50:15 -0700156 self.LASTRSP = ""
kelvin8ec71442015-01-15 16:57:00 -0800157 timeoutVar = args[ "TIMEOUT" ] if args[ "TIMEOUT" ] else 10
adminbae64d82013-08-01 10:50:15 -0700158 cmd = ''
kelvin8ec71442015-01-15 16:57:00 -0800159 if args[ "CMD" ]:
160 cmd = args[ "CMD" ]
161 else:
adminbae64d82013-08-01 10:50:15 -0700162 return 0
kelvin8ec71442015-01-15 16:57:00 -0800163 if args[ "MORE" ] is None:
164 args[ "MORE" ] = " "
165 self.handle.sendline( cmd )
adminbae64d82013-08-01 10:50:15 -0700166 self.lastCommand = cmd
Jon Hall3b489db2015-10-05 14:38:37 -0700167 index = self.handle.expect( [ expectPrompt,
168 "--More--",
169 'Command not found.',
170 pexpect.TIMEOUT,
171 "^:$" ],
172 timeout=timeoutVar )
adminbae64d82013-08-01 10:50:15 -0700173 if index == 0:
kelvin8ec71442015-01-15 16:57:00 -0800174 self.LASTRSP = self.LASTRSP + \
175 self.handle.before + self.handle.after
Jon Hall3b489db2015-10-05 14:38:37 -0700176 main.log.info( "Executed :" + str(cmd ) +
177 " \t\t Expected Prompt '" + str( expectPrompt) +
178 "' Found" )
adminbae64d82013-08-01 10:50:15 -0700179 elif index == 1:
180 self.LASTRSP = self.LASTRSP + self.handle.before
kelvin8ec71442015-01-15 16:57:00 -0800181 self.handle.send( args[ "MORE" ] )
182 main.log.info(
183 "Found More screen to go , Sending a key to proceed" )
184 indexMore = self.handle.expect(
185 [ "--More--", expectPrompt ], timeout=timeoutVar )
adminbae64d82013-08-01 10:50:15 -0700186 while indexMore == 0:
kelvin8ec71442015-01-15 16:57:00 -0800187 main.log.info(
188 "Found anoother More screen to go , Sending a key to proceed" )
189 self.handle.send( args[ "MORE" ] )
190 indexMore = self.handle.expect(
191 [ "--More--", expectPrompt ], timeout=timeoutVar )
adminbae64d82013-08-01 10:50:15 -0700192 self.LASTRSP = self.LASTRSP + self.handle.before
kelvin8ec71442015-01-15 16:57:00 -0800193 elif index == 2:
194 main.log.error( "Command not found" )
adminbae64d82013-08-01 10:50:15 -0700195 self.LASTRSP = self.LASTRSP + self.handle.before
kelvin8ec71442015-01-15 16:57:00 -0800196 elif index == 3:
Jon Hall3b489db2015-10-05 14:38:37 -0700197 main.log.error( "Expected Prompt not found, Time Out!!" )
kelvin8ec71442015-01-15 16:57:00 -0800198 main.log.error( expectPrompt )
Jon Hall3b489db2015-10-05 14:38:37 -0700199 self.LASTRSP = self.LASTRSP + self.handle.before
200 return self.LASTRSP
adminbae64d82013-08-01 10:50:15 -0700201 elif index == 4:
202 self.LASTRSP = self.LASTRSP + self.handle.before
kelvin8ec71442015-01-15 16:57:00 -0800203 # self.handle.send( args[ "MORE" ] )
204 self.handle.sendcontrol( "D" )
205 main.log.info(
Jon Hall3b489db2015-10-05 14:38:37 -0700206 "Found More screen to go, Sending a key to proceed" )
kelvin8ec71442015-01-15 16:57:00 -0800207 indexMore = self.handle.expect(
208 [ "^:$", expectPrompt ], timeout=timeoutVar )
adminbae64d82013-08-01 10:50:15 -0700209 while indexMore == 0:
kelvin8ec71442015-01-15 16:57:00 -0800210 main.log.info(
Jon Hall3b489db2015-10-05 14:38:37 -0700211 "Found another More screen to go, Sending a key to proceed" )
kelvin8ec71442015-01-15 16:57:00 -0800212 self.handle.sendcontrol( "D" )
213 indexMore = self.handle.expect(
214 [ "^:$", expectPrompt ], timeout=timeoutVar )
adminbae64d82013-08-01 10:50:15 -0700215 self.LASTRSP = self.LASTRSP + self.handle.before
kelvin8ec71442015-01-15 16:57:00 -0800216 main.last_response = self.remove_contol_chars( self.LASTRSP )
adminbae64d82013-08-01 10:50:15 -0700217 return self.LASTRSP
kelvin8ec71442015-01-15 16:57:00 -0800218
219 def remove_contol_chars( self, response ):
220 # 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 ) )
221 # response = re.sub( RE_XML_ILLEGAL, "\n", response )
222 response = re.sub( r"[\x01-\x1F\x7F]", "", response )
223 # response = re.sub( r"\[\d+\;1H", "\n", response )
224 response = re.sub( r"\[\d+\;\d+H", "", response )
adminbae64d82013-08-01 10:50:15 -0700225 return response
adminbae64d82013-08-01 10:50:15 -0700226
kelvin8ec71442015-01-15 16:57:00 -0800227 def runAsSudoUser( self, handle, pwd, default ):
228
229 i = handle.expect( [ ".ssword:*", default, pexpect.EOF ] )
230 if i == 0:
231 handle.sendline( pwd )
Jon Hall5ec6b1b2015-09-17 18:20:14 -0700232 handle.sendline( "\n" )
kelvin8ec71442015-01-15 16:57:00 -0800233
234 if i == 1:
235 handle.expect( default )
236
237 if i == 2:
238 main.log.error( "Unable to run as Sudo user" )
239
adminbae64d82013-08-01 10:50:15 -0700240 return handle
adminbae64d82013-08-01 10:50:15 -0700241
kelvin8ec71442015-01-15 16:57:00 -0800242 def onfail( self ):
243 if 'onfail' in main.componentDictionary[ self.name ]:
244 commandList = main.componentDictionary[
245 self.name ][ 'onfail' ].split( "," )
246 for command in commandList:
247 response = self.execute(
248 cmd=command,
249 prompt="(.*)",
250 timeout=120 )
adminbae64d82013-08-01 10:50:15 -0700251
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700252 def secureCopy( self, userName, ipAddress, filePath, dstPath, pwd="",
253 direction="from" ):
kelvin8ec71442015-01-15 16:57:00 -0800254 """
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700255 Definition:
256 Execute scp command in linux to copy to/from a remote host
257 Required:
258 str userName - User name of the remote host
259 str ipAddress - IP address of the remote host
260 str filePath - File path including the file it self
261 str dstPath - Destination path
262 Optional:
263 str pwd - Password of the host
264 str direction - Direction of the scp, default to "from" which means
265 copy "from" the remote machine to local machine,
266 while "to" means copy "to" the remote machine from
267 local machine
kelvin8ec71442015-01-15 16:57:00 -0800268 """
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700269 returnVal = main.TRUE
adminbae64d82013-08-01 10:50:15 -0700270 ssh_newkey = 'Are you sure you want to continue connecting'
kelvin8ec71442015-01-15 16:57:00 -0800271 refused = "ssh: connect to host " + \
Jon Hall547e0582015-09-21 17:35:40 -0700272 ipAddress + " port 22: Connection refused"
acsmars07f9d392015-07-15 10:30:58 -0700273
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700274 if direction == "from":
275 cmd = 'scp ' + str( userName ) + '@' + str( ipAddress ) + ':' + \
Jon Hall547e0582015-09-21 17:35:40 -0700276 str( filePath ) + ' ' + str( dstPath )
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700277 elif direction == "to":
278 cmd = 'scp ' + str( filePath ) + ' ' + str( userName ) + \
Jon Hall547e0582015-09-21 17:35:40 -0700279 '@' + str( ipAddress ) + ':' + str( dstPath )
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700280 else:
281 main.log.debug( "Wrong direction using secure copy command!" )
282 return main.FALSE
kelvin8ec71442015-01-15 16:57:00 -0800283
284 main.log.info( "Sending: " + cmd )
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700285 self.handle.sendline( cmd )
Jon Hall547e0582015-09-21 17:35:40 -0700286 i = 0
287 while i < 2:
288 i = self.handle.expect( [
289 ssh_newkey,
290 'password:',
291 "100%",
292 refused,
293 "No such file or directory",
Jon Hall53c5e662016-04-13 16:06:56 -0700294 "Permission denied",
295 "\$",
Jon Hall547e0582015-09-21 17:35:40 -0700296 pexpect.EOF,
297 pexpect.TIMEOUT ],
298 120 )
Jon Hall547e0582015-09-21 17:35:40 -0700299 if i == 0: # ask for ssh key confirmation
300 main.log.info( "ssh key confirmation received, sending yes" )
301 self.handle.sendline( 'yes' )
302 elif i == 1: # Asked for ssh password
303 main.log.info( "ssh connection asked for password, gave password" )
304 self.handle.sendline( pwd )
305 elif i == 2: # File finished transfering
306 main.log.info( "Secure copy successful" )
307 returnVal = main.TRUE
308 elif i == 3: # Connection refused
309 main.log.error(
310 "ssh: connect to host " +
311 ipAddress +
312 " port 22: Connection refused" )
313 returnVal = main.FALSE
314 elif i == 4: # File Not found
315 main.log.error( "No such file found" )
316 returnVal = main.FALSE
Jon Hall53c5e662016-04-13 16:06:56 -0700317 elif i == 5: # Permission denied
318 main.log.error( "Permission denied. Check folder permissions" )
319 returnVal = main.FALSE
320 elif i == 6: # prompt returned
321 return returnVal
322 elif i == 7: # EOF
Jon Hall547e0582015-09-21 17:35:40 -0700323 main.log.error( "Pexpect.EOF found!!!" )
324 main.cleanup()
325 main.exit()
Jon Hall53c5e662016-04-13 16:06:56 -0700326 elif i == 8: # timeout
Jon Hall547e0582015-09-21 17:35:40 -0700327 main.log.error(
328 "No route to the Host " +
329 userName +
330 "@" +
331 ipAddress )
332 returnVal = main.FALSE
Jon Hall3b489db2015-10-05 14:38:37 -0700333 self.handle.expect( "\$" )
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700334 return returnVal
335
336 def scp( self, remoteHost, filePath, dstPath, direction="from" ):
337 """
338 Definition:
339 Execute scp command in linux to copy to/from a remote host
340 Required:
341 * remoteHost - Test ON component to be parsed
342 str filePath - File path including the file it self
343 str dstPath - Destination path
344 Optional:
345 str direction - Direction of the scp, default to "from" which means
346 copy "from" the remote machine to local machine,
347 while "to" means copy "to" the remote machine from
348 local machine
349 """
350 return self.secureCopy( remoteHost.user_name,
351 remoteHost.ip_address,
352 filePath,
353 dstPath,
354 pwd=remoteHost.pwd,
355 direction=direction )