blob: e32bf64662b96321a16716a070302ae3fd967990 [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 McIntyrecc297112014-08-11 18:46:58 +010024
Steve McIntyre72a8bce2015-01-23 18:02:19 +000025if __name__ == '__main__':
Steve McIntyrea67473a2015-02-12 08:26:33 +000026 import os
Steve McIntyre72a8bce2015-01-23 18:02:19 +000027 vlandpath = os.path.abspath(os.path.normpath(os.path.dirname(sys.argv[0])))
28 sys.path.insert(0, vlandpath)
29 sys.path.insert(0, "%s/.." % vlandpath)
30
Steve McIntyre5fa22652015-04-01 18:01:45 +010031from errors import InputError, PExpectError
Steve McIntyre17c421c2015-04-29 14:37:36 +010032from drivers.common import SwitchDriver, SwitchErrors
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 McIntyre1c8a3212015-07-14 17:07:31 +010047 _expected_descr_re = re.compile(r'.*\d+-Port.*Managed Switch.*')
Steve McIntyrecc297112014-08-11 18:46:58 +010048
Steve McIntyre48dc6ae2014-12-23 16:08:19 +000049 def __init__(self, switch_hostname, switch_telnetport=23, debug = False):
Steve McIntyrebb58a272015-07-14 15:39:54 +010050 SwitchDriver.__init__(self, switch_hostname, debug)
51 self._systemdata = []
Steve McIntyrecc297112014-08-11 18:46:58 +010052 self.exec_string = "/usr/bin/telnet %s %d" % (switch_hostname, switch_telnetport)
Steve McIntyre3c3f4fc2015-02-12 04:26:27 +000053 self.errors = SwitchErrors()
Steve McIntyrecc297112014-08-11 18:46:58 +010054
55 ################################
56 ### Switch-level API functions
57 ################################
58
Steve McIntyrecc297112014-08-11 18:46:58 +010059 # Save the current running config into flash - we want config to
60 # remain across reboots
Steve McIntyre9b09b9d2014-09-24 15:08:10 +010061 def switch_save_running_config(self):
Steve McIntyrec5359e62015-02-12 04:54:07 +000062 try:
63 self._cli("copy running-config startup-config")
64 self.connection.expect("Y/N")
65 self._cli("y")
66 self.connection.expect("succeeded")
Steve McIntyre2d84e522015-02-12 06:44:42 +000067 except (PExpectError, pexpect.EOF, pexpect.TIMEOUT):
Steve McIntyrec5359e62015-02-12 04:54:07 +000068 # recurse on error
69 self._switch_connect()
70 self.switch_save_running_config()
Steve McIntyrecc297112014-08-11 18:46:58 +010071
Steve McIntyre095b4452014-12-19 17:53:43 +000072 # Restart the switch - we need to reload config to do a
73 # roll-back. Do NOT save running-config first if the switch asks -
74 # we're trying to dump recent changes, not save them.
75 #
Steve McIntyre1c8a3212015-07-14 17:07:31 +010076 # This will also implicitly cause a connection to be closed
Steve McIntyre095b4452014-12-19 17:53:43 +000077 def switch_restart(self):
78 self._cli("reload")
79 index = self.connection.expect(['Are you sure', 'will reset'])
80 if index == 0:
81 self._cli("y") # Yes, continue without saving
82 self.connection.expect("reset the whole")
83
84 # Fall through
85 self._cli("y") # Yes, continue to reset
86 self.connection.close(True)
87
Steve McIntyrecc297112014-08-11 18:46:58 +010088 ################################
89 ### VLAN API functions
90 ################################
91
92 # Create a VLAN with the specified tag
Steve McIntyre9b09b9d2014-09-24 15:08:10 +010093 def vlan_create(self, tag):
Steve McIntyre5fa22652015-04-01 18:01:45 +010094 logging.debug("Creating VLAN %d", tag)
Steve McIntyrecc297112014-08-11 18:46:58 +010095
Steve McIntyrec5359e62015-02-12 04:54:07 +000096 try:
97 self._configure()
98 self._cli("vlan database")
99 self._cli("vlan %d" % tag)
100 self._end_configure()
101
102 # Validate it happened
103 vlans = self.vlan_get_list()
104 for vlan in vlans:
105 if vlan == tag:
106 return
Steve McIntyre661af762015-02-12 06:18:07 +0000107 raise IOError("Failed to create VLAN %d" % tag)
Steve McIntyrec5359e62015-02-12 04:54:07 +0000108
109 except PExpectError:
110 # recurse on error
111 self._switch_connect()
112 self.vlan_create(tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100113
114 # Destroy a VLAN with the specified tag
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100115 def vlan_destroy(self, tag):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100116 logging.debug("Destroying VLAN %d", tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100117
Steve McIntyrec5359e62015-02-12 04:54:07 +0000118 try:
119 self._configure()
120 self._cli("no 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 raise IOError("Failed to destroy VLAN %d" % tag)
128
129 except PExpectError:
130 # recurse on error
131 self._switch_connect()
132 self.vlan_destroy(tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100133
134 # Set the name of a VLAN
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100135 def vlan_set_name(self, tag, name):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100136 logging.debug("Setting name of VLAN %d to %s", tag, name)
Steve McIntyrecc297112014-08-11 18:46:58 +0100137
Steve McIntyrec5359e62015-02-12 04:54:07 +0000138 try:
139 self._configure()
140 self._cli("vlan %d" % tag)
141 self._cli("interface vlan %d" % tag)
142 self._cli("name %s" % name)
143 self._end_configure()
144
145 # Validate it happened
146 read_name = self.vlan_get_name(tag)
147 if read_name != name:
148 raise IOError("Failed to set name for VLAN %d (name found is \"%s\", not \"%s\")"
149 % (tag, read_name, name))
150
151 except PExpectError:
152 # recurse on error
153 self._switch_connect()
154 self.vlan_set_name(tag, name)
Steve McIntyrecc297112014-08-11 18:46:58 +0100155
156 # Get a list of the VLAN tags currently registered on the switch
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100157 def vlan_get_list(self):
Steve McIntyrecc297112014-08-11 18:46:58 +0100158 logging.debug("Grabbing list of VLANs")
Steve McIntyrecc297112014-08-11 18:46:58 +0100159
Steve McIntyrec5359e62015-02-12 04:54:07 +0000160 try:
161 vlans = []
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100162 regex = re.compile(r'^ *(\d+).*(D|S|G|R)')
Steve McIntyrecc297112014-08-11 18:46:58 +0100163
Steve McIntyrec5359e62015-02-12 04:54:07 +0000164 self._cli("show vlan")
Steve McIntyref5fb22b2015-04-01 18:19:54 +0100165 for line in self._read_long_output("show vlan"):
Steve McIntyrec5359e62015-02-12 04:54:07 +0000166 match = regex.match(line)
167 if match:
168 vlans.append(int(match.group(1)))
169 return vlans
170
171 except PExpectError:
172 # recurse on error
173 self._switch_connect()
174 return self.vlan_get_list()
Steve McIntyrecc297112014-08-11 18:46:58 +0100175
176 # For a given VLAN tag, ask the switch what the associated name is
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100177 def vlan_get_name(self, tag):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100178 logging.debug("Grabbing the name of VLAN %d", tag)
Steve McIntyrec5359e62015-02-12 04:54:07 +0000179
180 try:
181 name = None
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100182 regex = re.compile(r'^ *\d+\s+(\S+).*(D|S|G|R)')
Steve McIntyrec5359e62015-02-12 04:54:07 +0000183 self._cli("show vlan tag %d" % tag)
Steve McIntyref5fb22b2015-04-01 18:19:54 +0100184 for line in self._read_long_output("show vlan tag"):
Steve McIntyrec5359e62015-02-12 04:54:07 +0000185 match = regex.match(line)
186 if match:
187 name = match.group(1)
188 name.strip()
189 return name
190
191 except PExpectError:
192 # recurse on error
193 self._switch_connect()
194 return self.vlan_get_name(tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100195
196 ################################
197 ### Port API functions
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100198 ################################
Steve McIntyrecc297112014-08-11 18:46:58 +0100199
Steve McIntyre9936d002014-10-01 15:54:10 +0100200 # Set the mode of a port: access or trunk
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100201 def port_set_mode(self, port, mode):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100202 logging.debug("Setting port %s to %s", port, mode)
Steve McIntyrecc297112014-08-11 18:46:58 +0100203 if not self._is_port_mode_valid(mode):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000204 raise InputError("Port mode %s is not allowed" % mode)
Steve McIntyrecc297112014-08-11 18:46:58 +0100205 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000206 raise InputError("Port name %s not recognised" % port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100207
Steve McIntyrec5359e62015-02-12 04:54:07 +0000208 try:
209 self._configure()
210 self._cli("interface %s" % port)
211 self._cli("switchport mode %s" % mode)
212 self._end_configure()
Steve McIntyrecc297112014-08-11 18:46:58 +0100213
Steve McIntyrec5359e62015-02-12 04:54:07 +0000214 # Validate it happened
215 read_mode = self.port_get_mode(port)
216 if read_mode != mode:
217 raise IOError("Failed to set mode for port %s" % port)
218
219 except PExpectError:
220 # recurse on error
221 self._switch_connect()
222 self.port_set_mode(port, mode)
Steve McIntyrecc297112014-08-11 18:46:58 +0100223
Steve McIntyre9936d002014-10-01 15:54:10 +0100224 # Get the mode of a port: access or trunk
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100225 def port_get_mode(self, port):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100226 logging.debug("Getting mode of port %s", port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100227 mode = ''
228 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000229 raise InputError("Port name %s not recognised" % port)
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100230 regex = re.compile(r'Port Mode: (\S+)')
Steve McIntyrec5359e62015-02-12 04:54:07 +0000231
232 try:
233 self._cli("show interfaces switchport %s" % port)
Steve McIntyref5fb22b2015-04-01 18:19:54 +0100234 for line in self._read_long_output("show interfaces switchport"):
Steve McIntyrec5359e62015-02-12 04:54:07 +0000235 match = regex.match(line)
236 if match:
237 mode = match.group(1)
238 return mode.lower()
239
240 except PExpectError:
241 # recurse on error
242 self._switch_connect()
243 return self.port_get_mode(port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100244
Steve McIntyre9936d002014-10-01 15:54:10 +0100245 # Set an access port to be in a specified VLAN (tag)
246 def port_set_access_vlan(self, port, tag):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100247 logging.debug("Setting access port %s to VLAN %d", port, tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100248 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 McIntyre9936d002014-10-01 15:54:10 +0100250 if not (self.port_get_mode(port) == "access"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000251 raise InputError("Port %s not in access mode" % port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100252
Steve McIntyrec5359e62015-02-12 04:54:07 +0000253 try:
254 self._configure()
255 self._cli("interface %s" % port)
256 self._cli("switchport access vlan %d" % tag)
257 self._end_configure()
Steve McIntyrecc297112014-08-11 18:46:58 +0100258
Steve McIntyrec5359e62015-02-12 04:54:07 +0000259 # Validate things worked
260 read_vlan = int(self.port_get_access_vlan(port))
261 if read_vlan != tag:
262 raise IOError("Failed to move access port %s to VLAN %d - got VLAN %d instead"
263 % (port, tag, read_vlan))
264
265 except PExpectError:
266 # recurse on error
267 self._switch_connect()
268 self.port_set_access_vlan(port, tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100269
Steve McIntyrecc297112014-08-11 18:46:58 +0100270 # Add a trunk port to a specified VLAN (tag)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100271 def port_add_trunk_to_vlan(self, port, tag):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100272 logging.debug("Adding trunk port %s to VLAN %d", port, tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100273 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000274 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100275 if not (self.port_get_mode(port) == "trunk"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000276 raise InputError("Port %s not in trunk mode" % port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100277
Steve McIntyrec5359e62015-02-12 04:54:07 +0000278 try:
279 self._configure()
280 self._cli("interface %s" % port)
281 self._cli("switchport trunk allowed vlan add %d" % tag)
282 self._end_configure()
283
284 # Validate it happened
285 read_vlans = self.port_get_trunk_vlan_list(port)
286 for vlan in read_vlans:
287 if vlan == tag:
288 return
Steve McIntyref1771282015-04-29 18:08:34 +0100289 raise IOError("Failed to add trunk port %s to VLAN %d" % (port, tag))
Steve McIntyrec5359e62015-02-12 04:54:07 +0000290
291 except PExpectError:
292 # recurse on error
293 self._switch_connect()
294 self.port_add_trunk_to_vlan(port, tag)
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100295
Steve McIntyrecc297112014-08-11 18:46:58 +0100296 # Remove a trunk port from a specified VLAN (tag)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100297 def port_remove_trunk_from_vlan(self, port, tag):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100298 logging.debug("Removing trunk port %s from VLAN %d", port, tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100299 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000300 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100301 if not (self.port_get_mode(port) == "trunk"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000302 raise InputError("Port %s not in trunk mode" % port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100303
Steve McIntyrec5359e62015-02-12 04:54:07 +0000304 try:
305 self._configure()
306 self._cli("interface %s" % port)
307 self._cli("switchport trunk allowed vlan remove %d" % tag)
308 self._end_configure()
309
310 # Validate it happened
311 read_vlans = self.port_get_trunk_vlan_list(port)
312 for vlan in read_vlans:
313 if vlan == tag:
314 raise IOError("Failed to remove trunk port %s from VLAN %d" % (port, tag))
315
316 except PExpectError:
317 # recurse on error
318 self._switch_connect()
319 self.port_remove_trunk_from_vlan(port, tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100320
Steve McIntyre9936d002014-10-01 15:54:10 +0100321 # Get the configured VLAN tag for an access port (tag)
322 def port_get_access_vlan(self, port):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100323 logging.debug("Getting VLAN for access port %s", port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100324 vlan = 1
325 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000326 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9936d002014-10-01 15:54:10 +0100327 if not (self.port_get_mode(port) == "access"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000328 raise InputError("Port %s not in access mode" % port)
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100329 regex = re.compile(r'(\d+)\s+\S+\s+Untagged\s+(Static|System)')
Steve McIntyrec5359e62015-02-12 04:54:07 +0000330
331 try:
332 self._cli("show interfaces switchport %s" % port)
Steve McIntyref5fb22b2015-04-01 18:19:54 +0100333 for line in self._read_long_output("show interfaces switchport"):
Steve McIntyrec5359e62015-02-12 04:54:07 +0000334 match = regex.match(line)
335 if match:
336 vlan = match.group(1)
337 return int(vlan)
338
339 except PExpectError:
340 # recurse on error
341 self._switch_connect()
342 return self.port_get_access_vlan(port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100343
344 # Get the list of configured VLAN tags for a trunk port
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100345 def port_get_trunk_vlan_list(self, port):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100346 logging.debug("Getting VLANs for trunk port %s", port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100347 vlans = [ ]
348 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000349 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100350 if not (self.port_get_mode(port) == "trunk"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000351 raise InputError("Port %s not in trunk mode" % port)
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100352 regex = re.compile(r'(\d+)\s+\S+\s+(Tagged|Untagged)\s+Static')
Steve McIntyrec5359e62015-02-12 04:54:07 +0000353
354 try:
355 self._cli("show interfaces switchport %s" % port)
Steve McIntyref5fb22b2015-04-01 18:19:54 +0100356 for line in self._read_long_output("show interfaces switchport"):
Steve McIntyrec5359e62015-02-12 04:54:07 +0000357 match = regex.match(line)
358 if match:
359 vlans.append (int(match.group(1)))
360 return vlans
361
362 except PExpectError:
363 # recurse on error
364 self._switch_connect()
365 return self.port_get_trunk_vlan_list(port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100366
367 ################################
368 ### Internal functions
369 ################################
370
Steve McIntyreb345fc52015-02-12 06:37:05 +0000371 # Connect to the switch and log in
372 def _switch_connect(self):
373
374 if not self.connection is None:
375 self.connection.close(True)
376 self.connection = None
377
Steve McIntyre5fa22652015-04-01 18:01:45 +0100378 logging.debug("Connecting to Switch with: %s", self.exec_string)
Steve McIntyre06c644a2015-07-17 17:07:41 +0100379 self.connection = pexpect.spawn(self.exec_string, logfile=self.logger)
Steve McIntyreb345fc52015-02-12 06:37:05 +0000380 self._login()
381
382 # Avoid paged output
383 self._cli("terminal datadump")
384
385 # And grab details about the switch. in case we need it
386 self._get_systemdata()
387
388 # And also validate them - make sure we're driving a switch of
389 # the correct model! Also store the serial number
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100390 descr_regex = re.compile(r'System Description:.\s+(.*)')
391 sn_regex = re.compile(r'SN:\s+(\S_)')
Steve McIntyreb345fc52015-02-12 06:37:05 +0000392 descr = ""
393
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100394 for line in self._systemdata:
Steve McIntyreb345fc52015-02-12 06:37:05 +0000395 match = descr_regex.match(line)
396 if match:
397 descr = match.group(1)
398 match = sn_regex.match(line)
399 if match:
400 self.serial_number = match.group(1)
401
402 if not self._expected_descr_re.match(descr):
403 raise IOError("Switch %s not recognised by this driver: abort" % descr)
404
405 # Now build a list of our ports, for later sanity checking
406 self._ports = self._get_port_names()
407 if len(self._ports) < 4:
408 raise IOError("Not enough ports detected - problem!")
409
Steve McIntyrebbd2ac82015-02-12 03:08:48 +0000410 def _login(self):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100411 logging.debug("attempting login with username %s, password %s", self._username, self._password)
Steve McIntyrecc297112014-08-11 18:46:58 +0100412 self._cli("")
413 self.connection.expect("User Name:")
Steve McIntyrebbd2ac82015-02-12 03:08:48 +0000414 self._cli("%s" % self._username)
Steve McIntyrecc297112014-08-11 18:46:58 +0100415 self.connection.expect("Password:")
Steve McIntyrebbd2ac82015-02-12 03:08:48 +0000416 self._cli("%s" % self._password, False)
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100417 self.connection.expect(r"\*\*")
Steve McIntyrecc297112014-08-11 18:46:58 +0100418 while True:
Steve McIntyre2651aca2015-02-12 05:10:58 +0000419 index = self.connection.expect(['User Name:', 'authentication failed', r'([^#]+)#', 'Password:', '.+'])
Steve McIntyrecc297112014-08-11 18:46:58 +0100420 if index == 0 or index == 1: # Failed to log in!
Steve McIntyre5fa22652015-04-01 18:01:45 +0100421 logging.error("Login failure: %s\n", self.connection.match)
Steve McIntyrecc297112014-08-11 18:46:58 +0100422 raise IOError
423 elif index == 2:
Steve McIntyre65dfe7f2015-06-09 15:57:02 +0100424 self._prompt_name = re.escape(self.connection.match.group(1).strip())
Steve McIntyre5c51bc52015-02-12 05:34:09 +0000425 # Horrible output from the switch at login time may
426 # confuse our pexpect stuff here. If we've somehow got
427 # multiple lines of output, clean up and just take the
428 # *last* line here. Anything before that is going to
429 # just be noise from the "***" password input, etc.
430 prompt_lines = self._prompt_name.split('\r\n')
431 if len(prompt_lines) > 1:
432 self._prompt_name = prompt_lines[-1]
Steve McIntyre5fa22652015-04-01 18:01:45 +0100433 logging.debug("Got prompt name %s", self._prompt_name)
Steve McIntyrecc297112014-08-11 18:46:58 +0100434 return 0
Steve McIntyreca6c5a32014-12-23 15:34:29 +0000435 elif index == 3 or index == 4:
436 self._cli("", False)
Steve McIntyrecc297112014-08-11 18:46:58 +0100437
438 def _logout(self):
439 logging.debug("Logging out")
440 self._cli("exit", False)
441
Steve McIntyrecc297112014-08-11 18:46:58 +0100442 def _configure(self):
443 self._cli("configure terminal")
444
445 def _end_configure(self):
446 self._cli("end")
447
Steve McIntyref5fb22b2015-04-01 18:19:54 +0100448 def _read_long_output(self, text):
Steve McIntyre366046f2014-08-18 18:57:03 +0100449 prompt = self._prompt_name + '#'
Steve McIntyre2eececb2015-02-12 03:09:17 +0000450 try:
451 self.connection.expect(prompt)
Steve McIntyre1d23b492015-02-12 03:47:24 +0000452 except (pexpect.EOF, pexpect.TIMEOUT):
Steve McIntyred0614fb2015-02-12 03:44:42 +0000453 # Something went wrong; logout, log in and try again!
Steve McIntyre7a9c8192015-02-12 07:15:52 +0000454 logging.error("PEXPECT FAILURE, RECONNECT")
Steve McIntyref5fb22b2015-04-01 18:19:54 +0100455 self.errors.log_error_in(text)
Steve McIntyre1a3e2f72015-02-12 03:49:05 +0000456 raise PExpectError("_read_long_output failed")
Steve McIntyre2eececb2015-02-12 03:09:17 +0000457 except:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100458 logging.error("prompt is \"%s\"", prompt)
Steve McIntyre2eececb2015-02-12 03:09:17 +0000459 raise
Steve McIntyredf6aabf2015-02-12 06:33:19 +0000460
Steve McIntyre5fa22652015-04-01 18:01:45 +0100461 longbuf = []
Steve McIntyrea7cdefc2014-12-24 00:48:19 +0000462 for line in self.connection.before.split('\r\n'):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100463 longbuf.append(line.strip())
464 return longbuf
Steve McIntyrecc297112014-08-11 18:46:58 +0100465
466 def _get_port_names(self):
467 logging.debug("Grabbing list of ports")
468 interfaces = []
469
470 # Use "Up" or "Down" to only identify lines in the output that
471 # match interfaces that exist
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100472 regex = re.compile(r'^(\w+).*(Up|Down)')
Steve McIntyrecc297112014-08-11 18:46:58 +0100473
Steve McIntyre48ab82c2015-02-12 04:59:10 +0000474 try:
475 self._cli("show interfaces status detailed")
Steve McIntyref5fb22b2015-04-01 18:19:54 +0100476 for line in self._read_long_output("show interfaces status detailed"):
Steve McIntyre48ab82c2015-02-12 04:59:10 +0000477 match = regex.match(line)
478 if match:
479 interfaces.append(match.group(1))
Steve McIntyred601ab82015-07-09 17:42:36 +0100480 logging.debug(" found %d ports on the switch", len(interfaces))
Steve McIntyre48ab82c2015-02-12 04:59:10 +0000481 return interfaces
482 except PExpectError:
483 # recurse on error
484 self._switch_connect()
485 return self._get_port_names()
Steve McIntyrecc297112014-08-11 18:46:58 +0100486
Steve McIntyrecc297112014-08-11 18:46:58 +0100487 def _show_config(self):
488 logging.debug("Grabbing config")
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000489 try:
490 self._cli("show running-config")
Steve McIntyref5fb22b2015-04-01 18:19:54 +0100491 return self._read_long_output("show running-config")
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000492 except PExpectError:
493 # recurse on error
494 self._switch_connect()
495 return self._show_config()
Steve McIntyrecc297112014-08-11 18:46:58 +0100496
497 def _show_clock(self):
498 logging.debug("Grabbing time")
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000499 try:
500 self._cli("show clock")
Steve McIntyref5fb22b2015-04-01 18:19:54 +0100501 return self._read_long_output("show clock")
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000502 except PExpectError:
503 # recurse on error
504 self._switch_connect()
505 return self._show_clock()
Steve McIntyrecc297112014-08-11 18:46:58 +0100506
Steve McIntyrecc297112014-08-11 18:46:58 +0100507 def _get_systemdata(self):
Steve McIntyredf6aabf2015-02-12 06:33:19 +0000508 logging.debug("Grabbing system data")
Steve McIntyrecc297112014-08-11 18:46:58 +0100509
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000510 try:
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100511 self._systemdata = []
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000512 self._cli("show system")
Steve McIntyref5fb22b2015-04-01 18:19:54 +0100513 for line in self._read_long_output("show system"):
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100514 self._systemdata.append(line)
Steve McIntyre28d34ca2014-08-12 15:40:24 +0100515
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000516 logging.debug("Grabbing system sw and hw versions")
517 self._cli("show version")
Steve McIntyref5fb22b2015-04-01 18:19:54 +0100518 for line in self._read_long_output("show version"):
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100519 self._systemdata.append(line)
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000520
521 except PExpectError:
522 # recurse on error
523 self._switch_connect()
524 return self._get_systemdata()
Steve McIntyrecc297112014-08-11 18:46:58 +0100525
Steve McIntyre16f02162014-08-14 17:47:36 +0100526 ######################################
527 # Internal port access helper methods
528 ######################################
529 # N.B. No parameter checking here, for speed reasons - if you're
530 # calling this internal API then you should already have validated
531 # things yourself! Equally, no post-set checks in here - do that
532 # at the higher level.
533 ######################################
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100534
Steve McIntyrecc297112014-08-11 18:46:58 +0100535 # Wrapper around connection.send - by default, expect() the same
536 # text we've sent, to remove it from the output from the
537 # switch. For the few cases where we don't need that, override
538 # this using echo=False.
539 # Horrible, but seems to work.
540 def _cli(self, text, echo=True):
541 self.connection.send(text + '\r')
542 if echo:
Steve McIntyre9f7f03b2015-02-12 03:10:13 +0000543 try:
544 self.connection.expect(text)
Steve McIntyre1d23b492015-02-12 03:47:24 +0000545 except (pexpect.EOF, pexpect.TIMEOUT):
Steve McIntyre9f7f03b2015-02-12 03:10:13 +0000546 # Something went wrong; logout, log in and try again!
Steve McIntyre7a9c8192015-02-12 07:15:52 +0000547 logging.error("PEXPECT FAILURE, RECONNECT")
Steve McIntyre3c3f4fc2015-02-12 04:26:27 +0000548 self.errors.log_error_out(text)
Steve McIntyre729d9502015-02-12 03:14:10 +0000549 raise PExpectError("_cli failed on %s" % text)
Steve McIntyre9f7f03b2015-02-12 03:10:13 +0000550 except:
Steve McIntyrea67473a2015-02-12 08:26:33 +0000551 logging.error("Unexpected error: %s", sys.exc_info()[0])
Steve McIntyre9f7f03b2015-02-12 03:10:13 +0000552 raise
Steve McIntyrecc297112014-08-11 18:46:58 +0100553
554if __name__ == "__main__":
Steve McIntyref1699322014-08-19 22:49:43 +0100555# p = CiscoSX300('10.172.2.52', 23)
Steve McIntyre48dc6ae2014-12-23 16:08:19 +0000556
557 import optparse
558
559 switch = 'vlandswitch02'
560 parser = optparse.OptionParser()
561 parser.add_option("--switch",
562 dest = "switch",
563 action = "store",
564 nargs = 1,
565 type = "string",
566 help = "specify switch to connect to for testing",
567 metavar = "<switch>")
568 (opts, args) = parser.parse_args()
569 if opts.switch:
570 switch = opts.switch
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100571
Steve McIntyre3d92ef72015-07-08 20:02:18 +0100572 portname_base = "fa"
573 # Text to match if we're on a SG-series switch, ports all called gi<number>
574 sys_descr_re = re.compile('System Description.*Gigabit')
Steve McIntyre48dc6ae2014-12-23 16:08:19 +0000575
Steve McIntyre3d92ef72015-07-08 20:02:18 +0100576 def _port_name(number):
577 return "%s%d" % (portname_base, number)
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100578
Steve McIntyre144d51c2015-07-07 17:56:30 +0100579 logging.basicConfig(level = logging.DEBUG,
580 format = '%(asctime)s %(levelname)-8s %(message)s')
Steve McIntyre48dc6ae2014-12-23 16:08:19 +0000581 p = CiscoSX300(switch, 23, debug = True)
Steve McIntyreaea995c2014-12-22 17:17:38 +0000582 p.switch_connect('cisco', 'cisco', None)
Steve McIntyrecc297112014-08-11 18:46:58 +0100583 #buf = p._show_clock()
584 #print "%s" % buf
585 #buf = p._show_config()
Steve McIntyre5fa22652015-04-01 18:01:45 +0100586 #p.dump_list(buf)
Steve McIntyrecc297112014-08-11 18:46:58 +0100587
Steve McIntyre366046f2014-08-18 18:57:03 +0100588 print "System data:"
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100589 p.dump_list(p._systemdata)
590 for l in p._systemdata:
591 m = sys_descr_re.match(l)
592 if m:
Steve McIntyre3d92ef72015-07-08 20:02:18 +0100593 print 'Found an SG switch, using "gi" as port name prefix for testing'
594 portname_base = "gi"
595
596 if portname_base == "fa":
597 print 'Found an SF switch, using "fa" as port name prefix for testing'
Steve McIntyrecc297112014-08-11 18:46:58 +0100598
599 print "Creating VLANs for testing:"
600 for i in [ 2, 3, 4, 5, 20 ]:
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100601 p.vlan_create(i)
602 p.vlan_set_name(i, "test%d" % i)
Steve McIntyrecc297112014-08-11 18:46:58 +0100603 print " %d (test%d)" % (i, i)
604
605 #print "And dump config\n"
606 #buf = p._show_config()
607 #print "%s" % buf
608
609 #print "Destroying VLAN 2\n"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100610 #p.vlan_destroy(2)
Steve McIntyrecc297112014-08-11 18:46:58 +0100611
612 #print "And dump config\n"
613 #buf = p._show_config()
614 #print "%s" % buf
615
616 #print "Port names are:"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100617 #buf = p.switch_get_port_names()
Steve McIntyre5fa22652015-04-01 18:01:45 +0100618 #p.dump_list(buf)
Steve McIntyrecc297112014-08-11 18:46:58 +0100619
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100620 #buf = p.vlan_get_name(25)
Steve McIntyrecc297112014-08-11 18:46:58 +0100621 #print "VLAN with tag 25 is called \"%s\"" % buf
622
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100623 #p.vlan_set_name(35, "foo")
Steve McIntyrecc297112014-08-11 18:46:58 +0100624 #print "VLAN with tag 35 is called \"foo\""
625
Steve McIntyre3d92ef72015-07-08 20:02:18 +0100626 #buf = p.port_get_mode(_port_name(12))
627 #print "Port %s is in %s mode" % (_port_name(12), buf)
Steve McIntyrecc297112014-08-11 18:46:58 +0100628
Steve McIntyre9936d002014-10-01 15:54:10 +0100629 # Test access stuff
Steve McIntyre3d92ef72015-07-08 20:02:18 +0100630 print "Set %s to access mode" % _port_name(6)
631 p.port_set_mode(_port_name(6), "access")
632 print "Move %s to VLAN 2" % _port_name(6)
633 p.port_set_access_vlan(_port_name(6), 2)
634 buf = p.port_get_access_vlan(_port_name(6))
635 print "Read from switch: %s is on VLAN %s" % (_port_name(6), buf)
636 print "Move %s back to default VLAN 1" % _port_name(6)
637 p.port_set_access_vlan(_port_name(6), 1)
638 #print "And move %s back to a trunk port" % _port_name(6)
639 #p.port_set_mode(_port_name(6), "trunk")
640 #buf = p.port_get_mode(_port_name(6))
641 #print "Port %s is in %s mode" % (_port_name(6), buf)
Steve McIntyrecc297112014-08-11 18:46:58 +0100642
643 # Test trunk stuff
Steve McIntyre3d92ef72015-07-08 20:02:18 +0100644 print "Set %s to trunk mode" % _port_name(2)
645 p.port_set_mode(_port_name(2), "trunk")
646 print "Add %s to VLAN 2" % _port_name(2)
647 p.port_add_trunk_to_vlan(_port_name(2), 2)
648 print "Add %s to VLAN 3" % _port_name(2)
649 p.port_add_trunk_to_vlan(_port_name(2), 3)
650 print "Add %s to VLAN 4" % _port_name(2)
651 p.port_add_trunk_to_vlan(_port_name(2), 4)
652 print "Read from switch: which VLANs is %s on?" % _port_name(2)
653 buf = p.port_get_trunk_vlan_list(_port_name(2))
Steve McIntyre5fa22652015-04-01 18:01:45 +0100654 p.dump_list(buf)
Steve McIntyrecc297112014-08-11 18:46:58 +0100655
Steve McIntyre3d92ef72015-07-08 20:02:18 +0100656 print "Remove %s from VLANs 3,3,4" % _port_name(2)
657 p.port_remove_trunk_from_vlan(_port_name(2), 3)
658 p.port_remove_trunk_from_vlan(_port_name(2), 3)
659 p.port_remove_trunk_from_vlan(_port_name(2), 4)
660 print "Read from switch: which VLANs is %s on?" % _port_name(2)
661 buf = p.port_get_trunk_vlan_list(_port_name(2))
Steve McIntyre5fa22652015-04-01 18:01:45 +0100662 p.dump_list(buf)
Steve McIntyrecc297112014-08-11 18:46:58 +0100663
664 # print "Adding lots of ports to VLANs"
Steve McIntyre3d92ef72015-07-08 20:02:18 +0100665 # p.port_add_trunk_to_vlan(_port_name(1), 2)
666 # p.port_add_trunk_to_vlan(_port_name(3), 2)
667 # p.port_add_trunk_to_vlan(_port_name(5), 2)
668 # p.port_add_trunk_to_vlan(_port_name(7), 2)
669 # p.port_add_trunk_to_vlan(_port_name(9), 2)
670 # p.port_add_trunk_to_vlan(_port_name(11), 2)
671 # p.port_add_trunk_to_vlan(_port_name(13), 2)
672 # p.port_add_trunk_to_vlan(_port_name(15), 2)
673 # p.port_add_trunk_to_vlan(_port_name(17), 2)
674 # p.port_add_trunk_to_vlan(_port_name(19), 2)
675 # p.port_add_trunk_to_vlan(_port_name(21), 2)
676 # p.port_add_trunk_to_vlan(_port_name(23), 2)
677 # p.port_add_trunk_to_vlan(_port_name(4, 2)
Steve McIntyrecc297112014-08-11 18:46:58 +0100678
679 print "VLANs are:"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100680 buf = p.vlan_get_list()
Steve McIntyre5fa22652015-04-01 18:01:45 +0100681 p.dump_list(buf)
Steve McIntyrecc297112014-08-11 18:46:58 +0100682
Steve McIntyre7460d972014-12-23 14:45:30 +0000683# print 'Restarting switch, to explicitly reset config'
684# p.switch_restart()
Steve McIntyrecc297112014-08-11 18:46:58 +0100685
Steve McIntyrebf5dc882014-12-19 17:54:58 +0000686# p.switch_save_running_config()
Steve McIntyrecc297112014-08-11 18:46:58 +0100687# p._show_config()
688