blob: f933af3bacff816d93d20faf37bf288e20d6f9d0 [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
Steve McIntyrecc297112014-08-11 18:46:58 +010023import re
Steve McIntyre5fa22652015-04-01 18:01:45 +010024from drivers.common import SwitchDriver, SwitchErrors
Steve McIntyrecc297112014-08-11 18:46:58 +010025
Steve McIntyre72a8bce2015-01-23 18:02:19 +000026if __name__ == '__main__':
Steve McIntyrea67473a2015-02-12 08:26:33 +000027 import os
Steve McIntyre72a8bce2015-01-23 18:02:19 +000028 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 McIntyre5fa22652015-04-01 18:01:45 +010032from errors import 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):
Steve McIntyre5fa22652015-04-01 18:01:45 +010052 SwitchDriver.__init__(self)
53 self.systemdata = []
Steve McIntyre48dc6ae2014-12-23 16:08:19 +000054 if debug:
Steve McIntyre9f5a0232014-12-23 16:14:28 +000055 self.logfile = sys.stderr
Steve McIntyrecc297112014-08-11 18:46:58 +010056 self.exec_string = "/usr/bin/telnet %s %d" % (switch_hostname, switch_telnetport)
Steve McIntyre3c3f4fc2015-02-12 04:26:27 +000057 self.errors = SwitchErrors()
Steve McIntyrecc297112014-08-11 18:46:58 +010058
59 ################################
60 ### Switch-level API functions
61 ################################
62
63 # Connect to the switch and log in
Steve McIntyrec1e42b72014-12-22 16:13:08 +000064 def switch_connect(self, username, password, enablepassword):
Steve McIntyrebbd2ac82015-02-12 03:08:48 +000065 self._username = username
66 self._password = password
67 self._enable_password = enablepassword
Steve McIntyre9295d502015-02-12 04:46:45 +000068 self._switch_connect()
69
Steve McIntyrecc297112014-08-11 18:46:58 +010070 # Log out of the switch and drop the connection and all state
Steve McIntyre9b09b9d2014-09-24 15:08:10 +010071 def switch_disconnect(self):
Steve McIntyrecc297112014-08-11 18:46:58 +010072 self._logout()
Steve McIntyre5fa22652015-04-01 18:01:45 +010073 logging.debug("Closing connection: %s", self.connection)
Steve McIntyrecc297112014-08-11 18:46:58 +010074 self.connection.close(True)
Steve McIntyre11393992014-10-10 15:53:34 +010075 self._ports = []
76 self._prompt_name = ''
Steve McIntyre5fa22652015-04-01 18:01:45 +010077 self.systemdata = []
Steve McIntyrecc297112014-08-11 18:46:58 +010078 del(self)
79
80 # Save the current running config into flash - we want config to
81 # remain across reboots
Steve McIntyre9b09b9d2014-09-24 15:08:10 +010082 def switch_save_running_config(self):
Steve McIntyrec5359e62015-02-12 04:54:07 +000083 try:
84 self._cli("copy running-config startup-config")
85 self.connection.expect("Y/N")
86 self._cli("y")
87 self.connection.expect("succeeded")
Steve McIntyre2d84e522015-02-12 06:44:42 +000088 except (PExpectError, pexpect.EOF, pexpect.TIMEOUT):
Steve McIntyrec5359e62015-02-12 04:54:07 +000089 # recurse on error
90 self._switch_connect()
91 self.switch_save_running_config()
Steve McIntyrecc297112014-08-11 18:46:58 +010092
Steve McIntyre095b4452014-12-19 17:53:43 +000093 # Restart the switch - we need to reload config to do a
94 # roll-back. Do NOT save running-config first if the switch asks -
95 # we're trying to dump recent changes, not save them.
96 #
97 # This will also implicitly cause a connection to be closed
98 def switch_restart(self):
99 self._cli("reload")
100 index = self.connection.expect(['Are you sure', 'will reset'])
101 if index == 0:
102 self._cli("y") # Yes, continue without saving
103 self.connection.expect("reset the whole")
104
105 # Fall through
106 self._cli("y") # Yes, continue to reset
107 self.connection.close(True)
108
Steve McIntyrecc297112014-08-11 18:46:58 +0100109 ################################
110 ### VLAN API functions
111 ################################
112
113 # Create a VLAN with the specified tag
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100114 def vlan_create(self, tag):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100115 logging.debug("Creating VLAN %d", tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100116
Steve McIntyrec5359e62015-02-12 04:54:07 +0000117 try:
118 self._configure()
119 self._cli("vlan database")
120 self._cli("vlan %d" % tag)
121 self._end_configure()
122
123 # Validate it happened
124 vlans = self.vlan_get_list()
125 for vlan in vlans:
126 if vlan == tag:
127 return
Steve McIntyre661af762015-02-12 06:18:07 +0000128 raise IOError("Failed to create VLAN %d" % tag)
Steve McIntyrec5359e62015-02-12 04:54:07 +0000129
130 except PExpectError:
131 # recurse on error
132 self._switch_connect()
133 self.vlan_create(tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100134
135 # Destroy a VLAN with the specified tag
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100136 def vlan_destroy(self, tag):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100137 logging.debug("Destroying VLAN %d", tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100138
Steve McIntyrec5359e62015-02-12 04:54:07 +0000139 try:
140 self._configure()
141 self._cli("no vlan %d" % tag)
142 self._end_configure()
143
144 # Validate it happened
145 vlans = self.vlan_get_list()
146 for vlan in vlans:
147 if vlan == tag:
148 raise IOError("Failed to destroy VLAN %d" % tag)
149
150 except PExpectError:
151 # recurse on error
152 self._switch_connect()
153 self.vlan_destroy(tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100154
155 # Set the name of a VLAN
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100156 def vlan_set_name(self, tag, name):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100157 logging.debug("Setting name of VLAN %d to %s", tag, name)
Steve McIntyrecc297112014-08-11 18:46:58 +0100158
Steve McIntyrec5359e62015-02-12 04:54:07 +0000159 try:
160 self._configure()
161 self._cli("vlan %d" % tag)
162 self._cli("interface vlan %d" % tag)
163 self._cli("name %s" % name)
164 self._end_configure()
165
166 # Validate it happened
167 read_name = self.vlan_get_name(tag)
168 if read_name != name:
169 raise IOError("Failed to set name for VLAN %d (name found is \"%s\", not \"%s\")"
170 % (tag, read_name, name))
171
172 except PExpectError:
173 # recurse on error
174 self._switch_connect()
175 self.vlan_set_name(tag, name)
Steve McIntyrecc297112014-08-11 18:46:58 +0100176
177 # Get a list of the VLAN tags currently registered on the switch
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100178 def vlan_get_list(self):
Steve McIntyrecc297112014-08-11 18:46:58 +0100179 logging.debug("Grabbing list of VLANs")
Steve McIntyrecc297112014-08-11 18:46:58 +0100180
Steve McIntyrec5359e62015-02-12 04:54:07 +0000181 try:
182 vlans = []
183 regex = re.compile('^ *(\d+).*(D|S|G|R)')
Steve McIntyrecc297112014-08-11 18:46:58 +0100184
Steve McIntyrec5359e62015-02-12 04:54:07 +0000185 self._cli("show vlan")
186 for line in self._read_long_output():
187 match = regex.match(line)
188 if match:
189 vlans.append(int(match.group(1)))
190 return vlans
191
192 except PExpectError:
193 # recurse on error
194 self._switch_connect()
195 return self.vlan_get_list()
Steve McIntyrecc297112014-08-11 18:46:58 +0100196
197 # For a given VLAN tag, ask the switch what the associated name is
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100198 def vlan_get_name(self, tag):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100199 logging.debug("Grabbing the name of VLAN %d", tag)
Steve McIntyrec5359e62015-02-12 04:54:07 +0000200
201 try:
202 name = None
203 regex = re.compile('^ *\d+\s+(\S+).*(D|S|G|R)')
204 self._cli("show vlan tag %d" % tag)
205 for line in self._read_long_output():
206 match = regex.match(line)
207 if match:
208 name = match.group(1)
209 name.strip()
210 return name
211
212 except PExpectError:
213 # recurse on error
214 self._switch_connect()
215 return self.vlan_get_name(tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100216
217 ################################
218 ### Port API functions
219 ################################
220
Steve McIntyre9936d002014-10-01 15:54:10 +0100221 # Set the mode of a port: access or trunk
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100222 def port_set_mode(self, port, mode):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100223 logging.debug("Setting port %s to %s", port, mode)
Steve McIntyrecc297112014-08-11 18:46:58 +0100224 if not self._is_port_mode_valid(mode):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000225 raise InputError("Port mode %s is not allowed" % mode)
Steve McIntyrecc297112014-08-11 18:46:58 +0100226 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000227 raise InputError("Port name %s not recognised" % port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100228
Steve McIntyrec5359e62015-02-12 04:54:07 +0000229 try:
230 self._configure()
231 self._cli("interface %s" % port)
232 self._cli("switchport mode %s" % mode)
233 self._end_configure()
Steve McIntyrecc297112014-08-11 18:46:58 +0100234
Steve McIntyrec5359e62015-02-12 04:54:07 +0000235 # Validate it happened
236 read_mode = self.port_get_mode(port)
237 if read_mode != mode:
238 raise IOError("Failed to set mode for port %s" % port)
239
240 except PExpectError:
241 # recurse on error
242 self._switch_connect()
243 self.port_set_mode(port, mode)
Steve McIntyrecc297112014-08-11 18:46:58 +0100244
Steve McIntyre9936d002014-10-01 15:54:10 +0100245 # Get the mode of a port: access or trunk
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100246 def port_get_mode(self, port):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100247 logging.debug("Getting mode of port %s", port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100248 mode = ''
249 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000250 raise InputError("Port name %s not recognised" % port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100251 regex = re.compile('Port Mode: (\S+)')
Steve McIntyrec5359e62015-02-12 04:54:07 +0000252
253 try:
254 self._cli("show interfaces switchport %s" % port)
255 for line in self._read_long_output():
256 match = regex.match(line)
257 if match:
258 mode = match.group(1)
259 return mode.lower()
260
261 except PExpectError:
262 # recurse on error
263 self._switch_connect()
264 return self.port_get_mode(port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100265
Steve McIntyre9936d002014-10-01 15:54:10 +0100266 # Set an access port to be in a specified VLAN (tag)
267 def port_set_access_vlan(self, port, tag):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100268 logging.debug("Setting access port %s to VLAN %d", port, tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100269 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000270 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9936d002014-10-01 15:54:10 +0100271 if not (self.port_get_mode(port) == "access"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000272 raise InputError("Port %s not in access mode" % port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100273
Steve McIntyrec5359e62015-02-12 04:54:07 +0000274 try:
275 self._configure()
276 self._cli("interface %s" % port)
277 self._cli("switchport access vlan %d" % tag)
278 self._end_configure()
Steve McIntyrecc297112014-08-11 18:46:58 +0100279
Steve McIntyrec5359e62015-02-12 04:54:07 +0000280 # Validate things worked
281 read_vlan = int(self.port_get_access_vlan(port))
282 if read_vlan != tag:
283 raise IOError("Failed to move access port %s to VLAN %d - got VLAN %d instead"
284 % (port, tag, read_vlan))
285
286 except PExpectError:
287 # recurse on error
288 self._switch_connect()
289 self.port_set_access_vlan(port, tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100290
Steve McIntyrecc297112014-08-11 18:46:58 +0100291 # Add a trunk port to a specified VLAN (tag)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100292 def port_add_trunk_to_vlan(self, port, tag):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100293 logging.debug("Adding trunk port %s to VLAN %d", port, tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100294 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000295 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100296 if not (self.port_get_mode(port) == "trunk"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000297 raise InputError("Port %s not in trunk mode" % port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100298
Steve McIntyrec5359e62015-02-12 04:54:07 +0000299 try:
300 self._configure()
301 self._cli("interface %s" % port)
302 self._cli("switchport trunk allowed vlan add %d" % tag)
303 self._end_configure()
304
305 # Validate it happened
306 read_vlans = self.port_get_trunk_vlan_list(port)
307 for vlan in read_vlans:
308 if vlan == tag:
309 return
310 raise IOError("Failed to add trunk port %s to VLAN %d" % (port, tag))
311
312 except PExpectError:
313 # recurse on error
314 self._switch_connect()
315 self.port_add_trunk_to_vlan(port, tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100316
317 # Remove a trunk port from a specified VLAN (tag)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100318 def port_remove_trunk_from_vlan(self, port, tag):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100319 logging.debug("Removing trunk port %s from VLAN %d", port, tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100320 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000321 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100322 if not (self.port_get_mode(port) == "trunk"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000323 raise InputError("Port %s not in trunk mode" % port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100324
Steve McIntyrec5359e62015-02-12 04:54:07 +0000325 try:
326 self._configure()
327 self._cli("interface %s" % port)
328 self._cli("switchport trunk allowed vlan remove %d" % tag)
329 self._end_configure()
330
331 # Validate it happened
332 read_vlans = self.port_get_trunk_vlan_list(port)
333 for vlan in read_vlans:
334 if vlan == tag:
335 raise IOError("Failed to remove trunk port %s from VLAN %d" % (port, tag))
336
337 except PExpectError:
338 # recurse on error
339 self._switch_connect()
340 self.port_remove_trunk_from_vlan(port, tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100341
Steve McIntyre9936d002014-10-01 15:54:10 +0100342 # Get the configured VLAN tag for an access port (tag)
343 def port_get_access_vlan(self, port):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100344 logging.debug("Getting VLAN for access port %s", port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100345 vlan = 1
346 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000347 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9936d002014-10-01 15:54:10 +0100348 if not (self.port_get_mode(port) == "access"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000349 raise InputError("Port %s not in access mode" % port)
Steve McIntyre0f893602014-12-24 02:14:53 +0000350 regex = re.compile('(\d+)\s+\S+\s+Untagged\s+(Static|System)')
Steve McIntyrec5359e62015-02-12 04:54:07 +0000351
352 try:
353 self._cli("show interfaces switchport %s" % port)
354 for line in self._read_long_output():
355 match = regex.match(line)
356 if match:
357 vlan = match.group(1)
358 return int(vlan)
359
360 except PExpectError:
361 # recurse on error
362 self._switch_connect()
363 return self.port_get_access_vlan(port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100364
365 # Get the list of configured VLAN tags for a trunk port
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100366 def port_get_trunk_vlan_list(self, port):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100367 logging.debug("Getting VLANs for trunk port %s", port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100368 vlans = [ ]
369 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000370 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100371 if not (self.port_get_mode(port) == "trunk"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000372 raise InputError("Port %s not in trunk mode" % port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100373 regex = re.compile('(\d+)\s+\S+\s+(Tagged|Untagged)\s+Static')
Steve McIntyrec5359e62015-02-12 04:54:07 +0000374
375 try:
376 self._cli("show interfaces switchport %s" % port)
377 for line in self._read_long_output():
378 match = regex.match(line)
379 if match:
380 vlans.append (int(match.group(1)))
381 return vlans
382
383 except PExpectError:
384 # recurse on error
385 self._switch_connect()
386 return self.port_get_trunk_vlan_list(port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100387
388 ################################
389 ### Internal functions
390 ################################
391
Steve McIntyreb345fc52015-02-12 06:37:05 +0000392 # Connect to the switch and log in
393 def _switch_connect(self):
394
395 if not self.connection is None:
396 self.connection.close(True)
397 self.connection = None
398
Steve McIntyre5fa22652015-04-01 18:01:45 +0100399 logging.debug("Connecting to Switch with: %s", self.exec_string)
Steve McIntyreb345fc52015-02-12 06:37:05 +0000400 self.connection = pexpect.spawn(self.exec_string, logfile = self.logfile)
401
402 self._login()
403
404 # Avoid paged output
405 self._cli("terminal datadump")
406
407 # And grab details about the switch. in case we need it
408 self._get_systemdata()
409
410 # And also validate them - make sure we're driving a switch of
411 # the correct model! Also store the serial number
412 descr_regex = re.compile('System Description:.\s+(.*)')
413 sn_regex = re.compile('SN:\s+(\S_)')
414 descr = ""
415
Steve McIntyre5fa22652015-04-01 18:01:45 +0100416 for line in self.systemdata:
Steve McIntyreb345fc52015-02-12 06:37:05 +0000417 match = descr_regex.match(line)
418 if match:
419 descr = match.group(1)
420 match = sn_regex.match(line)
421 if match:
422 self.serial_number = match.group(1)
423
424 if not self._expected_descr_re.match(descr):
425 raise IOError("Switch %s not recognised by this driver: abort" % descr)
426
427 # Now build a list of our ports, for later sanity checking
428 self._ports = self._get_port_names()
429 if len(self._ports) < 4:
430 raise IOError("Not enough ports detected - problem!")
431
Steve McIntyrebbd2ac82015-02-12 03:08:48 +0000432 def _login(self):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100433 logging.debug("attempting login with username %s, password %s", self._username, self._password)
Steve McIntyrecc297112014-08-11 18:46:58 +0100434 self._cli("")
435 self.connection.expect("User Name:")
Steve McIntyrebbd2ac82015-02-12 03:08:48 +0000436 self._cli("%s" % self._username)
Steve McIntyrecc297112014-08-11 18:46:58 +0100437 self.connection.expect("Password:")
Steve McIntyrebbd2ac82015-02-12 03:08:48 +0000438 self._cli("%s" % self._password, False)
Steve McIntyreca6c5a32014-12-23 15:34:29 +0000439 self.connection.expect("\*\*")
Steve McIntyrecc297112014-08-11 18:46:58 +0100440 while True:
Steve McIntyre2651aca2015-02-12 05:10:58 +0000441 index = self.connection.expect(['User Name:', 'authentication failed', r'([^#]+)#', 'Password:', '.+'])
Steve McIntyrecc297112014-08-11 18:46:58 +0100442 if index == 0 or index == 1: # Failed to log in!
Steve McIntyre5fa22652015-04-01 18:01:45 +0100443 logging.error("Login failure: %s\n", self.connection.match)
Steve McIntyrecc297112014-08-11 18:46:58 +0100444 raise IOError
445 elif index == 2:
Steve McIntyre366046f2014-08-18 18:57:03 +0100446 self._prompt_name = self.connection.match.group(1).strip()
Steve McIntyre5c51bc52015-02-12 05:34:09 +0000447 # Horrible output from the switch at login time may
448 # confuse our pexpect stuff here. If we've somehow got
449 # multiple lines of output, clean up and just take the
450 # *last* line here. Anything before that is going to
451 # just be noise from the "***" password input, etc.
452 prompt_lines = self._prompt_name.split('\r\n')
453 if len(prompt_lines) > 1:
454 self._prompt_name = prompt_lines[-1]
Steve McIntyre5fa22652015-04-01 18:01:45 +0100455 logging.debug("Got prompt name %s", self._prompt_name)
Steve McIntyrecc297112014-08-11 18:46:58 +0100456 return 0
Steve McIntyreca6c5a32014-12-23 15:34:29 +0000457 elif index == 3 or index == 4:
458 self._cli("", False)
Steve McIntyrecc297112014-08-11 18:46:58 +0100459
460 def _logout(self):
461 logging.debug("Logging out")
462 self._cli("exit", False)
463
Steve McIntyrecc297112014-08-11 18:46:58 +0100464 def _configure(self):
465 self._cli("configure terminal")
466
467 def _end_configure(self):
468 self._cli("end")
469
Steve McIntyrec68d6d72014-12-24 00:23:36 +0000470 def _read_long_output(self):
Steve McIntyre366046f2014-08-18 18:57:03 +0100471 prompt = self._prompt_name + '#'
Steve McIntyre2eececb2015-02-12 03:09:17 +0000472 try:
473 self.connection.expect(prompt)
Steve McIntyre1d23b492015-02-12 03:47:24 +0000474 except (pexpect.EOF, pexpect.TIMEOUT):
Steve McIntyred0614fb2015-02-12 03:44:42 +0000475 # Something went wrong; logout, log in and try again!
Steve McIntyre7a9c8192015-02-12 07:15:52 +0000476 logging.error("PEXPECT FAILURE, RECONNECT")
Steve McIntyre3c3f4fc2015-02-12 04:26:27 +0000477 self.errors.log_error_in("")
Steve McIntyre1a3e2f72015-02-12 03:49:05 +0000478 raise PExpectError("_read_long_output failed")
Steve McIntyre2eececb2015-02-12 03:09:17 +0000479 except:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100480 logging.error("prompt is \"%s\"", prompt)
Steve McIntyre2eececb2015-02-12 03:09:17 +0000481 raise
Steve McIntyredf6aabf2015-02-12 06:33:19 +0000482
Steve McIntyre5fa22652015-04-01 18:01:45 +0100483 longbuf = []
Steve McIntyrea7cdefc2014-12-24 00:48:19 +0000484 for line in self.connection.before.split('\r\n'):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100485 longbuf.append(line.strip())
486 return longbuf
Steve McIntyrecc297112014-08-11 18:46:58 +0100487
488 def _get_port_names(self):
489 logging.debug("Grabbing list of ports")
490 interfaces = []
491
492 # Use "Up" or "Down" to only identify lines in the output that
493 # match interfaces that exist
494 regex = re.compile('^(\w+).*(Up|Down)')
495
Steve McIntyre48ab82c2015-02-12 04:59:10 +0000496 try:
497 self._cli("show interfaces status detailed")
498 for line in self._read_long_output():
499 match = regex.match(line)
500 if match:
501 interfaces.append(match.group(1))
502 return interfaces
503 except PExpectError:
504 # recurse on error
505 self._switch_connect()
506 return self._get_port_names()
Steve McIntyrecc297112014-08-11 18:46:58 +0100507
Steve McIntyrecc297112014-08-11 18:46:58 +0100508 def _show_config(self):
509 logging.debug("Grabbing config")
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000510 try:
511 self._cli("show running-config")
512 return self._read_long_output()
513 except PExpectError:
514 # recurse on error
515 self._switch_connect()
516 return self._show_config()
Steve McIntyrecc297112014-08-11 18:46:58 +0100517
518 def _show_clock(self):
519 logging.debug("Grabbing time")
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000520 try:
521 self._cli("show clock")
522 return self._read_long_output()
523 except PExpectError:
524 # recurse on error
525 self._switch_connect()
526 return self._show_clock()
Steve McIntyrecc297112014-08-11 18:46:58 +0100527
Steve McIntyrecc297112014-08-11 18:46:58 +0100528 def _get_systemdata(self):
Steve McIntyredf6aabf2015-02-12 06:33:19 +0000529 logging.debug("Grabbing system data")
Steve McIntyrecc297112014-08-11 18:46:58 +0100530
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000531 try:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100532 self.systemdata = []
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000533 self._cli("show system")
534 for line in self._read_long_output():
Steve McIntyre5fa22652015-04-01 18:01:45 +0100535 self.systemdata.append(line)
Steve McIntyre28d34ca2014-08-12 15:40:24 +0100536
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000537 logging.debug("Grabbing system sw and hw versions")
538 self._cli("show version")
539 for line in self._read_long_output():
Steve McIntyre5fa22652015-04-01 18:01:45 +0100540 self.systemdata.append(line)
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000541
542 except PExpectError:
543 # recurse on error
544 self._switch_connect()
545 return self._get_systemdata()
Steve McIntyrecc297112014-08-11 18:46:58 +0100546
Steve McIntyre16f02162014-08-14 17:47:36 +0100547 ######################################
548 # Internal port access helper methods
549 ######################################
550 # N.B. No parameter checking here, for speed reasons - if you're
551 # calling this internal API then you should already have validated
552 # things yourself! Equally, no post-set checks in here - do that
553 # at the higher level.
554 ######################################
555
Steve McIntyrecc297112014-08-11 18:46:58 +0100556 # Wrapper around connection.send - by default, expect() the same
557 # text we've sent, to remove it from the output from the
558 # switch. For the few cases where we don't need that, override
559 # this using echo=False.
560 # Horrible, but seems to work.
561 def _cli(self, text, echo=True):
562 self.connection.send(text + '\r')
563 if echo:
Steve McIntyre9f7f03b2015-02-12 03:10:13 +0000564 try:
565 self.connection.expect(text)
Steve McIntyre1d23b492015-02-12 03:47:24 +0000566 except (pexpect.EOF, pexpect.TIMEOUT):
Steve McIntyre9f7f03b2015-02-12 03:10:13 +0000567 # Something went wrong; logout, log in and try again!
Steve McIntyre7a9c8192015-02-12 07:15:52 +0000568 logging.error("PEXPECT FAILURE, RECONNECT")
Steve McIntyre3c3f4fc2015-02-12 04:26:27 +0000569 self.errors.log_error_out(text)
Steve McIntyre729d9502015-02-12 03:14:10 +0000570 raise PExpectError("_cli failed on %s" % text)
Steve McIntyre9f7f03b2015-02-12 03:10:13 +0000571 except:
Steve McIntyrea67473a2015-02-12 08:26:33 +0000572 logging.error("Unexpected error: %s", sys.exc_info()[0])
Steve McIntyre9f7f03b2015-02-12 03:10:13 +0000573 raise
Steve McIntyrecc297112014-08-11 18:46:58 +0100574
575if __name__ == "__main__":
Steve McIntyref1699322014-08-19 22:49:43 +0100576# p = CiscoSX300('10.172.2.52', 23)
Steve McIntyre48dc6ae2014-12-23 16:08:19 +0000577
578 import optparse
579
580 switch = 'vlandswitch02'
581 parser = optparse.OptionParser()
582 parser.add_option("--switch",
583 dest = "switch",
584 action = "store",
585 nargs = 1,
586 type = "string",
587 help = "specify switch to connect to for testing",
588 metavar = "<switch>")
589 (opts, args) = parser.parse_args()
590 if opts.switch:
591 switch = opts.switch
592
593 p = CiscoSX300(switch, 23, debug = True)
Steve McIntyreaea995c2014-12-22 17:17:38 +0000594 p.switch_connect('cisco', 'cisco', None)
Steve McIntyrecc297112014-08-11 18:46:58 +0100595 #buf = p._show_clock()
596 #print "%s" % buf
597 #buf = p._show_config()
Steve McIntyre5fa22652015-04-01 18:01:45 +0100598 #p.dump_list(buf)
Steve McIntyrecc297112014-08-11 18:46:58 +0100599
Steve McIntyre366046f2014-08-18 18:57:03 +0100600 print "System data:"
Steve McIntyre5fa22652015-04-01 18:01:45 +0100601 p.dump_list(p.systemdata)
Steve McIntyrecc297112014-08-11 18:46:58 +0100602
603 print "Creating VLANs for testing:"
604 for i in [ 2, 3, 4, 5, 20 ]:
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100605 p.vlan_create(i)
606 p.vlan_set_name(i, "test%d" % i)
Steve McIntyrecc297112014-08-11 18:46:58 +0100607 print " %d (test%d)" % (i, i)
608
609 #print "And dump config\n"
610 #buf = p._show_config()
611 #print "%s" % buf
612
613 #print "Destroying VLAN 2\n"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100614 #p.vlan_destroy(2)
Steve McIntyrecc297112014-08-11 18:46:58 +0100615
616 #print "And dump config\n"
617 #buf = p._show_config()
618 #print "%s" % buf
619
620 #print "Port names are:"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100621 #buf = p.switch_get_port_names()
Steve McIntyre5fa22652015-04-01 18:01:45 +0100622 #p.dump_list(buf)
Steve McIntyrecc297112014-08-11 18:46:58 +0100623
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100624 #buf = p.vlan_get_name(25)
Steve McIntyrecc297112014-08-11 18:46:58 +0100625 #print "VLAN with tag 25 is called \"%s\"" % buf
626
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100627 #p.vlan_set_name(35, "foo")
Steve McIntyrecc297112014-08-11 18:46:58 +0100628 #print "VLAN with tag 35 is called \"foo\""
629
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100630 #buf = p.port_get_mode("fa12")
Steve McIntyrecc297112014-08-11 18:46:58 +0100631 #print "Port fa12 is in %s mode" % buf
632
Steve McIntyre9936d002014-10-01 15:54:10 +0100633 # Test access stuff
634 print "Set fa6 to access mode"
635 p.port_set_mode("fa6", "access")
Steve McIntyre16f02162014-08-14 17:47:36 +0100636 print "Move fa6 to VLAN 2"
Steve McIntyre9936d002014-10-01 15:54:10 +0100637 p.port_set_access_vlan("fa6", 2)
638 buf = p.port_get_access_vlan("fa6")
Steve McIntyrecc297112014-08-11 18:46:58 +0100639 print "Read from switch: fa6 is on VLAN %s" % buf
Steve McIntyre16f02162014-08-14 17:47:36 +0100640 print "Move fa6 back to default VLAN 1"
Steve McIntyre9936d002014-10-01 15:54:10 +0100641 p.port_set_access_vlan("fa6", 1)
Steve McIntyrecc297112014-08-11 18:46:58 +0100642 #print "And move fa6 back to a trunk port"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100643 #p.port_set_mode("fa6", "trunk")
644 #buf = p.port_get_mode("fa6")
Steve McIntyrecc297112014-08-11 18:46:58 +0100645 #print "Port fa6 is in %s mode" % buf
646
647 # Test trunk stuff
648 print "Set gi2 to trunk mode"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100649 p.port_set_mode("gi2", "trunk")
Steve McIntyrecc297112014-08-11 18:46:58 +0100650 print "Add gi2 to VLAN 2"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100651 p.port_add_trunk_to_vlan("gi2", 2)
Steve McIntyrecc297112014-08-11 18:46:58 +0100652 print "Add gi2 to VLAN 3"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100653 p.port_add_trunk_to_vlan("gi2", 3)
Steve McIntyrecc297112014-08-11 18:46:58 +0100654 print "Add gi2 to VLAN 4"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100655 p.port_add_trunk_to_vlan("gi2", 4)
Steve McIntyrecc297112014-08-11 18:46:58 +0100656 print "Read from switch: which VLANs is gi2 on?"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100657 buf = p.port_get_trunk_vlan_list("gi2")
Steve McIntyre5fa22652015-04-01 18:01:45 +0100658 p.dump_list(buf)
Steve McIntyrecc297112014-08-11 18:46:58 +0100659
Steve McIntyre366046f2014-08-18 18:57:03 +0100660 print "Remove gi2 from VLANs 3,3,4"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100661 p.port_remove_trunk_from_vlan("gi2", 3)
662 p.port_remove_trunk_from_vlan("gi2", 3)
663 p.port_remove_trunk_from_vlan("gi2", 4)
Steve McIntyrecc297112014-08-11 18:46:58 +0100664 print "Read from switch: which VLANs is gi2 on?"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100665 buf = p.port_get_trunk_vlan_list("gi2")
Steve McIntyre5fa22652015-04-01 18:01:45 +0100666 p.dump_list(buf)
Steve McIntyrecc297112014-08-11 18:46:58 +0100667
668 # print "Adding lots of ports to VLANs"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100669 # p.port_add_trunk_to_vlan("fa1", 2)
670 # p.port_add_trunk_to_vlan("fa3", 2)
671 # p.port_add_trunk_to_vlan("fa5", 2)
672 # p.port_add_trunk_to_vlan("fa7", 2)
673 # p.port_add_trunk_to_vlan("fa9", 2)
674 # p.port_add_trunk_to_vlan("fa11", 2)
675 # p.port_add_trunk_to_vlan("fa13", 2)
676 # p.port_add_trunk_to_vlan("fa15", 2)
677 # p.port_add_trunk_to_vlan("fa17", 2)
678 # p.port_add_trunk_to_vlan("fa19", 2)
679 # p.port_add_trunk_to_vlan("fa21", 2)
680 # p.port_add_trunk_to_vlan("fa23", 2)
681 # p.port_add_trunk_to_vlan("gi4", 2)
Steve McIntyrecc297112014-08-11 18:46:58 +0100682
683 print "VLANs are:"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100684 buf = p.vlan_get_list()
Steve McIntyre5fa22652015-04-01 18:01:45 +0100685 p.dump_list(buf)
Steve McIntyrecc297112014-08-11 18:46:58 +0100686
Steve McIntyre7460d972014-12-23 14:45:30 +0000687# print 'Restarting switch, to explicitly reset config'
688# p.switch_restart()
Steve McIntyrecc297112014-08-11 18:46:58 +0100689
Steve McIntyrebf5dc882014-12-19 17:54:58 +0000690# p.switch_save_running_config()
Steve McIntyrecc297112014-08-11 18:46:58 +0100691# p._show_config()
692