blob: 283088d8320a0dfcda4c40e57d6c477a69875e3c [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
70 self.handle.logfile = self.logfile_handler
kelvin8ec71442015-01-15 16:57:00 -080071 i = 5
72 while i == 5:
73 i = self.handle.expect( [
acsmarse2be32c2015-06-29 16:16:28 -070074 ssh_newkey,
Jon Hall05f88682015-06-09 14:57:53 -070075 'password:|Password:',
kelvin8ec71442015-01-15 16:57:00 -080076 pexpect.EOF,
77 pexpect.TIMEOUT,
78 refused,
79 'teston>',
80 '>|#|\$' ],
acsmars07f9d392015-07-15 10:30:58 -070081 120 )
acsmars32de0bc2015-06-30 09:57:12 -070082 if i == 0: # Accept key, then expect either a password prompt or access
kelvin8ec71442015-01-15 16:57:00 -080083 main.log.info( "ssh key confirmation received, send yes" )
84 self.handle.sendline( 'yes' )
acsmars32de0bc2015-06-30 09:57:12 -070085 i = 5 # Run the loop again
acsmars07f9d392015-07-15 10:30:58 -070086 continue
87 if i == 1: # Password required
Jon Hall63604932015-02-26 17:09:50 -080088 if self.pwd:
89 main.log.info(
acsmars07f9d392015-07-15 10:30:58 -070090 "ssh connection asked for password, gave password" )
Jon Hall63604932015-02-26 17:09:50 -080091 else:
acsmars07f9d392015-07-15 10:30:58 -070092 main.log.info( "Server asked for password, but none was "
93 "given in the .topo file. Trying "
94 "no password.")
95 self.pwd = ""
96 self.handle.sendline( self.pwd )
97 j = self.handle.expect( [
98 '>|#|\$',
99 'password:|Password:',
100 pexpect.EOF,
101 pexpect.TIMEOUT ],
102 120 )
103 if j != 0:
104 main.log.error( "Incorrect Password" )
105 return main.FALSE
kelvin8ec71442015-01-15 16:57:00 -0800106 elif i == 2:
107 main.log.error( "Connection timeout" )
108 return main.FALSE
109 elif i == 3: # timeout
110 main.log.error(
111 "No route to the Host " +
112 self.user_name +
113 "@" +
114 self.ip_address )
115 return main.FALSE
116 elif i == 4:
117 main.log.error(
118 "ssh: connect to host " +
119 self.ip_address +
120 " port 22: Connection refused" )
121 return main.FALSE
122 elif i == 6:
123 main.log.info( "Password not required logged in" )
adminbae64d82013-08-01 10:50:15 -0700124
kelvin8ec71442015-01-15 16:57:00 -0800125 self.handle.sendline( "" )
126 self.handle.expect( '>|#|\$' )
adminbae64d82013-08-01 10:50:15 -0700127 return self.handle
128
kelvin8ec71442015-01-15 16:57:00 -0800129 def disconnect( self ):
130 result = super( CLI, self ).disconnect( self )
adminbae64d82013-08-01 10:50:15 -0700131 result = main.TRUE
kelvin8ec71442015-01-15 16:57:00 -0800132 # self.execute( cmd="exit",timeout=120,prompt="(.*)" )
133
134 def execute( self, **execparams ):
135 """
adminbae64d82013-08-01 10:50:15 -0700136 It facilitates the command line execution of a given command. It has arguments as :
137 cmd => represents command to be executed,
138 prompt => represents expect command prompt or output,
139 timeout => timeout for command execution,
140 more => to provide a key press if it is on.
141
142 It will return output of command exection.
kelvin8ec71442015-01-15 16:57:00 -0800143 """
144 result = super( CLI, self ).execute( self )
adminaef00552014-05-08 09:18:36 -0700145 defaultPrompt = '.*[$>\#]'
Jon Hall3b489db2015-10-05 14:38:37 -0700146 args = utilities.parse_args( [ "CMD",
147 "TIMEOUT",
148 "PROMPT",
149 "MORE" ],
150 **execparams )
kelvin8ec71442015-01-15 16:57:00 -0800151
152 expectPrompt = args[ "PROMPT" ] if args[ "PROMPT" ] else defaultPrompt
adminbae64d82013-08-01 10:50:15 -0700153 self.LASTRSP = ""
kelvin8ec71442015-01-15 16:57:00 -0800154 timeoutVar = args[ "TIMEOUT" ] if args[ "TIMEOUT" ] else 10
adminbae64d82013-08-01 10:50:15 -0700155 cmd = ''
kelvin8ec71442015-01-15 16:57:00 -0800156 if args[ "CMD" ]:
157 cmd = args[ "CMD" ]
158 else:
adminbae64d82013-08-01 10:50:15 -0700159 return 0
kelvin8ec71442015-01-15 16:57:00 -0800160 if args[ "MORE" ] is None:
161 args[ "MORE" ] = " "
162 self.handle.sendline( cmd )
adminbae64d82013-08-01 10:50:15 -0700163 self.lastCommand = cmd
Jon Hall3b489db2015-10-05 14:38:37 -0700164 index = self.handle.expect( [ expectPrompt,
165 "--More--",
166 'Command not found.',
167 pexpect.TIMEOUT,
168 "^:$" ],
169 timeout=timeoutVar )
adminbae64d82013-08-01 10:50:15 -0700170 if index == 0:
kelvin8ec71442015-01-15 16:57:00 -0800171 self.LASTRSP = self.LASTRSP + \
172 self.handle.before + self.handle.after
Jon Hall3b489db2015-10-05 14:38:37 -0700173 main.log.info( "Executed :" + str(cmd ) +
174 " \t\t Expected Prompt '" + str( expectPrompt) +
175 "' Found" )
adminbae64d82013-08-01 10:50:15 -0700176 elif index == 1:
177 self.LASTRSP = self.LASTRSP + self.handle.before
kelvin8ec71442015-01-15 16:57:00 -0800178 self.handle.send( args[ "MORE" ] )
179 main.log.info(
180 "Found More screen to go , Sending a key to proceed" )
181 indexMore = self.handle.expect(
182 [ "--More--", expectPrompt ], timeout=timeoutVar )
adminbae64d82013-08-01 10:50:15 -0700183 while indexMore == 0:
kelvin8ec71442015-01-15 16:57:00 -0800184 main.log.info(
185 "Found anoother More screen to go , Sending a key to proceed" )
186 self.handle.send( args[ "MORE" ] )
187 indexMore = self.handle.expect(
188 [ "--More--", expectPrompt ], timeout=timeoutVar )
adminbae64d82013-08-01 10:50:15 -0700189 self.LASTRSP = self.LASTRSP + self.handle.before
kelvin8ec71442015-01-15 16:57:00 -0800190 elif index == 2:
191 main.log.error( "Command not found" )
adminbae64d82013-08-01 10:50:15 -0700192 self.LASTRSP = self.LASTRSP + self.handle.before
kelvin8ec71442015-01-15 16:57:00 -0800193 elif index == 3:
Jon Hall3b489db2015-10-05 14:38:37 -0700194 main.log.error( "Expected Prompt not found, Time Out!!" )
kelvin8ec71442015-01-15 16:57:00 -0800195 main.log.error( expectPrompt )
Jon Hall3b489db2015-10-05 14:38:37 -0700196 self.LASTRSP = self.LASTRSP + self.handle.before
197 return self.LASTRSP
adminbae64d82013-08-01 10:50:15 -0700198 elif index == 4:
199 self.LASTRSP = self.LASTRSP + self.handle.before
kelvin8ec71442015-01-15 16:57:00 -0800200 # self.handle.send( args[ "MORE" ] )
201 self.handle.sendcontrol( "D" )
202 main.log.info(
Jon Hall3b489db2015-10-05 14:38:37 -0700203 "Found More screen to go, Sending a key to proceed" )
kelvin8ec71442015-01-15 16:57:00 -0800204 indexMore = self.handle.expect(
205 [ "^:$", expectPrompt ], timeout=timeoutVar )
adminbae64d82013-08-01 10:50:15 -0700206 while indexMore == 0:
kelvin8ec71442015-01-15 16:57:00 -0800207 main.log.info(
Jon Hall3b489db2015-10-05 14:38:37 -0700208 "Found another More screen to go, Sending a key to proceed" )
kelvin8ec71442015-01-15 16:57:00 -0800209 self.handle.sendcontrol( "D" )
210 indexMore = self.handle.expect(
211 [ "^:$", expectPrompt ], timeout=timeoutVar )
adminbae64d82013-08-01 10:50:15 -0700212 self.LASTRSP = self.LASTRSP + self.handle.before
kelvin8ec71442015-01-15 16:57:00 -0800213 main.last_response = self.remove_contol_chars( self.LASTRSP )
adminbae64d82013-08-01 10:50:15 -0700214 return self.LASTRSP
kelvin8ec71442015-01-15 16:57:00 -0800215
216 def remove_contol_chars( self, response ):
217 # 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 ) )
218 # response = re.sub( RE_XML_ILLEGAL, "\n", response )
219 response = re.sub( r"[\x01-\x1F\x7F]", "", response )
220 # response = re.sub( r"\[\d+\;1H", "\n", response )
221 response = re.sub( r"\[\d+\;\d+H", "", response )
adminbae64d82013-08-01 10:50:15 -0700222 return response
adminbae64d82013-08-01 10:50:15 -0700223
kelvin8ec71442015-01-15 16:57:00 -0800224 def runAsSudoUser( self, handle, pwd, default ):
225
226 i = handle.expect( [ ".ssword:*", default, pexpect.EOF ] )
227 if i == 0:
228 handle.sendline( pwd )
Jon Hall5ec6b1b2015-09-17 18:20:14 -0700229 handle.sendline( "\n" )
kelvin8ec71442015-01-15 16:57:00 -0800230
231 if i == 1:
232 handle.expect( default )
233
234 if i == 2:
235 main.log.error( "Unable to run as Sudo user" )
236
adminbae64d82013-08-01 10:50:15 -0700237 return handle
adminbae64d82013-08-01 10:50:15 -0700238
kelvin8ec71442015-01-15 16:57:00 -0800239 def onfail( self ):
240 if 'onfail' in main.componentDictionary[ self.name ]:
241 commandList = main.componentDictionary[
242 self.name ][ 'onfail' ].split( "," )
243 for command in commandList:
244 response = self.execute(
245 cmd=command,
246 prompt="(.*)",
247 timeout=120 )
adminbae64d82013-08-01 10:50:15 -0700248
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700249 def secureCopy( self, userName, ipAddress, filePath, dstPath, pwd="",
250 direction="from" ):
kelvin8ec71442015-01-15 16:57:00 -0800251 """
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700252 Definition:
253 Execute scp command in linux to copy to/from a remote host
254 Required:
255 str userName - User name of the remote host
256 str ipAddress - IP address of the remote host
257 str filePath - File path including the file it self
258 str dstPath - Destination path
259 Optional:
260 str pwd - Password of the host
261 str direction - Direction of the scp, default to "from" which means
262 copy "from" the remote machine to local machine,
263 while "to" means copy "to" the remote machine from
264 local machine
kelvin8ec71442015-01-15 16:57:00 -0800265 """
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700266 returnVal = main.TRUE
adminbae64d82013-08-01 10:50:15 -0700267 ssh_newkey = 'Are you sure you want to continue connecting'
kelvin8ec71442015-01-15 16:57:00 -0800268 refused = "ssh: connect to host " + \
Jon Hall547e0582015-09-21 17:35:40 -0700269 ipAddress + " port 22: Connection refused"
acsmars07f9d392015-07-15 10:30:58 -0700270
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700271 if direction == "from":
272 cmd = 'scp ' + str( userName ) + '@' + str( ipAddress ) + ':' + \
Jon Hall547e0582015-09-21 17:35:40 -0700273 str( filePath ) + ' ' + str( dstPath )
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700274 elif direction == "to":
275 cmd = 'scp ' + str( filePath ) + ' ' + str( userName ) + \
Jon Hall547e0582015-09-21 17:35:40 -0700276 '@' + str( ipAddress ) + ':' + str( dstPath )
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700277 else:
278 main.log.debug( "Wrong direction using secure copy command!" )
279 return main.FALSE
kelvin8ec71442015-01-15 16:57:00 -0800280
281 main.log.info( "Sending: " + cmd )
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700282 self.handle.sendline( cmd )
Jon Hall547e0582015-09-21 17:35:40 -0700283 i = 0
284 while i < 2:
285 i = self.handle.expect( [
286 ssh_newkey,
287 'password:',
288 "100%",
289 refused,
290 "No such file or directory",
Jon Hall53c5e662016-04-13 16:06:56 -0700291 "Permission denied",
292 "\$",
Jon Hall547e0582015-09-21 17:35:40 -0700293 pexpect.EOF,
294 pexpect.TIMEOUT ],
295 120 )
Jon Hall547e0582015-09-21 17:35:40 -0700296 if i == 0: # ask for ssh key confirmation
297 main.log.info( "ssh key confirmation received, sending yes" )
298 self.handle.sendline( 'yes' )
299 elif i == 1: # Asked for ssh password
300 main.log.info( "ssh connection asked for password, gave password" )
301 self.handle.sendline( pwd )
302 elif i == 2: # File finished transfering
303 main.log.info( "Secure copy successful" )
304 returnVal = main.TRUE
305 elif i == 3: # Connection refused
306 main.log.error(
307 "ssh: connect to host " +
308 ipAddress +
309 " port 22: Connection refused" )
310 returnVal = main.FALSE
311 elif i == 4: # File Not found
312 main.log.error( "No such file found" )
313 returnVal = main.FALSE
Jon Hall53c5e662016-04-13 16:06:56 -0700314 elif i == 5: # Permission denied
315 main.log.error( "Permission denied. Check folder permissions" )
316 returnVal = main.FALSE
317 elif i == 6: # prompt returned
318 return returnVal
319 elif i == 7: # EOF
Jon Hall547e0582015-09-21 17:35:40 -0700320 main.log.error( "Pexpect.EOF found!!!" )
321 main.cleanup()
322 main.exit()
Jon Hall53c5e662016-04-13 16:06:56 -0700323 elif i == 8: # timeout
Jon Hall547e0582015-09-21 17:35:40 -0700324 main.log.error(
325 "No route to the Host " +
326 userName +
327 "@" +
328 ipAddress )
329 returnVal = main.FALSE
Jon Hall3b489db2015-10-05 14:38:37 -0700330 self.handle.expect( "\$" )
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700331 return returnVal
332
333 def scp( self, remoteHost, filePath, dstPath, direction="from" ):
334 """
335 Definition:
336 Execute scp command in linux to copy to/from a remote host
337 Required:
338 * remoteHost - Test ON component to be parsed
339 str filePath - File path including the file it self
340 str dstPath - Destination path
341 Optional:
342 str direction - Direction of the scp, default to "from" which means
343 copy "from" the remote machine to local machine,
344 while "to" means copy "to" the remote machine from
345 local machine
346 """
347 return self.secureCopy( remoteHost.user_name,
348 remoteHost.ip_address,
349 filePath,
350 dstPath,
351 pwd=remoteHost.pwd,
352 direction=direction )