blob: 4e4d4c1feec3c855aa5dd83df1db374d020cc91e [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__':
Steve McIntyrea67473a2015-02-12 08:26:33 +000028 import os
Steve McIntyre72a8bce2015-01-23 18:02:19 +000029 vlandpath = os.path.abspath(os.path.normpath(os.path.dirname(sys.argv[0])))
30 sys.path.insert(0, vlandpath)
31 sys.path.insert(0, "%s/.." % vlandpath)
32
Steve McIntyre9f7f03b2015-02-12 03:10:13 +000033from errors import CriticalError, InputError, PExpectError
Steve McIntyre72a8bce2015-01-23 18:02:19 +000034
Steve McIntyrecc297112014-08-11 18:46:58 +010035class CiscoSX300(SwitchDriver):
36
37 connection = None
Steve McIntyrebbd2ac82015-02-12 03:08:48 +000038 _username = None
39 _password = None
40 _enable_password = None
Steve McIntyre366046f2014-08-18 18:57:03 +010041
42 # No extra capabilities for this switch/driver yet
43 _capabilities = [
44 ]
45
Steve McIntyrecc297112014-08-11 18:46:58 +010046 # Regexp of expected hardware information - fail if we don't see
47 # this
Steve McIntyreb9783ff2014-12-23 16:36:35 +000048 _expected_descr_re = re.compile('.*\d+-Port.*Managed Switch.*')
Steve McIntyrecc297112014-08-11 18:46:58 +010049
Steve McIntyre16f02162014-08-14 17:47:36 +010050 logfile = None
Steve McIntyrecc297112014-08-11 18:46:58 +010051
Steve McIntyre48dc6ae2014-12-23 16:08:19 +000052 def __init__(self, switch_hostname, switch_telnetport=23, debug = False):
53 if debug:
Steve McIntyre9f5a0232014-12-23 16:14:28 +000054 self.logfile = sys.stderr
Steve McIntyrecc297112014-08-11 18:46:58 +010055 self.exec_string = "/usr/bin/telnet %s %d" % (switch_hostname, switch_telnetport)
Steve McIntyre3c3f4fc2015-02-12 04:26:27 +000056 self.errors = SwitchErrors()
Steve McIntyrecc297112014-08-11 18:46:58 +010057
58 ################################
59 ### Switch-level API functions
60 ################################
61
62 # Connect to the switch and log in
Steve McIntyrec1e42b72014-12-22 16:13:08 +000063 def switch_connect(self, username, password, enablepassword):
Steve McIntyrebbd2ac82015-02-12 03:08:48 +000064 self._username = username
65 self._password = password
66 self._enable_password = enablepassword
Steve McIntyre9295d502015-02-12 04:46:45 +000067 self._switch_connect()
68
Steve McIntyrecc297112014-08-11 18:46:58 +010069 # Log out of the switch and drop the connection and all state
Steve McIntyre9b09b9d2014-09-24 15:08:10 +010070 def switch_disconnect(self):
Steve McIntyrecc297112014-08-11 18:46:58 +010071 self._logout()
72 logging.debug("Closing connection: %s" % self.connection)
73 self.connection.close(True)
Steve McIntyre11393992014-10-10 15:53:34 +010074 self._ports = []
75 self._prompt_name = ''
76 self._systemdata = []
Steve McIntyrecc297112014-08-11 18:46:58 +010077 del(self)
78
79 # Save the current running config into flash - we want config to
80 # remain across reboots
Steve McIntyre9b09b9d2014-09-24 15:08:10 +010081 def switch_save_running_config(self):
Steve McIntyrec5359e62015-02-12 04:54:07 +000082 try:
83 self._cli("copy running-config startup-config")
84 self.connection.expect("Y/N")
85 self._cli("y")
86 self.connection.expect("succeeded")
Steve McIntyre2d84e522015-02-12 06:44:42 +000087 except (PExpectError, pexpect.EOF, pexpect.TIMEOUT):
Steve McIntyrec5359e62015-02-12 04:54:07 +000088 # recurse on error
89 self._switch_connect()
90 self.switch_save_running_config()
Steve McIntyrecc297112014-08-11 18:46:58 +010091
Steve McIntyre095b4452014-12-19 17:53:43 +000092 # Restart the switch - we need to reload config to do a
93 # roll-back. Do NOT save running-config first if the switch asks -
94 # we're trying to dump recent changes, not save them.
95 #
96 # This will also implicitly cause a connection to be closed
97 def switch_restart(self):
98 self._cli("reload")
99 index = self.connection.expect(['Are you sure', 'will reset'])
100 if index == 0:
101 self._cli("y") # Yes, continue without saving
102 self.connection.expect("reset the whole")
103
104 # Fall through
105 self._cli("y") # Yes, continue to reset
106 self.connection.close(True)
107
Steve McIntyrecc297112014-08-11 18:46:58 +0100108 ################################
109 ### VLAN API functions
110 ################################
111
112 # Create a VLAN with the specified tag
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100113 def vlan_create(self, tag):
Steve McIntyrecc297112014-08-11 18:46:58 +0100114 logging.debug("Creating VLAN %d" % tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100115
Steve McIntyrec5359e62015-02-12 04:54:07 +0000116 try:
117 self._configure()
118 self._cli("vlan database")
119 self._cli("vlan %d" % tag)
120 self._end_configure()
121
122 # Validate it happened
123 vlans = self.vlan_get_list()
124 for vlan in vlans:
125 if vlan == tag:
126 return
Steve McIntyre661af762015-02-12 06:18:07 +0000127 raise IOError("Failed to create VLAN %d" % tag)
Steve McIntyrec5359e62015-02-12 04:54:07 +0000128
129 except PExpectError:
130 # recurse on error
131 self._switch_connect()
132 self.vlan_create(tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100133
134 # Destroy a VLAN with the specified tag
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100135 def vlan_destroy(self, tag):
Steve McIntyrecc297112014-08-11 18:46:58 +0100136 logging.debug("Destroying VLAN %d" % tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100137
Steve McIntyrec5359e62015-02-12 04:54:07 +0000138 try:
139 self._configure()
140 self._cli("no vlan %d" % tag)
141 self._end_configure()
142
143 # Validate it happened
144 vlans = self.vlan_get_list()
145 for vlan in vlans:
146 if vlan == tag:
147 raise IOError("Failed to destroy VLAN %d" % tag)
148
149 except PExpectError:
150 # recurse on error
151 self._switch_connect()
152 self.vlan_destroy(tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100153
154 # Set the name of a VLAN
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100155 def vlan_set_name(self, tag, name):
Steve McIntyrecc297112014-08-11 18:46:58 +0100156 logging.debug("Setting name of VLAN %d to %s" % (tag, name))
Steve McIntyrecc297112014-08-11 18:46:58 +0100157
Steve McIntyrec5359e62015-02-12 04:54:07 +0000158 try:
159 self._configure()
160 self._cli("vlan %d" % tag)
161 self._cli("interface vlan %d" % tag)
162 self._cli("name %s" % name)
163 self._end_configure()
164
165 # Validate it happened
166 read_name = self.vlan_get_name(tag)
167 if read_name != name:
168 raise IOError("Failed to set name for VLAN %d (name found is \"%s\", not \"%s\")"
169 % (tag, read_name, name))
170
171 except PExpectError:
172 # recurse on error
173 self._switch_connect()
174 self.vlan_set_name(tag, name)
Steve McIntyrecc297112014-08-11 18:46:58 +0100175
176 # Get a list of the VLAN tags currently registered on the switch
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100177 def vlan_get_list(self):
Steve McIntyrecc297112014-08-11 18:46:58 +0100178 logging.debug("Grabbing list of VLANs")
Steve McIntyrecc297112014-08-11 18:46:58 +0100179
Steve McIntyrec5359e62015-02-12 04:54:07 +0000180 try:
181 vlans = []
182 regex = re.compile('^ *(\d+).*(D|S|G|R)')
Steve McIntyrecc297112014-08-11 18:46:58 +0100183
Steve McIntyrec5359e62015-02-12 04:54:07 +0000184 self._cli("show vlan")
185 for line in self._read_long_output():
186 match = regex.match(line)
187 if match:
188 vlans.append(int(match.group(1)))
189 return vlans
190
191 except PExpectError:
192 # recurse on error
193 self._switch_connect()
194 return self.vlan_get_list()
Steve McIntyrecc297112014-08-11 18:46:58 +0100195
196 # For a given VLAN tag, ask the switch what the associated name is
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100197 def vlan_get_name(self, tag):
Steve McIntyrecc297112014-08-11 18:46:58 +0100198 logging.debug("Grabbing the name of VLAN %d" % tag)
Steve McIntyrec5359e62015-02-12 04:54:07 +0000199
200 try:
201 name = None
202 regex = re.compile('^ *\d+\s+(\S+).*(D|S|G|R)')
203 self._cli("show vlan tag %d" % tag)
204 for line in self._read_long_output():
205 match = regex.match(line)
206 if match:
207 name = match.group(1)
208 name.strip()
209 return name
210
211 except PExpectError:
212 # recurse on error
213 self._switch_connect()
214 return self.vlan_get_name(tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100215
216 ################################
217 ### Port API functions
218 ################################
219
Steve McIntyre9936d002014-10-01 15:54:10 +0100220 # Set the mode of a port: access or trunk
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100221 def port_set_mode(self, port, mode):
Steve McIntyrecc297112014-08-11 18:46:58 +0100222 logging.debug("Setting port %s to %s" % (port, mode))
223 if not self._is_port_mode_valid(mode):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000224 raise InputError("Port mode %s is not allowed" % mode)
Steve McIntyrecc297112014-08-11 18:46:58 +0100225 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000226 raise InputError("Port name %s not recognised" % port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100227
Steve McIntyrec5359e62015-02-12 04:54:07 +0000228 try:
229 self._configure()
230 self._cli("interface %s" % port)
231 self._cli("switchport mode %s" % mode)
232 self._end_configure()
Steve McIntyrecc297112014-08-11 18:46:58 +0100233
Steve McIntyrec5359e62015-02-12 04:54:07 +0000234 # Validate it happened
235 read_mode = self.port_get_mode(port)
236 if read_mode != mode:
237 raise IOError("Failed to set mode for port %s" % port)
238
239 except PExpectError:
240 # recurse on error
241 self._switch_connect()
242 self.port_set_mode(port, mode)
Steve McIntyrecc297112014-08-11 18:46:58 +0100243
Steve McIntyre9936d002014-10-01 15:54:10 +0100244 # Get the mode of a port: access or trunk
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100245 def port_get_mode(self, port):
Steve McIntyrecc297112014-08-11 18:46:58 +0100246 logging.debug("Getting mode of port %s" % port)
247 mode = ''
248 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000249 raise InputError("Port name %s not recognised" % port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100250 regex = re.compile('Port Mode: (\S+)')
Steve McIntyrec5359e62015-02-12 04:54:07 +0000251
252 try:
253 self._cli("show interfaces switchport %s" % port)
254 for line in self._read_long_output():
255 match = regex.match(line)
256 if match:
257 mode = match.group(1)
258 return mode.lower()
259
260 except PExpectError:
261 # recurse on error
262 self._switch_connect()
263 return self.port_get_mode(port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100264
Steve McIntyre9936d002014-10-01 15:54:10 +0100265 # Set an access port to be in a specified VLAN (tag)
266 def port_set_access_vlan(self, port, tag):
267 logging.debug("Setting access port %s to VLAN %d" % (port, tag))
Steve McIntyrecc297112014-08-11 18:46:58 +0100268 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000269 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9936d002014-10-01 15:54:10 +0100270 if not (self.port_get_mode(port) == "access"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000271 raise InputError("Port %s not in access mode" % port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100272
Steve McIntyrec5359e62015-02-12 04:54:07 +0000273 try:
274 self._configure()
275 self._cli("interface %s" % port)
276 self._cli("switchport access vlan %d" % tag)
277 self._end_configure()
Steve McIntyrecc297112014-08-11 18:46:58 +0100278
Steve McIntyrec5359e62015-02-12 04:54:07 +0000279 # Validate things worked
280 read_vlan = int(self.port_get_access_vlan(port))
281 if read_vlan != tag:
282 raise IOError("Failed to move access port %s to VLAN %d - got VLAN %d instead"
283 % (port, tag, read_vlan))
284
285 except PExpectError:
286 # recurse on error
287 self._switch_connect()
288 self.port_set_access_vlan(port, tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100289
Steve McIntyrecc297112014-08-11 18:46:58 +0100290 # Add a trunk port to a specified VLAN (tag)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100291 def port_add_trunk_to_vlan(self, port, tag):
Steve McIntyrecc297112014-08-11 18:46:58 +0100292 logging.debug("Adding trunk port %s to VLAN %d" % (port, tag))
293 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000294 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100295 if not (self.port_get_mode(port) == "trunk"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000296 raise InputError("Port %s not in trunk mode" % port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100297
Steve McIntyrec5359e62015-02-12 04:54:07 +0000298 try:
299 self._configure()
300 self._cli("interface %s" % port)
301 self._cli("switchport trunk allowed vlan add %d" % tag)
302 self._end_configure()
303
304 # Validate it happened
305 read_vlans = self.port_get_trunk_vlan_list(port)
306 for vlan in read_vlans:
307 if vlan == tag:
308 return
309 raise IOError("Failed to add trunk port %s to VLAN %d" % (port, tag))
310
311 except PExpectError:
312 # recurse on error
313 self._switch_connect()
314 self.port_add_trunk_to_vlan(port, tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100315
316 # Remove a trunk port from a specified VLAN (tag)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100317 def port_remove_trunk_from_vlan(self, port, tag):
Steve McIntyrecc297112014-08-11 18:46:58 +0100318 logging.debug("Removing trunk port %s from VLAN %d" % (port, tag))
319 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000320 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100321 if not (self.port_get_mode(port) == "trunk"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000322 raise InputError("Port %s not in trunk mode" % port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100323
Steve McIntyrec5359e62015-02-12 04:54:07 +0000324 try:
325 self._configure()
326 self._cli("interface %s" % port)
327 self._cli("switchport trunk allowed vlan remove %d" % tag)
328 self._end_configure()
329
330 # Validate it happened
331 read_vlans = self.port_get_trunk_vlan_list(port)
332 for vlan in read_vlans:
333 if vlan == tag:
334 raise IOError("Failed to remove trunk port %s from VLAN %d" % (port, tag))
335
336 except PExpectError:
337 # recurse on error
338 self._switch_connect()
339 self.port_remove_trunk_from_vlan(port, tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100340
Steve McIntyre9936d002014-10-01 15:54:10 +0100341 # Get the configured VLAN tag for an access port (tag)
342 def port_get_access_vlan(self, port):
343 logging.debug("Getting VLAN for access port %s" % port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100344 vlan = 1
345 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000346 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9936d002014-10-01 15:54:10 +0100347 if not (self.port_get_mode(port) == "access"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000348 raise InputError("Port %s not in access mode" % port)
Steve McIntyre0f893602014-12-24 02:14:53 +0000349 regex = re.compile('(\d+)\s+\S+\s+Untagged\s+(Static|System)')
Steve McIntyrec5359e62015-02-12 04:54:07 +0000350
351 try:
352 self._cli("show interfaces switchport %s" % port)
353 for line in self._read_long_output():
354 match = regex.match(line)
355 if match:
356 vlan = match.group(1)
357 return int(vlan)
358
359 except PExpectError:
360 # recurse on error
361 self._switch_connect()
362 return self.port_get_access_vlan(port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100363
364 # Get the list of configured VLAN tags for a trunk port
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100365 def port_get_trunk_vlan_list(self, port):
Steve McIntyrecc297112014-08-11 18:46:58 +0100366 logging.debug("Getting VLANs for trunk port %s" % port)
367 vlans = [ ]
368 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000369 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100370 if not (self.port_get_mode(port) == "trunk"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000371 raise InputError("Port %s not in trunk mode" % port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100372 regex = re.compile('(\d+)\s+\S+\s+(Tagged|Untagged)\s+Static')
Steve McIntyrec5359e62015-02-12 04:54:07 +0000373
374 try:
375 self._cli("show interfaces switchport %s" % port)
376 for line in self._read_long_output():
377 match = regex.match(line)
378 if match:
379 vlans.append (int(match.group(1)))
380 return vlans
381
382 except PExpectError:
383 # recurse on error
384 self._switch_connect()
385 return self.port_get_trunk_vlan_list(port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100386
387 ################################
388 ### Internal functions
389 ################################
390
Steve McIntyreb345fc52015-02-12 06:37:05 +0000391 # Connect to the switch and log in
392 def _switch_connect(self):
393
394 if not self.connection is None:
395 self.connection.close(True)
396 self.connection = None
397
398 logging.debug("Connecting to Switch with: %s" % self.exec_string)
399 self.connection = pexpect.spawn(self.exec_string, logfile = self.logfile)
400
401 self._login()
402
403 # Avoid paged output
404 self._cli("terminal datadump")
405
406 # And grab details about the switch. in case we need it
407 self._get_systemdata()
408
409 # And also validate them - make sure we're driving a switch of
410 # the correct model! Also store the serial number
411 descr_regex = re.compile('System Description:.\s+(.*)')
412 sn_regex = re.compile('SN:\s+(\S_)')
413 descr = ""
414
415 for line in self._systemdata:
416 match = descr_regex.match(line)
417 if match:
418 descr = match.group(1)
419 match = sn_regex.match(line)
420 if match:
421 self.serial_number = match.group(1)
422
423 if not self._expected_descr_re.match(descr):
424 raise IOError("Switch %s not recognised by this driver: abort" % descr)
425
426 # Now build a list of our ports, for later sanity checking
427 self._ports = self._get_port_names()
428 if len(self._ports) < 4:
429 raise IOError("Not enough ports detected - problem!")
430
Steve McIntyrebbd2ac82015-02-12 03:08:48 +0000431 def _login(self):
432 logging.debug("attempting login with username %s, password %s" % (self._username, self._password))
Steve McIntyrecc297112014-08-11 18:46:58 +0100433 self._cli("")
434 self.connection.expect("User Name:")
Steve McIntyrebbd2ac82015-02-12 03:08:48 +0000435 self._cli("%s" % self._username)
Steve McIntyrecc297112014-08-11 18:46:58 +0100436 self.connection.expect("Password:")
Steve McIntyrebbd2ac82015-02-12 03:08:48 +0000437 self._cli("%s" % self._password, False)
Steve McIntyreca6c5a32014-12-23 15:34:29 +0000438 self.connection.expect("\*\*")
Steve McIntyrecc297112014-08-11 18:46:58 +0100439 while True:
Steve McIntyre2651aca2015-02-12 05:10:58 +0000440 index = self.connection.expect(['User Name:', 'authentication failed', r'([^#]+)#', 'Password:', '.+'])
Steve McIntyrecc297112014-08-11 18:46:58 +0100441 if index == 0 or index == 1: # Failed to log in!
442 logging.error("Login failure: %s\n" % self.connection.match)
443 raise IOError
444 elif index == 2:
Steve McIntyre366046f2014-08-18 18:57:03 +0100445 self._prompt_name = self.connection.match.group(1).strip()
Steve McIntyre5c51bc52015-02-12 05:34:09 +0000446 # Horrible output from the switch at login time may
447 # confuse our pexpect stuff here. If we've somehow got
448 # multiple lines of output, clean up and just take the
449 # *last* line here. Anything before that is going to
450 # just be noise from the "***" password input, etc.
451 prompt_lines = self._prompt_name.split('\r\n')
452 if len(prompt_lines) > 1:
453 self._prompt_name = prompt_lines[-1]
Steve McIntyreca6c5a32014-12-23 15:34:29 +0000454 logging.debug("Got prompt name %s" % self._prompt_name)
Steve McIntyrecc297112014-08-11 18:46:58 +0100455 return 0
Steve McIntyreca6c5a32014-12-23 15:34:29 +0000456 elif index == 3 or index == 4:
457 self._cli("", False)
Steve McIntyrecc297112014-08-11 18:46:58 +0100458
459 def _logout(self):
460 logging.debug("Logging out")
461 self._cli("exit", False)
462
Steve McIntyrecc297112014-08-11 18:46:58 +0100463 def _configure(self):
464 self._cli("configure terminal")
465
466 def _end_configure(self):
467 self._cli("end")
468
Steve McIntyrec68d6d72014-12-24 00:23:36 +0000469 def _read_long_output(self):
Steve McIntyre366046f2014-08-18 18:57:03 +0100470 prompt = self._prompt_name + '#'
Steve McIntyre2eececb2015-02-12 03:09:17 +0000471 try:
472 self.connection.expect(prompt)
Steve McIntyre1d23b492015-02-12 03:47:24 +0000473 except (pexpect.EOF, pexpect.TIMEOUT):
Steve McIntyred0614fb2015-02-12 03:44:42 +0000474 # Something went wrong; logout, log in and try again!
Steve McIntyre7a9c8192015-02-12 07:15:52 +0000475 logging.error("PEXPECT FAILURE, RECONNECT")
Steve McIntyre3c3f4fc2015-02-12 04:26:27 +0000476 self.errors.log_error_in("")
Steve McIntyre1a3e2f72015-02-12 03:49:05 +0000477 raise PExpectError("_read_long_output failed")
Steve McIntyre2eececb2015-02-12 03:09:17 +0000478 except:
Steve McIntyre7a9c8192015-02-12 07:15:52 +0000479 logging.error("prompt is \"%s\"" % prompt)
Steve McIntyre2eececb2015-02-12 03:09:17 +0000480 raise
Steve McIntyredf6aabf2015-02-12 06:33:19 +0000481
Steve McIntyrea7cdefc2014-12-24 00:48:19 +0000482 buf = []
483 for line in self.connection.before.split('\r\n'):
484 buf.append(line.strip())
485 return buf
Steve McIntyrecc297112014-08-11 18:46:58 +0100486
487 def _get_port_names(self):
488 logging.debug("Grabbing list of ports")
489 interfaces = []
490
491 # Use "Up" or "Down" to only identify lines in the output that
492 # match interfaces that exist
493 regex = re.compile('^(\w+).*(Up|Down)')
494
Steve McIntyre48ab82c2015-02-12 04:59:10 +0000495 try:
496 self._cli("show interfaces status detailed")
497 for line in self._read_long_output():
498 match = regex.match(line)
499 if match:
500 interfaces.append(match.group(1))
501 return interfaces
502 except PExpectError:
503 # recurse on error
504 self._switch_connect()
505 return self._get_port_names()
Steve McIntyrecc297112014-08-11 18:46:58 +0100506
Steve McIntyrecc297112014-08-11 18:46:58 +0100507 def _show_config(self):
508 logging.debug("Grabbing config")
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000509 try:
510 self._cli("show running-config")
511 return self._read_long_output()
512 except PExpectError:
513 # recurse on error
514 self._switch_connect()
515 return self._show_config()
Steve McIntyrecc297112014-08-11 18:46:58 +0100516
517 def _show_clock(self):
518 logging.debug("Grabbing time")
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000519 try:
520 self._cli("show clock")
521 return self._read_long_output()
522 except PExpectError:
523 # recurse on error
524 self._switch_connect()
525 return self._show_clock()
Steve McIntyrecc297112014-08-11 18:46:58 +0100526
Steve McIntyrecc297112014-08-11 18:46:58 +0100527 def _get_systemdata(self):
Steve McIntyredf6aabf2015-02-12 06:33:19 +0000528 logging.debug("Grabbing system data")
Steve McIntyrecc297112014-08-11 18:46:58 +0100529
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000530 try:
531 self._systemdata = []
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000532 self._cli("show system")
533 for line in self._read_long_output():
534 self._systemdata.append(line)
Steve McIntyre28d34ca2014-08-12 15:40:24 +0100535
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000536 logging.debug("Grabbing system sw and hw versions")
537 self._cli("show version")
538 for line in self._read_long_output():
539 self._systemdata.append(line)
540
541 except PExpectError:
542 # recurse on error
543 self._switch_connect()
544 return self._get_systemdata()
Steve McIntyrecc297112014-08-11 18:46:58 +0100545
Steve McIntyre16f02162014-08-14 17:47:36 +0100546 ######################################
547 # Internal port access helper methods
548 ######################################
549 # N.B. No parameter checking here, for speed reasons - if you're
550 # calling this internal API then you should already have validated
551 # things yourself! Equally, no post-set checks in here - do that
552 # at the higher level.
553 ######################################
554
Steve McIntyrecc297112014-08-11 18:46:58 +0100555 # Wrapper around connection.send - by default, expect() the same
556 # text we've sent, to remove it from the output from the
557 # switch. For the few cases where we don't need that, override
558 # this using echo=False.
559 # Horrible, but seems to work.
560 def _cli(self, text, echo=True):
561 self.connection.send(text + '\r')
562 if echo:
Steve McIntyre9f7f03b2015-02-12 03:10:13 +0000563 try:
564 self.connection.expect(text)
Steve McIntyre1d23b492015-02-12 03:47:24 +0000565 except (pexpect.EOF, pexpect.TIMEOUT):
Steve McIntyre9f7f03b2015-02-12 03:10:13 +0000566 # Something went wrong; logout, log in and try again!
Steve McIntyre7a9c8192015-02-12 07:15:52 +0000567 logging.error("PEXPECT FAILURE, RECONNECT")
Steve McIntyre3c3f4fc2015-02-12 04:26:27 +0000568 self.errors.log_error_out(text)
Steve McIntyre729d9502015-02-12 03:14:10 +0000569 raise PExpectError("_cli failed on %s" % text)
Steve McIntyre9f7f03b2015-02-12 03:10:13 +0000570 except:
Steve McIntyrea67473a2015-02-12 08:26:33 +0000571 logging.error("Unexpected error: %s", sys.exc_info()[0])
Steve McIntyre9f7f03b2015-02-12 03:10:13 +0000572 raise
Steve McIntyrecc297112014-08-11 18:46:58 +0100573
574if __name__ == "__main__":
Steve McIntyref1699322014-08-19 22:49:43 +0100575# p = CiscoSX300('10.172.2.52', 23)
Steve McIntyre48dc6ae2014-12-23 16:08:19 +0000576
577 import optparse
578
579 switch = 'vlandswitch02'
580 parser = optparse.OptionParser()
581 parser.add_option("--switch",
582 dest = "switch",
583 action = "store",
584 nargs = 1,
585 type = "string",
586 help = "specify switch to connect to for testing",
587 metavar = "<switch>")
588 (opts, args) = parser.parse_args()
589 if opts.switch:
590 switch = opts.switch
591
592 p = CiscoSX300(switch, 23, debug = True)
Steve McIntyreaea995c2014-12-22 17:17:38 +0000593 p.switch_connect('cisco', 'cisco', None)
Steve McIntyrecc297112014-08-11 18:46:58 +0100594 #buf = p._show_clock()
595 #print "%s" % buf
596 #buf = p._show_config()
597 #p._dump_list(buf)
598
Steve McIntyre366046f2014-08-18 18:57:03 +0100599 print "System data:"
600 p._dump_list(p._systemdata)
Steve McIntyrecc297112014-08-11 18:46:58 +0100601
602 print "Creating VLANs for testing:"
603 for i in [ 2, 3, 4, 5, 20 ]:
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100604 p.vlan_create(i)
605 p.vlan_set_name(i, "test%d" % i)
Steve McIntyrecc297112014-08-11 18:46:58 +0100606 print " %d (test%d)" % (i, i)
607
608 #print "And dump config\n"
609 #buf = p._show_config()
610 #print "%s" % buf
611
612 #print "Destroying VLAN 2\n"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100613 #p.vlan_destroy(2)
Steve McIntyrecc297112014-08-11 18:46:58 +0100614
615 #print "And dump config\n"
616 #buf = p._show_config()
617 #print "%s" % buf
618
619 #print "Port names are:"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100620 #buf = p.switch_get_port_names()
Steve McIntyrecc297112014-08-11 18:46:58 +0100621 #p._dump_list(buf)
622
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100623 #buf = p.vlan_get_name(25)
Steve McIntyrecc297112014-08-11 18:46:58 +0100624 #print "VLAN with tag 25 is called \"%s\"" % buf
625
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100626 #p.vlan_set_name(35, "foo")
Steve McIntyrecc297112014-08-11 18:46:58 +0100627 #print "VLAN with tag 35 is called \"foo\""
628
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100629 #buf = p.port_get_mode("fa12")
Steve McIntyrecc297112014-08-11 18:46:58 +0100630 #print "Port fa12 is in %s mode" % buf
631
Steve McIntyre9936d002014-10-01 15:54:10 +0100632 # Test access stuff
633 print "Set fa6 to access mode"
634 p.port_set_mode("fa6", "access")
Steve McIntyre16f02162014-08-14 17:47:36 +0100635 print "Move fa6 to VLAN 2"
Steve McIntyre9936d002014-10-01 15:54:10 +0100636 p.port_set_access_vlan("fa6", 2)
637 buf = p.port_get_access_vlan("fa6")
Steve McIntyrecc297112014-08-11 18:46:58 +0100638 print "Read from switch: fa6 is on VLAN %s" % buf
Steve McIntyre16f02162014-08-14 17:47:36 +0100639 print "Move fa6 back to default VLAN 1"
Steve McIntyre9936d002014-10-01 15:54:10 +0100640 p.port_set_access_vlan("fa6", 1)
Steve McIntyrecc297112014-08-11 18:46:58 +0100641 #print "And move fa6 back to a trunk port"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100642 #p.port_set_mode("fa6", "trunk")
643 #buf = p.port_get_mode("fa6")
Steve McIntyrecc297112014-08-11 18:46:58 +0100644 #print "Port fa6 is in %s mode" % buf
645
646 # Test trunk stuff
647 print "Set gi2 to trunk mode"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100648 p.port_set_mode("gi2", "trunk")
Steve McIntyrecc297112014-08-11 18:46:58 +0100649 print "Add gi2 to VLAN 2"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100650 p.port_add_trunk_to_vlan("gi2", 2)
Steve McIntyrecc297112014-08-11 18:46:58 +0100651 print "Add gi2 to VLAN 3"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100652 p.port_add_trunk_to_vlan("gi2", 3)
Steve McIntyrecc297112014-08-11 18:46:58 +0100653 print "Add gi2 to VLAN 4"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100654 p.port_add_trunk_to_vlan("gi2", 4)
Steve McIntyrecc297112014-08-11 18:46:58 +0100655 print "Read from switch: which VLANs is gi2 on?"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100656 buf = p.port_get_trunk_vlan_list("gi2")
Steve McIntyrecc297112014-08-11 18:46:58 +0100657 p._dump_list(buf)
658
Steve McIntyre366046f2014-08-18 18:57:03 +0100659 print "Remove gi2 from VLANs 3,3,4"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100660 p.port_remove_trunk_from_vlan("gi2", 3)
661 p.port_remove_trunk_from_vlan("gi2", 3)
662 p.port_remove_trunk_from_vlan("gi2", 4)
Steve McIntyrecc297112014-08-11 18:46:58 +0100663 print "Read from switch: which VLANs is gi2 on?"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100664 buf = p.port_get_trunk_vlan_list("gi2")
Steve McIntyrecc297112014-08-11 18:46:58 +0100665 p._dump_list(buf)
666
667 # print "Adding lots of ports to VLANs"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100668 # p.port_add_trunk_to_vlan("fa1", 2)
669 # p.port_add_trunk_to_vlan("fa3", 2)
670 # p.port_add_trunk_to_vlan("fa5", 2)
671 # p.port_add_trunk_to_vlan("fa7", 2)
672 # p.port_add_trunk_to_vlan("fa9", 2)
673 # p.port_add_trunk_to_vlan("fa11", 2)
674 # p.port_add_trunk_to_vlan("fa13", 2)
675 # p.port_add_trunk_to_vlan("fa15", 2)
676 # p.port_add_trunk_to_vlan("fa17", 2)
677 # p.port_add_trunk_to_vlan("fa19", 2)
678 # p.port_add_trunk_to_vlan("fa21", 2)
679 # p.port_add_trunk_to_vlan("fa23", 2)
680 # p.port_add_trunk_to_vlan("gi4", 2)
Steve McIntyrecc297112014-08-11 18:46:58 +0100681
682 print "VLANs are:"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100683 buf = p.vlan_get_list()
Steve McIntyrecc297112014-08-11 18:46:58 +0100684 p._dump_list(buf)
685
Steve McIntyre7460d972014-12-23 14:45:30 +0000686# print 'Restarting switch, to explicitly reset config'
687# p.switch_restart()
Steve McIntyrecc297112014-08-11 18:46:58 +0100688
Steve McIntyrebf5dc882014-12-19 17:54:58 +0000689# p.switch_save_running_config()
Steve McIntyrecc297112014-08-11 18:46:58 +0100690# p._show_config()
691