blob: f9b69bd19340e1120b138c0ca76dcf7e9490e29b [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 ):
Devin Limdc78e202017-06-09 18:30:07 -070035 super( CLI, self ).__init__()
36 def checkPrompt(self):
37 for key in self.options:
38 if key == "prompt" and self.options['prompt'] is not None:
39 self.prompt = self.options['prompt']
40 break
kelvin8ec71442015-01-15 16:57:00 -080041
42 def connect( self, **connectargs ):
43 """
adminbae64d82013-08-01 10:50:15 -070044 Connection will establish to the remote host using ssh.
45 It will take user_name ,ip_address and password as arguments<br>
kelvin8ec71442015-01-15 16:57:00 -080046 and will return the handle.
47 """
adminbae64d82013-08-01 10:50:15 -070048 for key in connectargs:
kelvin8ec71442015-01-15 16:57:00 -080049 vars( self )[ key ] = connectargs[ key ]
Devin Limdc78e202017-06-09 18:30:07 -070050 self.checkPrompt()
adminbae64d82013-08-01 10:50:15 -070051
kelvin8ec71442015-01-15 16:57:00 -080052 connect_result = super( CLI, self ).connect()
adminbae64d82013-08-01 10:50:15 -070053 ssh_newkey = 'Are you sure you want to continue connecting'
kelvin8ec71442015-01-15 16:57:00 -080054 refused = "ssh: connect to host " + \
55 self.ip_address + " port 22: Connection refused"
adminbae64d82013-08-01 10:50:15 -070056 if self.port:
kelvin8ec71442015-01-15 16:57:00 -080057 self.handle = pexpect.spawn(
58 'ssh -p ' +
59 self.port +
60 ' ' +
61 self.user_name +
62 '@' +
63 self.ip_address,
Jon Hall9aaba882015-01-19 15:05:15 -080064 env={ "TERM": "xterm-mono" },
kelvin8ec71442015-01-15 16:57:00 -080065 maxread=50000 )
66 else:
67 self.handle = pexpect.spawn(
68 'ssh -X ' +
69 self.user_name +
70 '@' +
71 self.ip_address,
Jon Hall9aaba882015-01-19 15:05:15 -080072 env={ "TERM": "xterm-mono" },
kelvin8ec71442015-01-15 16:57:00 -080073 maxread=1000000,
74 timeout=60 )
adminbae64d82013-08-01 10:50:15 -070075
Jon Hall73057ee2016-08-23 09:57:26 -070076 # set tty window size
77 self.handle.setwinsize( 24, 250 )
78
adminbae64d82013-08-01 10:50:15 -070079 self.handle.logfile = self.logfile_handler
kelvin8ec71442015-01-15 16:57:00 -080080 i = 5
81 while i == 5:
82 i = self.handle.expect( [
acsmarse2be32c2015-06-29 16:16:28 -070083 ssh_newkey,
Jon Hall05f88682015-06-09 14:57:53 -070084 'password:|Password:',
kelvin8ec71442015-01-15 16:57:00 -080085 pexpect.EOF,
86 pexpect.TIMEOUT,
87 refused,
88 'teston>',
Devin Limdc78e202017-06-09 18:30:07 -070089 self.prompt ],
acsmars07f9d392015-07-15 10:30:58 -070090 120 )
acsmars32de0bc2015-06-30 09:57:12 -070091 if i == 0: # Accept key, then expect either a password prompt or access
kelvin8ec71442015-01-15 16:57:00 -080092 main.log.info( "ssh key confirmation received, send yes" )
93 self.handle.sendline( 'yes' )
acsmars32de0bc2015-06-30 09:57:12 -070094 i = 5 # Run the loop again
acsmars07f9d392015-07-15 10:30:58 -070095 continue
96 if i == 1: # Password required
Jon Hall63604932015-02-26 17:09:50 -080097 if self.pwd:
98 main.log.info(
acsmars07f9d392015-07-15 10:30:58 -070099 "ssh connection asked for password, gave password" )
Jon Hall63604932015-02-26 17:09:50 -0800100 else:
acsmars07f9d392015-07-15 10:30:58 -0700101 main.log.info( "Server asked for password, but none was "
102 "given in the .topo file. Trying "
103 "no password.")
104 self.pwd = ""
105 self.handle.sendline( self.pwd )
106 j = self.handle.expect( [
Devin Limdc78e202017-06-09 18:30:07 -0700107 self.prompt,
acsmars07f9d392015-07-15 10:30:58 -0700108 'password:|Password:',
109 pexpect.EOF,
110 pexpect.TIMEOUT ],
111 120 )
112 if j != 0:
113 main.log.error( "Incorrect Password" )
114 return main.FALSE
kelvin8ec71442015-01-15 16:57:00 -0800115 elif i == 2:
116 main.log.error( "Connection timeout" )
117 return main.FALSE
118 elif i == 3: # timeout
119 main.log.error(
120 "No route to the Host " +
121 self.user_name +
122 "@" +
123 self.ip_address )
124 return main.FALSE
125 elif i == 4:
126 main.log.error(
127 "ssh: connect to host " +
128 self.ip_address +
129 " port 22: Connection refused" )
130 return main.FALSE
131 elif i == 6:
132 main.log.info( "Password not required logged in" )
adminbae64d82013-08-01 10:50:15 -0700133
kelvin8ec71442015-01-15 16:57:00 -0800134 self.handle.sendline( "" )
Devin Limdc78e202017-06-09 18:30:07 -0700135 self.handle.expect( self.prompt )
adminbae64d82013-08-01 10:50:15 -0700136 return self.handle
137
kelvin8ec71442015-01-15 16:57:00 -0800138 def disconnect( self ):
139 result = super( CLI, self ).disconnect( self )
adminbae64d82013-08-01 10:50:15 -0700140 result = main.TRUE
kelvin8ec71442015-01-15 16:57:00 -0800141 # self.execute( cmd="exit",timeout=120,prompt="(.*)" )
142
143 def execute( self, **execparams ):
144 """
adminbae64d82013-08-01 10:50:15 -0700145 It facilitates the command line execution of a given command. It has arguments as :
146 cmd => represents command to be executed,
147 prompt => represents expect command prompt or output,
148 timeout => timeout for command execution,
149 more => to provide a key press if it is on.
150
151 It will return output of command exection.
kelvin8ec71442015-01-15 16:57:00 -0800152 """
153 result = super( CLI, self ).execute( self )
adminaef00552014-05-08 09:18:36 -0700154 defaultPrompt = '.*[$>\#]'
Jon Hall3b489db2015-10-05 14:38:37 -0700155 args = utilities.parse_args( [ "CMD",
156 "TIMEOUT",
157 "PROMPT",
158 "MORE" ],
159 **execparams )
kelvin8ec71442015-01-15 16:57:00 -0800160
161 expectPrompt = args[ "PROMPT" ] if args[ "PROMPT" ] else defaultPrompt
adminbae64d82013-08-01 10:50:15 -0700162 self.LASTRSP = ""
kelvin8ec71442015-01-15 16:57:00 -0800163 timeoutVar = args[ "TIMEOUT" ] if args[ "TIMEOUT" ] else 10
adminbae64d82013-08-01 10:50:15 -0700164 cmd = ''
kelvin8ec71442015-01-15 16:57:00 -0800165 if args[ "CMD" ]:
166 cmd = args[ "CMD" ]
167 else:
adminbae64d82013-08-01 10:50:15 -0700168 return 0
kelvin8ec71442015-01-15 16:57:00 -0800169 if args[ "MORE" ] is None:
170 args[ "MORE" ] = " "
171 self.handle.sendline( cmd )
adminbae64d82013-08-01 10:50:15 -0700172 self.lastCommand = cmd
Jon Hall3b489db2015-10-05 14:38:37 -0700173 index = self.handle.expect( [ expectPrompt,
174 "--More--",
175 'Command not found.',
176 pexpect.TIMEOUT,
177 "^:$" ],
178 timeout=timeoutVar )
adminbae64d82013-08-01 10:50:15 -0700179 if index == 0:
kelvin8ec71442015-01-15 16:57:00 -0800180 self.LASTRSP = self.LASTRSP + \
181 self.handle.before + self.handle.after
Jon Hall3b489db2015-10-05 14:38:37 -0700182 main.log.info( "Executed :" + str(cmd ) +
183 " \t\t Expected Prompt '" + str( expectPrompt) +
184 "' Found" )
adminbae64d82013-08-01 10:50:15 -0700185 elif index == 1:
186 self.LASTRSP = self.LASTRSP + self.handle.before
kelvin8ec71442015-01-15 16:57:00 -0800187 self.handle.send( args[ "MORE" ] )
188 main.log.info(
189 "Found More screen to go , Sending a key to proceed" )
190 indexMore = self.handle.expect(
191 [ "--More--", expectPrompt ], timeout=timeoutVar )
adminbae64d82013-08-01 10:50:15 -0700192 while indexMore == 0:
kelvin8ec71442015-01-15 16:57:00 -0800193 main.log.info(
194 "Found anoother More screen to go , Sending a key to proceed" )
195 self.handle.send( args[ "MORE" ] )
196 indexMore = self.handle.expect(
197 [ "--More--", expectPrompt ], timeout=timeoutVar )
adminbae64d82013-08-01 10:50:15 -0700198 self.LASTRSP = self.LASTRSP + self.handle.before
kelvin8ec71442015-01-15 16:57:00 -0800199 elif index == 2:
200 main.log.error( "Command not found" )
adminbae64d82013-08-01 10:50:15 -0700201 self.LASTRSP = self.LASTRSP + self.handle.before
kelvin8ec71442015-01-15 16:57:00 -0800202 elif index == 3:
Jon Hall3b489db2015-10-05 14:38:37 -0700203 main.log.error( "Expected Prompt not found, Time Out!!" )
kelvin8ec71442015-01-15 16:57:00 -0800204 main.log.error( expectPrompt )
Jon Hall3b489db2015-10-05 14:38:37 -0700205 self.LASTRSP = self.LASTRSP + self.handle.before
206 return self.LASTRSP
adminbae64d82013-08-01 10:50:15 -0700207 elif index == 4:
208 self.LASTRSP = self.LASTRSP + self.handle.before
kelvin8ec71442015-01-15 16:57:00 -0800209 # self.handle.send( args[ "MORE" ] )
210 self.handle.sendcontrol( "D" )
211 main.log.info(
Jon Hall3b489db2015-10-05 14:38:37 -0700212 "Found More screen to go, Sending a key to proceed" )
kelvin8ec71442015-01-15 16:57:00 -0800213 indexMore = self.handle.expect(
214 [ "^:$", expectPrompt ], timeout=timeoutVar )
adminbae64d82013-08-01 10:50:15 -0700215 while indexMore == 0:
kelvin8ec71442015-01-15 16:57:00 -0800216 main.log.info(
Jon Hall3b489db2015-10-05 14:38:37 -0700217 "Found another More screen to go, Sending a key to proceed" )
kelvin8ec71442015-01-15 16:57:00 -0800218 self.handle.sendcontrol( "D" )
219 indexMore = self.handle.expect(
220 [ "^:$", expectPrompt ], timeout=timeoutVar )
adminbae64d82013-08-01 10:50:15 -0700221 self.LASTRSP = self.LASTRSP + self.handle.before
kelvin8ec71442015-01-15 16:57:00 -0800222 main.last_response = self.remove_contol_chars( self.LASTRSP )
adminbae64d82013-08-01 10:50:15 -0700223 return self.LASTRSP
kelvin8ec71442015-01-15 16:57:00 -0800224
225 def remove_contol_chars( self, response ):
226 # 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 ) )
227 # response = re.sub( RE_XML_ILLEGAL, "\n", response )
228 response = re.sub( r"[\x01-\x1F\x7F]", "", response )
229 # response = re.sub( r"\[\d+\;1H", "\n", response )
230 response = re.sub( r"\[\d+\;\d+H", "", response )
adminbae64d82013-08-01 10:50:15 -0700231 return response
adminbae64d82013-08-01 10:50:15 -0700232
kelvin8ec71442015-01-15 16:57:00 -0800233 def runAsSudoUser( self, handle, pwd, default ):
234
235 i = handle.expect( [ ".ssword:*", default, pexpect.EOF ] )
236 if i == 0:
237 handle.sendline( pwd )
Jon Hall5ec6b1b2015-09-17 18:20:14 -0700238 handle.sendline( "\n" )
kelvin8ec71442015-01-15 16:57:00 -0800239
240 if i == 1:
241 handle.expect( default )
242
243 if i == 2:
244 main.log.error( "Unable to run as Sudo user" )
245
adminbae64d82013-08-01 10:50:15 -0700246 return handle
adminbae64d82013-08-01 10:50:15 -0700247
kelvin8ec71442015-01-15 16:57:00 -0800248 def onfail( self ):
249 if 'onfail' in main.componentDictionary[ self.name ]:
250 commandList = main.componentDictionary[
251 self.name ][ 'onfail' ].split( "," )
252 for command in commandList:
253 response = self.execute(
254 cmd=command,
255 prompt="(.*)",
256 timeout=120 )
adminbae64d82013-08-01 10:50:15 -0700257
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700258 def secureCopy( self, userName, ipAddress, filePath, dstPath, pwd="",
259 direction="from" ):
kelvin8ec71442015-01-15 16:57:00 -0800260 """
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700261 Definition:
262 Execute scp command in linux to copy to/from a remote host
263 Required:
264 str userName - User name of the remote host
265 str ipAddress - IP address of the remote host
266 str filePath - File path including the file it self
267 str dstPath - Destination path
268 Optional:
269 str pwd - Password of the host
270 str direction - Direction of the scp, default to "from" which means
271 copy "from" the remote machine to local machine,
272 while "to" means copy "to" the remote machine from
273 local machine
kelvin8ec71442015-01-15 16:57:00 -0800274 """
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700275 returnVal = main.TRUE
adminbae64d82013-08-01 10:50:15 -0700276 ssh_newkey = 'Are you sure you want to continue connecting'
kelvin8ec71442015-01-15 16:57:00 -0800277 refused = "ssh: connect to host " + \
Jon Hall547e0582015-09-21 17:35:40 -0700278 ipAddress + " port 22: Connection refused"
acsmars07f9d392015-07-15 10:30:58 -0700279
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700280 if direction == "from":
281 cmd = 'scp ' + str( userName ) + '@' + str( ipAddress ) + ':' + \
Jon Hall547e0582015-09-21 17:35:40 -0700282 str( filePath ) + ' ' + str( dstPath )
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700283 elif direction == "to":
284 cmd = 'scp ' + str( filePath ) + ' ' + str( userName ) + \
Jon Hall547e0582015-09-21 17:35:40 -0700285 '@' + str( ipAddress ) + ':' + str( dstPath )
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700286 else:
287 main.log.debug( "Wrong direction using secure copy command!" )
288 return main.FALSE
kelvin8ec71442015-01-15 16:57:00 -0800289
290 main.log.info( "Sending: " + cmd )
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700291 self.handle.sendline( cmd )
Jon Hall547e0582015-09-21 17:35:40 -0700292 i = 0
293 while i < 2:
294 i = self.handle.expect( [
295 ssh_newkey,
296 'password:',
297 "100%",
298 refused,
299 "No such file or directory",
Jon Hall53c5e662016-04-13 16:06:56 -0700300 "Permission denied",
Devin Limdc78e202017-06-09 18:30:07 -0700301 self.prompt,
Jon Hall547e0582015-09-21 17:35:40 -0700302 pexpect.EOF,
303 pexpect.TIMEOUT ],
304 120 )
Jon Hall547e0582015-09-21 17:35:40 -0700305 if i == 0: # ask for ssh key confirmation
306 main.log.info( "ssh key confirmation received, sending yes" )
307 self.handle.sendline( 'yes' )
308 elif i == 1: # Asked for ssh password
309 main.log.info( "ssh connection asked for password, gave password" )
310 self.handle.sendline( pwd )
311 elif i == 2: # File finished transfering
312 main.log.info( "Secure copy successful" )
313 returnVal = main.TRUE
314 elif i == 3: # Connection refused
315 main.log.error(
316 "ssh: connect to host " +
317 ipAddress +
318 " port 22: Connection refused" )
319 returnVal = main.FALSE
320 elif i == 4: # File Not found
321 main.log.error( "No such file found" )
322 returnVal = main.FALSE
Jon Hall53c5e662016-04-13 16:06:56 -0700323 elif i == 5: # Permission denied
324 main.log.error( "Permission denied. Check folder permissions" )
325 returnVal = main.FALSE
326 elif i == 6: # prompt returned
327 return returnVal
328 elif i == 7: # EOF
Jon Hall547e0582015-09-21 17:35:40 -0700329 main.log.error( "Pexpect.EOF found!!!" )
330 main.cleanup()
331 main.exit()
Jon Hall53c5e662016-04-13 16:06:56 -0700332 elif i == 8: # timeout
Jon Hall547e0582015-09-21 17:35:40 -0700333 main.log.error(
334 "No route to the Host " +
335 userName +
336 "@" +
337 ipAddress )
338 returnVal = main.FALSE
Devin Limdc78e202017-06-09 18:30:07 -0700339 self.handle.expect( self.prompt )
kelvin-onlabd9e23de2015-08-06 10:34:44 -0700340 return returnVal
341
342 def scp( self, remoteHost, filePath, dstPath, direction="from" ):
343 """
344 Definition:
345 Execute scp command in linux to copy to/from a remote host
346 Required:
347 * remoteHost - Test ON component to be parsed
348 str filePath - File path including the file it self
349 str dstPath - Destination path
350 Optional:
351 str direction - Direction of the scp, default to "from" which means
352 copy "from" the remote machine to local machine,
353 while "to" means copy "to" the remote machine from
354 local machine
355 """
356 return self.secureCopy( remoteHost.user_name,
357 remoteHost.ip_address,
358 filePath,
359 dstPath,
360 pwd=remoteHost.pwd,
361 direction=direction )