blob: dae5708b7fe730a01ed1f805c0d6921df5e8abbb [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
68 # Connect to the switch and log in
69 def _switch_connect(self):
70
71 if not self.connection is None:
72 self.connection.close(True)
73 self.connection = None
74
75 logging.debug("Connecting to Switch with: %s" % self.exec_string)
76 self.connection = pexpect.spawn(self.exec_string, logfile = self.logfile)
Steve McIntyrebbd2ac82015-02-12 03:08:48 +000077
78 self._login()
Steve McIntyrecc297112014-08-11 18:46:58 +010079
Steve McIntyredd0c0012014-12-24 00:10:17 +000080 # Avoid paged output
81 self._cli("terminal datadump")
Steve McIntyre7ced0732014-08-12 18:07:56 +010082
Steve McIntyrecc297112014-08-11 18:46:58 +010083 # And grab details about the switch. in case we need it
Steve McIntyre366046f2014-08-18 18:57:03 +010084 self._get_systemdata()
Steve McIntyrecc297112014-08-11 18:46:58 +010085
86 # And also validate them - make sure we're driving a switch of
87 # the correct model! Also store the serial number
Steve McIntyreb9783ff2014-12-23 16:36:35 +000088 descr_regex = re.compile('System Description:.\s+(.*)')
Steve McIntyrecc297112014-08-11 18:46:58 +010089 sn_regex = re.compile('SN:\s+(\S_)')
90 descr = ""
91
Steve McIntyre366046f2014-08-18 18:57:03 +010092 for line in self._systemdata:
Steve McIntyrecc297112014-08-11 18:46:58 +010093 match = descr_regex.match(line)
94 if match:
95 descr = match.group(1)
96 match = sn_regex.match(line)
97 if match:
98 self.serial_number = match.group(1)
99
Steve McIntyre366046f2014-08-18 18:57:03 +0100100 if not self._expected_descr_re.match(descr):
Steve McIntyrecc297112014-08-11 18:46:58 +0100101 raise IOError("Switch %s not recognised by this driver: abort" % descr)
102
Steve McIntyre28d34ca2014-08-12 15:40:24 +0100103 # Now build a list of our ports, for later sanity checking
Steve McIntyre366046f2014-08-18 18:57:03 +0100104 self._ports = self._get_port_names()
105 if len(self._ports) < 4:
Steve McIntyre28d34ca2014-08-12 15:40:24 +0100106 raise IOError("Not enough ports detected - problem!")
107
Steve McIntyrecc297112014-08-11 18:46:58 +0100108 # Log out of the switch and drop the connection and all state
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100109 def switch_disconnect(self):
Steve McIntyrecc297112014-08-11 18:46:58 +0100110 self._logout()
111 logging.debug("Closing connection: %s" % self.connection)
112 self.connection.close(True)
Steve McIntyre11393992014-10-10 15:53:34 +0100113 self._ports = []
114 self._prompt_name = ''
115 self._systemdata = []
Steve McIntyrecc297112014-08-11 18:46:58 +0100116 del(self)
117
118 # Save the current running config into flash - we want config to
119 # remain across reboots
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100120 def switch_save_running_config(self):
Steve McIntyrec5359e62015-02-12 04:54:07 +0000121 try:
122 self._cli("copy running-config startup-config")
123 self.connection.expect("Y/N")
124 self._cli("y")
125 self.connection.expect("succeeded")
126 except PExpectError:
127 # recurse on error
128 self._switch_connect()
129 self.switch_save_running_config()
Steve McIntyrecc297112014-08-11 18:46:58 +0100130
Steve McIntyre095b4452014-12-19 17:53:43 +0000131 # Restart the switch - we need to reload config to do a
132 # roll-back. Do NOT save running-config first if the switch asks -
133 # we're trying to dump recent changes, not save them.
134 #
135 # This will also implicitly cause a connection to be closed
136 def switch_restart(self):
137 self._cli("reload")
138 index = self.connection.expect(['Are you sure', 'will reset'])
139 if index == 0:
140 self._cli("y") # Yes, continue without saving
141 self.connection.expect("reset the whole")
142
143 # Fall through
144 self._cli("y") # Yes, continue to reset
145 self.connection.close(True)
146
Steve McIntyrecc297112014-08-11 18:46:58 +0100147 ################################
148 ### VLAN API functions
149 ################################
150
151 # Create a VLAN with the specified tag
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100152 def vlan_create(self, tag):
Steve McIntyrecc297112014-08-11 18:46:58 +0100153 logging.debug("Creating VLAN %d" % tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100154
Steve McIntyrec5359e62015-02-12 04:54:07 +0000155 try:
156 self._configure()
157 self._cli("vlan database")
158 self._cli("vlan %d" % tag)
159 self._end_configure()
160
161 # Validate it happened
162 vlans = self.vlan_get_list()
163 for vlan in vlans:
164 if vlan == tag:
165 return
166 raise IOError("Failed to create VLAN %d" % tag)
167
168 except PExpectError:
169 # recurse on error
170 self._switch_connect()
171 self.vlan_create(tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100172
173 # Destroy a VLAN with the specified tag
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100174 def vlan_destroy(self, tag):
Steve McIntyrecc297112014-08-11 18:46:58 +0100175 logging.debug("Destroying VLAN %d" % tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100176
Steve McIntyrec5359e62015-02-12 04:54:07 +0000177 try:
178 self._configure()
179 self._cli("no vlan %d" % tag)
180 self._end_configure()
181
182 # Validate it happened
183 vlans = self.vlan_get_list()
184 for vlan in vlans:
185 if vlan == tag:
186 raise IOError("Failed to destroy VLAN %d" % tag)
187
188 except PExpectError:
189 # recurse on error
190 self._switch_connect()
191 self.vlan_destroy(tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100192
193 # Set the name of a VLAN
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100194 def vlan_set_name(self, tag, name):
Steve McIntyrecc297112014-08-11 18:46:58 +0100195 logging.debug("Setting name of VLAN %d to %s" % (tag, name))
Steve McIntyrecc297112014-08-11 18:46:58 +0100196
Steve McIntyrec5359e62015-02-12 04:54:07 +0000197 try:
198 self._configure()
199 self._cli("vlan %d" % tag)
200 self._cli("interface vlan %d" % tag)
201 self._cli("name %s" % name)
202 self._end_configure()
203
204 # Validate it happened
205 read_name = self.vlan_get_name(tag)
206 if read_name != name:
207 raise IOError("Failed to set name for VLAN %d (name found is \"%s\", not \"%s\")"
208 % (tag, read_name, name))
209
210 except PExpectError:
211 # recurse on error
212 self._switch_connect()
213 self.vlan_set_name(tag, name)
Steve McIntyrecc297112014-08-11 18:46:58 +0100214
215 # Get a list of the VLAN tags currently registered on the switch
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100216 def vlan_get_list(self):
Steve McIntyrecc297112014-08-11 18:46:58 +0100217 logging.debug("Grabbing list of VLANs")
Steve McIntyrecc297112014-08-11 18:46:58 +0100218
Steve McIntyrec5359e62015-02-12 04:54:07 +0000219 try:
220 vlans = []
221 regex = re.compile('^ *(\d+).*(D|S|G|R)')
Steve McIntyrecc297112014-08-11 18:46:58 +0100222
Steve McIntyrec5359e62015-02-12 04:54:07 +0000223 self._cli("show vlan")
224 for line in self._read_long_output():
225 match = regex.match(line)
226 if match:
227 vlans.append(int(match.group(1)))
228 return vlans
229
230 except PExpectError:
231 # recurse on error
232 self._switch_connect()
233 return self.vlan_get_list()
Steve McIntyrecc297112014-08-11 18:46:58 +0100234
235 # For a given VLAN tag, ask the switch what the associated name is
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100236 def vlan_get_name(self, tag):
Steve McIntyrecc297112014-08-11 18:46:58 +0100237 logging.debug("Grabbing the name of VLAN %d" % tag)
Steve McIntyrec5359e62015-02-12 04:54:07 +0000238
239 try:
240 name = None
241 regex = re.compile('^ *\d+\s+(\S+).*(D|S|G|R)')
242 self._cli("show vlan tag %d" % tag)
243 for line in self._read_long_output():
244 match = regex.match(line)
245 if match:
246 name = match.group(1)
247 name.strip()
248 return name
249
250 except PExpectError:
251 # recurse on error
252 self._switch_connect()
253 return self.vlan_get_name(tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100254
255 ################################
256 ### Port API functions
257 ################################
258
Steve McIntyre9936d002014-10-01 15:54:10 +0100259 # Set the mode of a port: access or trunk
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100260 def port_set_mode(self, port, mode):
Steve McIntyrecc297112014-08-11 18:46:58 +0100261 logging.debug("Setting port %s to %s" % (port, mode))
262 if not self._is_port_mode_valid(mode):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000263 raise InputError("Port mode %s is not allowed" % mode)
Steve McIntyrecc297112014-08-11 18:46:58 +0100264 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000265 raise InputError("Port name %s not recognised" % port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100266
Steve McIntyrec5359e62015-02-12 04:54:07 +0000267 try:
268 self._configure()
269 self._cli("interface %s" % port)
270 self._cli("switchport mode %s" % mode)
271 self._end_configure()
Steve McIntyrecc297112014-08-11 18:46:58 +0100272
Steve McIntyrec5359e62015-02-12 04:54:07 +0000273 # Validate it happened
274 read_mode = self.port_get_mode(port)
275 if read_mode != mode:
276 raise IOError("Failed to set mode for port %s" % port)
277
278 except PExpectError:
279 # recurse on error
280 self._switch_connect()
281 self.port_set_mode(port, mode)
Steve McIntyrecc297112014-08-11 18:46:58 +0100282
Steve McIntyre9936d002014-10-01 15:54:10 +0100283 # Get the mode of a port: access or trunk
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100284 def port_get_mode(self, port):
Steve McIntyrecc297112014-08-11 18:46:58 +0100285 logging.debug("Getting mode of port %s" % port)
286 mode = ''
287 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000288 raise InputError("Port name %s not recognised" % port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100289 regex = re.compile('Port Mode: (\S+)')
Steve McIntyrec5359e62015-02-12 04:54:07 +0000290
291 try:
292 self._cli("show interfaces switchport %s" % port)
293 for line in self._read_long_output():
294 match = regex.match(line)
295 if match:
296 mode = match.group(1)
297 return mode.lower()
298
299 except PExpectError:
300 # recurse on error
301 self._switch_connect()
302 return self.port_get_mode(port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100303
Steve McIntyre9936d002014-10-01 15:54:10 +0100304 # Set an access port to be in a specified VLAN (tag)
305 def port_set_access_vlan(self, port, tag):
306 logging.debug("Setting access port %s to VLAN %d" % (port, tag))
Steve McIntyrecc297112014-08-11 18:46:58 +0100307 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000308 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9936d002014-10-01 15:54:10 +0100309 if not (self.port_get_mode(port) == "access"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000310 raise InputError("Port %s not in access mode" % port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100311
Steve McIntyrec5359e62015-02-12 04:54:07 +0000312 try:
313 self._configure()
314 self._cli("interface %s" % port)
315 self._cli("switchport access vlan %d" % tag)
316 self._end_configure()
Steve McIntyrecc297112014-08-11 18:46:58 +0100317
Steve McIntyrec5359e62015-02-12 04:54:07 +0000318 # Validate things worked
319 read_vlan = int(self.port_get_access_vlan(port))
320 if read_vlan != tag:
321 raise IOError("Failed to move access port %s to VLAN %d - got VLAN %d instead"
322 % (port, tag, read_vlan))
323
324 except PExpectError:
325 # recurse on error
326 self._switch_connect()
327 self.port_set_access_vlan(port, tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100328
Steve McIntyrecc297112014-08-11 18:46:58 +0100329 # Add a trunk port to a specified VLAN (tag)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100330 def port_add_trunk_to_vlan(self, port, tag):
Steve McIntyrecc297112014-08-11 18:46:58 +0100331 logging.debug("Adding trunk port %s to VLAN %d" % (port, tag))
332 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000333 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100334 if not (self.port_get_mode(port) == "trunk"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000335 raise InputError("Port %s not in trunk mode" % port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100336
Steve McIntyrec5359e62015-02-12 04:54:07 +0000337 try:
338 self._configure()
339 self._cli("interface %s" % port)
340 self._cli("switchport trunk allowed vlan add %d" % tag)
341 self._end_configure()
342
343 # Validate it happened
344 read_vlans = self.port_get_trunk_vlan_list(port)
345 for vlan in read_vlans:
346 if vlan == tag:
347 return
348 raise IOError("Failed to add trunk port %s to VLAN %d" % (port, tag))
349
350 except PExpectError:
351 # recurse on error
352 self._switch_connect()
353 self.port_add_trunk_to_vlan(port, tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100354
355 # Remove a trunk port from a specified VLAN (tag)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100356 def port_remove_trunk_from_vlan(self, port, tag):
Steve McIntyrecc297112014-08-11 18:46:58 +0100357 logging.debug("Removing trunk port %s from VLAN %d" % (port, tag))
358 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000359 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100360 if not (self.port_get_mode(port) == "trunk"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000361 raise InputError("Port %s not in trunk mode" % port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100362
Steve McIntyrec5359e62015-02-12 04:54:07 +0000363 try:
364 self._configure()
365 self._cli("interface %s" % port)
366 self._cli("switchport trunk allowed vlan remove %d" % tag)
367 self._end_configure()
368
369 # Validate it happened
370 read_vlans = self.port_get_trunk_vlan_list(port)
371 for vlan in read_vlans:
372 if vlan == tag:
373 raise IOError("Failed to remove trunk port %s from VLAN %d" % (port, tag))
374
375 except PExpectError:
376 # recurse on error
377 self._switch_connect()
378 self.port_remove_trunk_from_vlan(port, tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100379
Steve McIntyre9936d002014-10-01 15:54:10 +0100380 # Get the configured VLAN tag for an access port (tag)
381 def port_get_access_vlan(self, port):
382 logging.debug("Getting VLAN for access port %s" % port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100383 vlan = 1
384 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000385 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9936d002014-10-01 15:54:10 +0100386 if not (self.port_get_mode(port) == "access"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000387 raise InputError("Port %s not in access mode" % port)
Steve McIntyre0f893602014-12-24 02:14:53 +0000388 regex = re.compile('(\d+)\s+\S+\s+Untagged\s+(Static|System)')
Steve McIntyrec5359e62015-02-12 04:54:07 +0000389
390 try:
391 self._cli("show interfaces switchport %s" % port)
392 for line in self._read_long_output():
393 match = regex.match(line)
394 if match:
395 vlan = match.group(1)
396 return int(vlan)
397
398 except PExpectError:
399 # recurse on error
400 self._switch_connect()
401 return self.port_get_access_vlan(port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100402
403 # Get the list of configured VLAN tags for a trunk port
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100404 def port_get_trunk_vlan_list(self, port):
Steve McIntyrecc297112014-08-11 18:46:58 +0100405 logging.debug("Getting VLANs for trunk port %s" % port)
406 vlans = [ ]
407 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000408 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100409 if not (self.port_get_mode(port) == "trunk"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000410 raise InputError("Port %s not in trunk mode" % port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100411 regex = re.compile('(\d+)\s+\S+\s+(Tagged|Untagged)\s+Static')
Steve McIntyrec5359e62015-02-12 04:54:07 +0000412
413 try:
414 self._cli("show interfaces switchport %s" % port)
415 for line in self._read_long_output():
416 match = regex.match(line)
417 if match:
418 vlans.append (int(match.group(1)))
419 return vlans
420
421 except PExpectError:
422 # recurse on error
423 self._switch_connect()
424 return self.port_get_trunk_vlan_list(port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100425
426 ################################
427 ### Internal functions
428 ################################
429
Steve McIntyre4aaec3d2015-02-12 04:54:28 +0000430 def _hex(self, s):
431 return ':'.join(x.encode('hex') for x in s)
432
Steve McIntyrebbd2ac82015-02-12 03:08:48 +0000433 def _login(self):
434 logging.debug("attempting login with username %s, password %s" % (self._username, self._password))
Steve McIntyrecc297112014-08-11 18:46:58 +0100435 self._cli("")
436 self.connection.expect("User Name:")
Steve McIntyrebbd2ac82015-02-12 03:08:48 +0000437 self._cli("%s" % self._username)
Steve McIntyrecc297112014-08-11 18:46:58 +0100438 self.connection.expect("Password:")
Steve McIntyrebbd2ac82015-02-12 03:08:48 +0000439 self._cli("%s" % self._password, False)
Steve McIntyreca6c5a32014-12-23 15:34:29 +0000440 self.connection.expect("\*\*")
Steve McIntyrecc297112014-08-11 18:46:58 +0100441 while True:
Steve McIntyre2651aca2015-02-12 05:10:58 +0000442 index = self.connection.expect(['User Name:', 'authentication failed', r'([^#]+)#', 'Password:', '.+'])
Steve McIntyrecc297112014-08-11 18:46:58 +0100443 if index == 0 or index == 1: # Failed to log in!
444 logging.error("Login failure: %s\n" % self.connection.match)
445 raise IOError
446 elif index == 2:
Steve McIntyre366046f2014-08-18 18:57:03 +0100447 self._prompt_name = self.connection.match.group(1).strip()
Steve McIntyre5c51bc52015-02-12 05:34:09 +0000448 # Horrible output from the switch at login time may
449 # confuse our pexpect stuff here. If we've somehow got
450 # multiple lines of output, clean up and just take the
451 # *last* line here. Anything before that is going to
452 # just be noise from the "***" password input, etc.
453 prompt_lines = self._prompt_name.split('\r\n')
454 if len(prompt_lines) > 1:
455 self._prompt_name = prompt_lines[-1]
Steve McIntyreca6c5a32014-12-23 15:34:29 +0000456 logging.debug("Got prompt name %s" % self._prompt_name)
Steve McIntyrecc297112014-08-11 18:46:58 +0100457 return 0
Steve McIntyreca6c5a32014-12-23 15:34:29 +0000458 elif index == 3 or index == 4:
459 self._cli("", False)
Steve McIntyrecc297112014-08-11 18:46:58 +0100460
461 def _logout(self):
462 logging.debug("Logging out")
463 self._cli("exit", False)
464
Steve McIntyrecc297112014-08-11 18:46:58 +0100465 def _configure(self):
466 self._cli("configure terminal")
467
468 def _end_configure(self):
469 self._cli("end")
470
Steve McIntyrec68d6d72014-12-24 00:23:36 +0000471 def _read_long_output(self):
Steve McIntyre366046f2014-08-18 18:57:03 +0100472 prompt = self._prompt_name + '#'
Steve McIntyre2eececb2015-02-12 03:09:17 +0000473 try:
474 self.connection.expect(prompt)
Steve McIntyre1d23b492015-02-12 03:47:24 +0000475 except (pexpect.EOF, pexpect.TIMEOUT):
Steve McIntyred0614fb2015-02-12 03:44:42 +0000476 # Something went wrong; logout, log in and try again!
477 print "PEXPECT FAILURE, RECONNECT"
Steve McIntyre3c3f4fc2015-02-12 04:26:27 +0000478 self.errors.log_error_in("")
Steve McIntyre1a3e2f72015-02-12 03:49:05 +0000479 raise PExpectError("_read_long_output failed")
Steve McIntyre2eececb2015-02-12 03:09:17 +0000480 except:
481 print "prompt is \"%s\"" % prompt
482 raise
Steve McIntyrea7cdefc2014-12-24 00:48:19 +0000483 buf = []
484 for line in self.connection.before.split('\r\n'):
485 buf.append(line.strip())
486 return buf
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 McIntyrecc297112014-08-11 18:46:58 +0100529
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000530 try:
531 self._systemdata = []
Steve McIntyreffb9b5a2014-10-10 16:31:58 +0100532
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000533 logging.debug("Grabbing system data")
534 self._cli("show system")
535 for line in self._read_long_output():
536 self._systemdata.append(line)
Steve McIntyre28d34ca2014-08-12 15:40:24 +0100537
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000538 logging.debug("Grabbing system sw and hw versions")
539 self._cli("show version")
540 for line in self._read_long_output():
541 self._systemdata.append(line)
542
543 except PExpectError:
544 # recurse on error
545 self._switch_connect()
546 return self._get_systemdata()
Steve McIntyrecc297112014-08-11 18:46:58 +0100547
Steve McIntyre16f02162014-08-14 17:47:36 +0100548 ######################################
549 # Internal port access helper methods
550 ######################################
551 # N.B. No parameter checking here, for speed reasons - if you're
552 # calling this internal API then you should already have validated
553 # things yourself! Equally, no post-set checks in here - do that
554 # at the higher level.
555 ######################################
556
Steve McIntyrecc297112014-08-11 18:46:58 +0100557 # Wrapper around connection.send - by default, expect() the same
558 # text we've sent, to remove it from the output from the
559 # switch. For the few cases where we don't need that, override
560 # this using echo=False.
561 # Horrible, but seems to work.
562 def _cli(self, text, echo=True):
563 self.connection.send(text + '\r')
564 if echo:
Steve McIntyre9f7f03b2015-02-12 03:10:13 +0000565 try:
566 self.connection.expect(text)
Steve McIntyre1d23b492015-02-12 03:47:24 +0000567 except (pexpect.EOF, pexpect.TIMEOUT):
Steve McIntyre9f7f03b2015-02-12 03:10:13 +0000568 # Something went wrong; logout, log in and try again!
569 print "PEXPECT FAILURE, RECONNECT"
Steve McIntyre3c3f4fc2015-02-12 04:26:27 +0000570 self.errors.log_error_out(text)
Steve McIntyre729d9502015-02-12 03:14:10 +0000571 raise PExpectError("_cli failed on %s" % text)
Steve McIntyre9f7f03b2015-02-12 03:10:13 +0000572 except:
573 print "Unexpected error:", sys.exc_info()[0]
574 raise
Steve McIntyrecc297112014-08-11 18:46:58 +0100575
576if __name__ == "__main__":
Steve McIntyref1699322014-08-19 22:49:43 +0100577# p = CiscoSX300('10.172.2.52', 23)
Steve McIntyre48dc6ae2014-12-23 16:08:19 +0000578
579 import optparse
580
581 switch = 'vlandswitch02'
582 parser = optparse.OptionParser()
583 parser.add_option("--switch",
584 dest = "switch",
585 action = "store",
586 nargs = 1,
587 type = "string",
588 help = "specify switch to connect to for testing",
589 metavar = "<switch>")
590 (opts, args) = parser.parse_args()
591 if opts.switch:
592 switch = opts.switch
593
594 p = CiscoSX300(switch, 23, debug = True)
Steve McIntyreaea995c2014-12-22 17:17:38 +0000595 p.switch_connect('cisco', 'cisco', None)
Steve McIntyrecc297112014-08-11 18:46:58 +0100596 #buf = p._show_clock()
597 #print "%s" % buf
598 #buf = p._show_config()
599 #p._dump_list(buf)
600
Steve McIntyre366046f2014-08-18 18:57:03 +0100601 print "System data:"
602 p._dump_list(p._systemdata)
Steve McIntyrecc297112014-08-11 18:46:58 +0100603
604 print "Creating VLANs for testing:"
605 for i in [ 2, 3, 4, 5, 20 ]:
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100606 p.vlan_create(i)
607 p.vlan_set_name(i, "test%d" % i)
Steve McIntyrecc297112014-08-11 18:46:58 +0100608 print " %d (test%d)" % (i, i)
609
610 #print "And dump config\n"
611 #buf = p._show_config()
612 #print "%s" % buf
613
614 #print "Destroying VLAN 2\n"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100615 #p.vlan_destroy(2)
Steve McIntyrecc297112014-08-11 18:46:58 +0100616
617 #print "And dump config\n"
618 #buf = p._show_config()
619 #print "%s" % buf
620
621 #print "Port names are:"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100622 #buf = p.switch_get_port_names()
Steve McIntyrecc297112014-08-11 18:46:58 +0100623 #p._dump_list(buf)
624
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100625 #buf = p.vlan_get_name(25)
Steve McIntyrecc297112014-08-11 18:46:58 +0100626 #print "VLAN with tag 25 is called \"%s\"" % buf
627
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100628 #p.vlan_set_name(35, "foo")
Steve McIntyrecc297112014-08-11 18:46:58 +0100629 #print "VLAN with tag 35 is called \"foo\""
630
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100631 #buf = p.port_get_mode("fa12")
Steve McIntyrecc297112014-08-11 18:46:58 +0100632 #print "Port fa12 is in %s mode" % buf
633
Steve McIntyre9936d002014-10-01 15:54:10 +0100634 # Test access stuff
635 print "Set fa6 to access mode"
636 p.port_set_mode("fa6", "access")
Steve McIntyre16f02162014-08-14 17:47:36 +0100637 print "Move fa6 to VLAN 2"
Steve McIntyre9936d002014-10-01 15:54:10 +0100638 p.port_set_access_vlan("fa6", 2)
639 buf = p.port_get_access_vlan("fa6")
Steve McIntyrecc297112014-08-11 18:46:58 +0100640 print "Read from switch: fa6 is on VLAN %s" % buf
Steve McIntyre16f02162014-08-14 17:47:36 +0100641 print "Move fa6 back to default VLAN 1"
Steve McIntyre9936d002014-10-01 15:54:10 +0100642 p.port_set_access_vlan("fa6", 1)
Steve McIntyrecc297112014-08-11 18:46:58 +0100643 #print "And move fa6 back to a trunk port"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100644 #p.port_set_mode("fa6", "trunk")
645 #buf = p.port_get_mode("fa6")
Steve McIntyrecc297112014-08-11 18:46:58 +0100646 #print "Port fa6 is in %s mode" % buf
647
648 # Test trunk stuff
649 print "Set gi2 to trunk mode"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100650 p.port_set_mode("gi2", "trunk")
Steve McIntyrecc297112014-08-11 18:46:58 +0100651 print "Add gi2 to VLAN 2"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100652 p.port_add_trunk_to_vlan("gi2", 2)
Steve McIntyrecc297112014-08-11 18:46:58 +0100653 print "Add gi2 to VLAN 3"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100654 p.port_add_trunk_to_vlan("gi2", 3)
Steve McIntyrecc297112014-08-11 18:46:58 +0100655 print "Add gi2 to VLAN 4"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100656 p.port_add_trunk_to_vlan("gi2", 4)
Steve McIntyrecc297112014-08-11 18:46:58 +0100657 print "Read from switch: which VLANs is gi2 on?"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100658 buf = p.port_get_trunk_vlan_list("gi2")
Steve McIntyrecc297112014-08-11 18:46:58 +0100659 p._dump_list(buf)
660
Steve McIntyre366046f2014-08-18 18:57:03 +0100661 print "Remove gi2 from VLANs 3,3,4"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100662 p.port_remove_trunk_from_vlan("gi2", 3)
663 p.port_remove_trunk_from_vlan("gi2", 3)
664 p.port_remove_trunk_from_vlan("gi2", 4)
Steve McIntyrecc297112014-08-11 18:46:58 +0100665 print "Read from switch: which VLANs is gi2 on?"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100666 buf = p.port_get_trunk_vlan_list("gi2")
Steve McIntyrecc297112014-08-11 18:46:58 +0100667 p._dump_list(buf)
668
669 # print "Adding lots of ports to VLANs"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100670 # p.port_add_trunk_to_vlan("fa1", 2)
671 # p.port_add_trunk_to_vlan("fa3", 2)
672 # p.port_add_trunk_to_vlan("fa5", 2)
673 # p.port_add_trunk_to_vlan("fa7", 2)
674 # p.port_add_trunk_to_vlan("fa9", 2)
675 # p.port_add_trunk_to_vlan("fa11", 2)
676 # p.port_add_trunk_to_vlan("fa13", 2)
677 # p.port_add_trunk_to_vlan("fa15", 2)
678 # p.port_add_trunk_to_vlan("fa17", 2)
679 # p.port_add_trunk_to_vlan("fa19", 2)
680 # p.port_add_trunk_to_vlan("fa21", 2)
681 # p.port_add_trunk_to_vlan("fa23", 2)
682 # p.port_add_trunk_to_vlan("gi4", 2)
Steve McIntyrecc297112014-08-11 18:46:58 +0100683
684 print "VLANs are:"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100685 buf = p.vlan_get_list()
Steve McIntyrecc297112014-08-11 18:46:58 +0100686 p._dump_list(buf)
687
Steve McIntyre7460d972014-12-23 14:45:30 +0000688# print 'Restarting switch, to explicitly reset config'
689# p.switch_restart()
Steve McIntyrecc297112014-08-11 18:46:58 +0100690
Steve McIntyrebf5dc882014-12-19 17:54:58 +0000691# p.switch_save_running_config()
Steve McIntyrecc297112014-08-11 18:46:58 +0100692# p._show_config()
693