blob: 18589b95d4be8519eb85f03b89ac40a7e1d79f83 [file] [log] [blame]
Steve McIntyrecc297112014-08-11 18:46:58 +01001#! /usr/bin/python
2
3# Copyright 2014 Linaro Limited
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18# MA 02110-1301, USA.
19
20import logging
21import pexpect
22import sys
23import time
24import re
Steve McIntyre3c3f4fc2015-02-12 04:26:27 +000025from common import SwitchDriver, SwitchErrors
Steve McIntyrecc297112014-08-11 18:46:58 +010026
Steve McIntyre72a8bce2015-01-23 18:02:19 +000027if __name__ == '__main__':
28 vlandpath = os.path.abspath(os.path.normpath(os.path.dirname(sys.argv[0])))
29 sys.path.insert(0, vlandpath)
30 sys.path.insert(0, "%s/.." % vlandpath)
31
Steve McIntyre9f7f03b2015-02-12 03:10:13 +000032from errors import CriticalError, InputError, PExpectError
Steve McIntyre72a8bce2015-01-23 18:02:19 +000033
Steve McIntyrecc297112014-08-11 18:46:58 +010034class CiscoSX300(SwitchDriver):
35
36 connection = None
Steve McIntyrebbd2ac82015-02-12 03:08:48 +000037 _username = None
38 _password = None
39 _enable_password = None
Steve McIntyre366046f2014-08-18 18:57:03 +010040
41 # No extra capabilities for this switch/driver yet
42 _capabilities = [
43 ]
44
Steve McIntyrecc297112014-08-11 18:46:58 +010045 # Regexp of expected hardware information - fail if we don't see
46 # this
Steve McIntyreb9783ff2014-12-23 16:36:35 +000047 _expected_descr_re = re.compile('.*\d+-Port.*Managed Switch.*')
Steve McIntyrecc297112014-08-11 18:46:58 +010048
Steve McIntyre16f02162014-08-14 17:47:36 +010049 logfile = None
Steve McIntyrecc297112014-08-11 18:46:58 +010050
Steve McIntyre48dc6ae2014-12-23 16:08:19 +000051 def __init__(self, switch_hostname, switch_telnetport=23, debug = False):
52 if debug:
Steve McIntyre9f5a0232014-12-23 16:14:28 +000053 self.logfile = sys.stderr
Steve McIntyrecc297112014-08-11 18:46:58 +010054 self.exec_string = "/usr/bin/telnet %s %d" % (switch_hostname, switch_telnetport)
Steve McIntyre3c3f4fc2015-02-12 04:26:27 +000055 self.errors = SwitchErrors()
Steve McIntyrecc297112014-08-11 18:46:58 +010056
57 ################################
58 ### Switch-level API functions
59 ################################
60
61 # Connect to the switch and log in
Steve McIntyrec1e42b72014-12-22 16:13:08 +000062 def switch_connect(self, username, password, enablepassword):
Steve McIntyrebbd2ac82015-02-12 03:08:48 +000063 self._username = username
64 self._password = password
65 self._enable_password = enablepassword
Steve McIntyre9295d502015-02-12 04:46:45 +000066 self._switch_connect()
67
Steve McIntyrecc297112014-08-11 18:46:58 +010068 # Log out of the switch and drop the connection and all state
Steve McIntyre9b09b9d2014-09-24 15:08:10 +010069 def switch_disconnect(self):
Steve McIntyrecc297112014-08-11 18:46:58 +010070 self._logout()
71 logging.debug("Closing connection: %s" % self.connection)
72 self.connection.close(True)
Steve McIntyre11393992014-10-10 15:53:34 +010073 self._ports = []
74 self._prompt_name = ''
75 self._systemdata = []
Steve McIntyrecc297112014-08-11 18:46:58 +010076 del(self)
77
78 # Save the current running config into flash - we want config to
79 # remain across reboots
Steve McIntyre9b09b9d2014-09-24 15:08:10 +010080 def switch_save_running_config(self):
Steve McIntyrec5359e62015-02-12 04:54:07 +000081 try:
82 self._cli("copy running-config startup-config")
83 self.connection.expect("Y/N")
84 self._cli("y")
85 self.connection.expect("succeeded")
Steve McIntyre2d84e522015-02-12 06:44:42 +000086 except (PExpectError, pexpect.EOF, pexpect.TIMEOUT):
Steve McIntyrec5359e62015-02-12 04:54:07 +000087 # recurse on error
88 self._switch_connect()
89 self.switch_save_running_config()
Steve McIntyrecc297112014-08-11 18:46:58 +010090
Steve McIntyre095b4452014-12-19 17:53:43 +000091 # Restart the switch - we need to reload config to do a
92 # roll-back. Do NOT save running-config first if the switch asks -
93 # we're trying to dump recent changes, not save them.
94 #
95 # This will also implicitly cause a connection to be closed
96 def switch_restart(self):
97 self._cli("reload")
98 index = self.connection.expect(['Are you sure', 'will reset'])
99 if index == 0:
100 self._cli("y") # Yes, continue without saving
101 self.connection.expect("reset the whole")
102
103 # Fall through
104 self._cli("y") # Yes, continue to reset
105 self.connection.close(True)
106
Steve McIntyrecc297112014-08-11 18:46:58 +0100107 ################################
108 ### VLAN API functions
109 ################################
110
111 # Create a VLAN with the specified tag
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100112 def vlan_create(self, tag):
Steve McIntyrecc297112014-08-11 18:46:58 +0100113 logging.debug("Creating VLAN %d" % tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100114
Steve McIntyrec5359e62015-02-12 04:54:07 +0000115 try:
116 self._configure()
117 self._cli("vlan database")
118 self._cli("vlan %d" % tag)
119 self._end_configure()
120
121 # Validate it happened
122 vlans = self.vlan_get_list()
123 for vlan in vlans:
124 if vlan == tag:
125 return
Steve McIntyre661af762015-02-12 06:18:07 +0000126 raise IOError("Failed to create VLAN %d" % tag)
Steve McIntyrec5359e62015-02-12 04:54:07 +0000127
128 except PExpectError:
129 # recurse on error
130 self._switch_connect()
131 self.vlan_create(tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100132
133 # Destroy a VLAN with the specified tag
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100134 def vlan_destroy(self, tag):
Steve McIntyrecc297112014-08-11 18:46:58 +0100135 logging.debug("Destroying VLAN %d" % tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100136
Steve McIntyrec5359e62015-02-12 04:54:07 +0000137 try:
138 self._configure()
139 self._cli("no vlan %d" % tag)
140 self._end_configure()
141
142 # Validate it happened
143 vlans = self.vlan_get_list()
144 for vlan in vlans:
145 if vlan == tag:
146 raise IOError("Failed to destroy VLAN %d" % tag)
147
148 except PExpectError:
149 # recurse on error
150 self._switch_connect()
151 self.vlan_destroy(tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100152
153 # Set the name of a VLAN
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100154 def vlan_set_name(self, tag, name):
Steve McIntyrecc297112014-08-11 18:46:58 +0100155 logging.debug("Setting name of VLAN %d to %s" % (tag, name))
Steve McIntyrecc297112014-08-11 18:46:58 +0100156
Steve McIntyrec5359e62015-02-12 04:54:07 +0000157 try:
158 self._configure()
159 self._cli("vlan %d" % tag)
160 self._cli("interface vlan %d" % tag)
161 self._cli("name %s" % name)
162 self._end_configure()
163
164 # Validate it happened
165 read_name = self.vlan_get_name(tag)
166 if read_name != name:
167 raise IOError("Failed to set name for VLAN %d (name found is \"%s\", not \"%s\")"
168 % (tag, read_name, name))
169
170 except PExpectError:
171 # recurse on error
172 self._switch_connect()
173 self.vlan_set_name(tag, name)
Steve McIntyrecc297112014-08-11 18:46:58 +0100174
175 # Get a list of the VLAN tags currently registered on the switch
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100176 def vlan_get_list(self):
Steve McIntyrecc297112014-08-11 18:46:58 +0100177 logging.debug("Grabbing list of VLANs")
Steve McIntyrecc297112014-08-11 18:46:58 +0100178
Steve McIntyrec5359e62015-02-12 04:54:07 +0000179 try:
180 vlans = []
181 regex = re.compile('^ *(\d+).*(D|S|G|R)')
Steve McIntyrecc297112014-08-11 18:46:58 +0100182
Steve McIntyrec5359e62015-02-12 04:54:07 +0000183 self._cli("show vlan")
184 for line in self._read_long_output():
185 match = regex.match(line)
186 if match:
187 vlans.append(int(match.group(1)))
188 return vlans
189
190 except PExpectError:
191 # recurse on error
192 self._switch_connect()
193 return self.vlan_get_list()
Steve McIntyrecc297112014-08-11 18:46:58 +0100194
195 # For a given VLAN tag, ask the switch what the associated name is
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100196 def vlan_get_name(self, tag):
Steve McIntyrecc297112014-08-11 18:46:58 +0100197 logging.debug("Grabbing the name of VLAN %d" % tag)
Steve McIntyrec5359e62015-02-12 04:54:07 +0000198
199 try:
200 name = None
201 regex = re.compile('^ *\d+\s+(\S+).*(D|S|G|R)')
202 self._cli("show vlan tag %d" % tag)
203 for line in self._read_long_output():
204 match = regex.match(line)
205 if match:
206 name = match.group(1)
207 name.strip()
208 return name
209
210 except PExpectError:
211 # recurse on error
212 self._switch_connect()
213 return self.vlan_get_name(tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100214
215 ################################
216 ### Port API functions
217 ################################
218
Steve McIntyre9936d002014-10-01 15:54:10 +0100219 # Set the mode of a port: access or trunk
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100220 def port_set_mode(self, port, mode):
Steve McIntyrecc297112014-08-11 18:46:58 +0100221 logging.debug("Setting port %s to %s" % (port, mode))
222 if not self._is_port_mode_valid(mode):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000223 raise InputError("Port mode %s is not allowed" % mode)
Steve McIntyrecc297112014-08-11 18:46:58 +0100224 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000225 raise InputError("Port name %s not recognised" % port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100226
Steve McIntyrec5359e62015-02-12 04:54:07 +0000227 try:
228 self._configure()
229 self._cli("interface %s" % port)
230 self._cli("switchport mode %s" % mode)
231 self._end_configure()
Steve McIntyrecc297112014-08-11 18:46:58 +0100232
Steve McIntyrec5359e62015-02-12 04:54:07 +0000233 # Validate it happened
234 read_mode = self.port_get_mode(port)
235 if read_mode != mode:
236 raise IOError("Failed to set mode for port %s" % port)
237
238 except PExpectError:
239 # recurse on error
240 self._switch_connect()
241 self.port_set_mode(port, mode)
Steve McIntyrecc297112014-08-11 18:46:58 +0100242
Steve McIntyre9936d002014-10-01 15:54:10 +0100243 # Get the mode of a port: access or trunk
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100244 def port_get_mode(self, port):
Steve McIntyrecc297112014-08-11 18:46:58 +0100245 logging.debug("Getting mode of port %s" % port)
246 mode = ''
247 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000248 raise InputError("Port name %s not recognised" % port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100249 regex = re.compile('Port Mode: (\S+)')
Steve McIntyrec5359e62015-02-12 04:54:07 +0000250
251 try:
252 self._cli("show interfaces switchport %s" % port)
253 for line in self._read_long_output():
254 match = regex.match(line)
255 if match:
256 mode = match.group(1)
257 return mode.lower()
258
259 except PExpectError:
260 # recurse on error
261 self._switch_connect()
262 return self.port_get_mode(port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100263
Steve McIntyre9936d002014-10-01 15:54:10 +0100264 # Set an access port to be in a specified VLAN (tag)
265 def port_set_access_vlan(self, port, tag):
266 logging.debug("Setting access port %s to VLAN %d" % (port, tag))
Steve McIntyrecc297112014-08-11 18:46:58 +0100267 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000268 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9936d002014-10-01 15:54:10 +0100269 if not (self.port_get_mode(port) == "access"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000270 raise InputError("Port %s not in access mode" % port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100271
Steve McIntyrec5359e62015-02-12 04:54:07 +0000272 try:
273 self._configure()
274 self._cli("interface %s" % port)
275 self._cli("switchport access vlan %d" % tag)
276 self._end_configure()
Steve McIntyrecc297112014-08-11 18:46:58 +0100277
Steve McIntyrec5359e62015-02-12 04:54:07 +0000278 # Validate things worked
279 read_vlan = int(self.port_get_access_vlan(port))
280 if read_vlan != tag:
281 raise IOError("Failed to move access port %s to VLAN %d - got VLAN %d instead"
282 % (port, tag, read_vlan))
283
284 except PExpectError:
285 # recurse on error
286 self._switch_connect()
287 self.port_set_access_vlan(port, tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100288
Steve McIntyrecc297112014-08-11 18:46:58 +0100289 # Add a trunk port to a specified VLAN (tag)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100290 def port_add_trunk_to_vlan(self, port, tag):
Steve McIntyrecc297112014-08-11 18:46:58 +0100291 logging.debug("Adding trunk port %s to VLAN %d" % (port, tag))
292 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000293 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100294 if not (self.port_get_mode(port) == "trunk"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000295 raise InputError("Port %s not in trunk mode" % port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100296
Steve McIntyrec5359e62015-02-12 04:54:07 +0000297 try:
298 self._configure()
299 self._cli("interface %s" % port)
300 self._cli("switchport trunk allowed vlan add %d" % tag)
301 self._end_configure()
302
303 # Validate it happened
304 read_vlans = self.port_get_trunk_vlan_list(port)
305 for vlan in read_vlans:
306 if vlan == tag:
307 return
308 raise IOError("Failed to add trunk port %s to VLAN %d" % (port, tag))
309
310 except PExpectError:
311 # recurse on error
312 self._switch_connect()
313 self.port_add_trunk_to_vlan(port, tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100314
315 # Remove a trunk port from a specified VLAN (tag)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100316 def port_remove_trunk_from_vlan(self, port, tag):
Steve McIntyrecc297112014-08-11 18:46:58 +0100317 logging.debug("Removing trunk port %s from VLAN %d" % (port, tag))
318 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000319 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100320 if not (self.port_get_mode(port) == "trunk"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000321 raise InputError("Port %s not in trunk mode" % port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100322
Steve McIntyrec5359e62015-02-12 04:54:07 +0000323 try:
324 self._configure()
325 self._cli("interface %s" % port)
326 self._cli("switchport trunk allowed vlan remove %d" % tag)
327 self._end_configure()
328
329 # Validate it happened
330 read_vlans = self.port_get_trunk_vlan_list(port)
331 for vlan in read_vlans:
332 if vlan == tag:
333 raise IOError("Failed to remove trunk port %s from VLAN %d" % (port, tag))
334
335 except PExpectError:
336 # recurse on error
337 self._switch_connect()
338 self.port_remove_trunk_from_vlan(port, tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100339
Steve McIntyre9936d002014-10-01 15:54:10 +0100340 # Get the configured VLAN tag for an access port (tag)
341 def port_get_access_vlan(self, port):
342 logging.debug("Getting VLAN for access port %s" % port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100343 vlan = 1
344 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000345 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9936d002014-10-01 15:54:10 +0100346 if not (self.port_get_mode(port) == "access"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000347 raise InputError("Port %s not in access mode" % port)
Steve McIntyre0f893602014-12-24 02:14:53 +0000348 regex = re.compile('(\d+)\s+\S+\s+Untagged\s+(Static|System)')
Steve McIntyrec5359e62015-02-12 04:54:07 +0000349
350 try:
351 self._cli("show interfaces switchport %s" % port)
352 for line in self._read_long_output():
353 match = regex.match(line)
354 if match:
355 vlan = match.group(1)
356 return int(vlan)
357
358 except PExpectError:
359 # recurse on error
360 self._switch_connect()
361 return self.port_get_access_vlan(port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100362
363 # Get the list of configured VLAN tags for a trunk port
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100364 def port_get_trunk_vlan_list(self, port):
Steve McIntyrecc297112014-08-11 18:46:58 +0100365 logging.debug("Getting VLANs for trunk port %s" % port)
366 vlans = [ ]
367 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000368 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100369 if not (self.port_get_mode(port) == "trunk"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000370 raise InputError("Port %s not in trunk mode" % port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100371 regex = re.compile('(\d+)\s+\S+\s+(Tagged|Untagged)\s+Static')
Steve McIntyrec5359e62015-02-12 04:54:07 +0000372
373 try:
374 self._cli("show interfaces switchport %s" % port)
375 for line in self._read_long_output():
376 match = regex.match(line)
377 if match:
378 vlans.append (int(match.group(1)))
379 return vlans
380
381 except PExpectError:
382 # recurse on error
383 self._switch_connect()
384 return self.port_get_trunk_vlan_list(port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100385
386 ################################
387 ### Internal functions
388 ################################
389
Steve McIntyreb345fc52015-02-12 06:37:05 +0000390 # Connect to the switch and log in
391 def _switch_connect(self):
392
393 if not self.connection is None:
394 self.connection.close(True)
395 self.connection = None
396
397 logging.debug("Connecting to Switch with: %s" % self.exec_string)
398 self.connection = pexpect.spawn(self.exec_string, logfile = self.logfile)
399
400 self._login()
401
402 # Avoid paged output
403 self._cli("terminal datadump")
404
405 # And grab details about the switch. in case we need it
406 self._get_systemdata()
407
408 # And also validate them - make sure we're driving a switch of
409 # the correct model! Also store the serial number
410 descr_regex = re.compile('System Description:.\s+(.*)')
411 sn_regex = re.compile('SN:\s+(\S_)')
412 descr = ""
413
414 for line in self._systemdata:
415 match = descr_regex.match(line)
416 if match:
417 descr = match.group(1)
418 match = sn_regex.match(line)
419 if match:
420 self.serial_number = match.group(1)
421
422 if not self._expected_descr_re.match(descr):
423 raise IOError("Switch %s not recognised by this driver: abort" % descr)
424
425 # Now build a list of our ports, for later sanity checking
426 self._ports = self._get_port_names()
427 if len(self._ports) < 4:
428 raise IOError("Not enough ports detected - problem!")
429
Steve McIntyrebbd2ac82015-02-12 03:08:48 +0000430 def _login(self):
431 logging.debug("attempting login with username %s, password %s" % (self._username, self._password))
Steve McIntyrecc297112014-08-11 18:46:58 +0100432 self._cli("")
433 self.connection.expect("User Name:")
Steve McIntyrebbd2ac82015-02-12 03:08:48 +0000434 self._cli("%s" % self._username)
Steve McIntyrecc297112014-08-11 18:46:58 +0100435 self.connection.expect("Password:")
Steve McIntyrebbd2ac82015-02-12 03:08:48 +0000436 self._cli("%s" % self._password, False)
Steve McIntyreca6c5a32014-12-23 15:34:29 +0000437 self.connection.expect("\*\*")
Steve McIntyrecc297112014-08-11 18:46:58 +0100438 while True:
Steve McIntyre2651aca2015-02-12 05:10:58 +0000439 index = self.connection.expect(['User Name:', 'authentication failed', r'([^#]+)#', 'Password:', '.+'])
Steve McIntyrecc297112014-08-11 18:46:58 +0100440 if index == 0 or index == 1: # Failed to log in!
441 logging.error("Login failure: %s\n" % self.connection.match)
442 raise IOError
443 elif index == 2:
Steve McIntyre366046f2014-08-18 18:57:03 +0100444 self._prompt_name = self.connection.match.group(1).strip()
Steve McIntyre5c51bc52015-02-12 05:34:09 +0000445 # Horrible output from the switch at login time may
446 # confuse our pexpect stuff here. If we've somehow got
447 # multiple lines of output, clean up and just take the
448 # *last* line here. Anything before that is going to
449 # just be noise from the "***" password input, etc.
450 prompt_lines = self._prompt_name.split('\r\n')
451 if len(prompt_lines) > 1:
452 self._prompt_name = prompt_lines[-1]
Steve McIntyreca6c5a32014-12-23 15:34:29 +0000453 logging.debug("Got prompt name %s" % self._prompt_name)
Steve McIntyrecc297112014-08-11 18:46:58 +0100454 return 0
Steve McIntyreca6c5a32014-12-23 15:34:29 +0000455 elif index == 3 or index == 4:
456 self._cli("", False)
Steve McIntyrecc297112014-08-11 18:46:58 +0100457
458 def _logout(self):
459 logging.debug("Logging out")
460 self._cli("exit", False)
461
Steve McIntyrecc297112014-08-11 18:46:58 +0100462 def _configure(self):
463 self._cli("configure terminal")
464
465 def _end_configure(self):
466 self._cli("end")
467
Steve McIntyrec68d6d72014-12-24 00:23:36 +0000468 def _read_long_output(self):
Steve McIntyre366046f2014-08-18 18:57:03 +0100469 prompt = self._prompt_name + '#'
Steve McIntyre2eececb2015-02-12 03:09:17 +0000470 try:
471 self.connection.expect(prompt)
Steve McIntyre1d23b492015-02-12 03:47:24 +0000472 except (pexpect.EOF, pexpect.TIMEOUT):
Steve McIntyred0614fb2015-02-12 03:44:42 +0000473 # Something went wrong; logout, log in and try again!
474 print "PEXPECT FAILURE, RECONNECT"
Steve McIntyre3c3f4fc2015-02-12 04:26:27 +0000475 self.errors.log_error_in("")
Steve McIntyre1a3e2f72015-02-12 03:49:05 +0000476 raise PExpectError("_read_long_output failed")
Steve McIntyre2eececb2015-02-12 03:09:17 +0000477 except:
478 print "prompt is \"%s\"" % prompt
479 raise
Steve McIntyredf6aabf2015-02-12 06:33:19 +0000480
Steve McIntyrea7cdefc2014-12-24 00:48:19 +0000481 buf = []
482 for line in self.connection.before.split('\r\n'):
483 buf.append(line.strip())
484 return buf
Steve McIntyrecc297112014-08-11 18:46:58 +0100485
486 def _get_port_names(self):
487 logging.debug("Grabbing list of ports")
488 interfaces = []
489
490 # Use "Up" or "Down" to only identify lines in the output that
491 # match interfaces that exist
492 regex = re.compile('^(\w+).*(Up|Down)')
493
Steve McIntyre48ab82c2015-02-12 04:59:10 +0000494 try:
495 self._cli("show interfaces status detailed")
496 for line in self._read_long_output():
497 match = regex.match(line)
498 if match:
499 interfaces.append(match.group(1))
500 return interfaces
501 except PExpectError:
502 # recurse on error
503 self._switch_connect()
504 return self._get_port_names()
Steve McIntyrecc297112014-08-11 18:46:58 +0100505
Steve McIntyrecc297112014-08-11 18:46:58 +0100506 def _show_config(self):
507 logging.debug("Grabbing config")
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000508 try:
509 self._cli("show running-config")
510 return self._read_long_output()
511 except PExpectError:
512 # recurse on error
513 self._switch_connect()
514 return self._show_config()
Steve McIntyrecc297112014-08-11 18:46:58 +0100515
516 def _show_clock(self):
517 logging.debug("Grabbing time")
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000518 try:
519 self._cli("show clock")
520 return self._read_long_output()
521 except PExpectError:
522 # recurse on error
523 self._switch_connect()
524 return self._show_clock()
Steve McIntyrecc297112014-08-11 18:46:58 +0100525
Steve McIntyrecc297112014-08-11 18:46:58 +0100526 def _get_systemdata(self):
Steve McIntyredf6aabf2015-02-12 06:33:19 +0000527 logging.debug("Grabbing system data")
Steve McIntyrecc297112014-08-11 18:46:58 +0100528
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000529 try:
530 self._systemdata = []
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000531 self._cli("show system")
532 for line in self._read_long_output():
533 self._systemdata.append(line)
Steve McIntyre28d34ca2014-08-12 15:40:24 +0100534
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000535 logging.debug("Grabbing system sw and hw versions")
536 self._cli("show version")
537 for line in self._read_long_output():
538 self._systemdata.append(line)
539
540 except PExpectError:
541 # recurse on error
542 self._switch_connect()
543 return self._get_systemdata()
Steve McIntyrecc297112014-08-11 18:46:58 +0100544
Steve McIntyre16f02162014-08-14 17:47:36 +0100545 ######################################
546 # Internal port access helper methods
547 ######################################
548 # N.B. No parameter checking here, for speed reasons - if you're
549 # calling this internal API then you should already have validated
550 # things yourself! Equally, no post-set checks in here - do that
551 # at the higher level.
552 ######################################
553
Steve McIntyrecc297112014-08-11 18:46:58 +0100554 # Wrapper around connection.send - by default, expect() the same
555 # text we've sent, to remove it from the output from the
556 # switch. For the few cases where we don't need that, override
557 # this using echo=False.
558 # Horrible, but seems to work.
559 def _cli(self, text, echo=True):
560 self.connection.send(text + '\r')
561 if echo:
Steve McIntyre9f7f03b2015-02-12 03:10:13 +0000562 try:
563 self.connection.expect(text)
Steve McIntyre1d23b492015-02-12 03:47:24 +0000564 except (pexpect.EOF, pexpect.TIMEOUT):
Steve McIntyre9f7f03b2015-02-12 03:10:13 +0000565 # Something went wrong; logout, log in and try again!
566 print "PEXPECT FAILURE, RECONNECT"
Steve McIntyre3c3f4fc2015-02-12 04:26:27 +0000567 self.errors.log_error_out(text)
Steve McIntyre729d9502015-02-12 03:14:10 +0000568 raise PExpectError("_cli failed on %s" % text)
Steve McIntyre9f7f03b2015-02-12 03:10:13 +0000569 except:
570 print "Unexpected error:", sys.exc_info()[0]
571 raise
Steve McIntyrecc297112014-08-11 18:46:58 +0100572
573if __name__ == "__main__":
Steve McIntyref1699322014-08-19 22:49:43 +0100574# p = CiscoSX300('10.172.2.52', 23)
Steve McIntyre48dc6ae2014-12-23 16:08:19 +0000575
576 import optparse
577
578 switch = 'vlandswitch02'
579 parser = optparse.OptionParser()
580 parser.add_option("--switch",
581 dest = "switch",
582 action = "store",
583 nargs = 1,
584 type = "string",
585 help = "specify switch to connect to for testing",
586 metavar = "<switch>")
587 (opts, args) = parser.parse_args()
588 if opts.switch:
589 switch = opts.switch
590
591 p = CiscoSX300(switch, 23, debug = True)
Steve McIntyreaea995c2014-12-22 17:17:38 +0000592 p.switch_connect('cisco', 'cisco', None)
Steve McIntyrecc297112014-08-11 18:46:58 +0100593 #buf = p._show_clock()
594 #print "%s" % buf
595 #buf = p._show_config()
596 #p._dump_list(buf)
597
Steve McIntyre366046f2014-08-18 18:57:03 +0100598 print "System data:"
599 p._dump_list(p._systemdata)
Steve McIntyrecc297112014-08-11 18:46:58 +0100600
601 print "Creating VLANs for testing:"
602 for i in [ 2, 3, 4, 5, 20 ]:
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100603 p.vlan_create(i)
604 p.vlan_set_name(i, "test%d" % i)
Steve McIntyrecc297112014-08-11 18:46:58 +0100605 print " %d (test%d)" % (i, i)
606
607 #print "And dump config\n"
608 #buf = p._show_config()
609 #print "%s" % buf
610
611 #print "Destroying VLAN 2\n"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100612 #p.vlan_destroy(2)
Steve McIntyrecc297112014-08-11 18:46:58 +0100613
614 #print "And dump config\n"
615 #buf = p._show_config()
616 #print "%s" % buf
617
618 #print "Port names are:"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100619 #buf = p.switch_get_port_names()
Steve McIntyrecc297112014-08-11 18:46:58 +0100620 #p._dump_list(buf)
621
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100622 #buf = p.vlan_get_name(25)
Steve McIntyrecc297112014-08-11 18:46:58 +0100623 #print "VLAN with tag 25 is called \"%s\"" % buf
624
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100625 #p.vlan_set_name(35, "foo")
Steve McIntyrecc297112014-08-11 18:46:58 +0100626 #print "VLAN with tag 35 is called \"foo\""
627
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100628 #buf = p.port_get_mode("fa12")
Steve McIntyrecc297112014-08-11 18:46:58 +0100629 #print "Port fa12 is in %s mode" % buf
630
Steve McIntyre9936d002014-10-01 15:54:10 +0100631 # Test access stuff
632 print "Set fa6 to access mode"
633 p.port_set_mode("fa6", "access")
Steve McIntyre16f02162014-08-14 17:47:36 +0100634 print "Move fa6 to VLAN 2"
Steve McIntyre9936d002014-10-01 15:54:10 +0100635 p.port_set_access_vlan("fa6", 2)
636 buf = p.port_get_access_vlan("fa6")
Steve McIntyrecc297112014-08-11 18:46:58 +0100637 print "Read from switch: fa6 is on VLAN %s" % buf
Steve McIntyre16f02162014-08-14 17:47:36 +0100638 print "Move fa6 back to default VLAN 1"
Steve McIntyre9936d002014-10-01 15:54:10 +0100639 p.port_set_access_vlan("fa6", 1)
Steve McIntyrecc297112014-08-11 18:46:58 +0100640 #print "And move fa6 back to a trunk port"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100641 #p.port_set_mode("fa6", "trunk")
642 #buf = p.port_get_mode("fa6")
Steve McIntyrecc297112014-08-11 18:46:58 +0100643 #print "Port fa6 is in %s mode" % buf
644
645 # Test trunk stuff
646 print "Set gi2 to trunk mode"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100647 p.port_set_mode("gi2", "trunk")
Steve McIntyrecc297112014-08-11 18:46:58 +0100648 print "Add gi2 to VLAN 2"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100649 p.port_add_trunk_to_vlan("gi2", 2)
Steve McIntyrecc297112014-08-11 18:46:58 +0100650 print "Add gi2 to VLAN 3"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100651 p.port_add_trunk_to_vlan("gi2", 3)
Steve McIntyrecc297112014-08-11 18:46:58 +0100652 print "Add gi2 to VLAN 4"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100653 p.port_add_trunk_to_vlan("gi2", 4)
Steve McIntyrecc297112014-08-11 18:46:58 +0100654 print "Read from switch: which VLANs is gi2 on?"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100655 buf = p.port_get_trunk_vlan_list("gi2")
Steve McIntyrecc297112014-08-11 18:46:58 +0100656 p._dump_list(buf)
657
Steve McIntyre366046f2014-08-18 18:57:03 +0100658 print "Remove gi2 from VLANs 3,3,4"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100659 p.port_remove_trunk_from_vlan("gi2", 3)
660 p.port_remove_trunk_from_vlan("gi2", 3)
661 p.port_remove_trunk_from_vlan("gi2", 4)
Steve McIntyrecc297112014-08-11 18:46:58 +0100662 print "Read from switch: which VLANs is gi2 on?"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100663 buf = p.port_get_trunk_vlan_list("gi2")
Steve McIntyrecc297112014-08-11 18:46:58 +0100664 p._dump_list(buf)
665
666 # print "Adding lots of ports to VLANs"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100667 # p.port_add_trunk_to_vlan("fa1", 2)
668 # p.port_add_trunk_to_vlan("fa3", 2)
669 # p.port_add_trunk_to_vlan("fa5", 2)
670 # p.port_add_trunk_to_vlan("fa7", 2)
671 # p.port_add_trunk_to_vlan("fa9", 2)
672 # p.port_add_trunk_to_vlan("fa11", 2)
673 # p.port_add_trunk_to_vlan("fa13", 2)
674 # p.port_add_trunk_to_vlan("fa15", 2)
675 # p.port_add_trunk_to_vlan("fa17", 2)
676 # p.port_add_trunk_to_vlan("fa19", 2)
677 # p.port_add_trunk_to_vlan("fa21", 2)
678 # p.port_add_trunk_to_vlan("fa23", 2)
679 # p.port_add_trunk_to_vlan("gi4", 2)
Steve McIntyrecc297112014-08-11 18:46:58 +0100680
681 print "VLANs are:"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100682 buf = p.vlan_get_list()
Steve McIntyrecc297112014-08-11 18:46:58 +0100683 p._dump_list(buf)
684
Steve McIntyre7460d972014-12-23 14:45:30 +0000685# print 'Restarting switch, to explicitly reset config'
686# p.switch_restart()
Steve McIntyrecc297112014-08-11 18:46:58 +0100687
Steve McIntyrebf5dc882014-12-19 17:54:58 +0000688# p.switch_save_running_config()
Steve McIntyrecc297112014-08-11 18:46:58 +0100689# p._show_config()
690