blob: 992fd30ae2057448c1bde3986043d29b40dfdd97 [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 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 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
59 # Connect to the switch and log in
Steve McIntyrec1e42b72014-12-22 16:13:08 +000060 def switch_connect(self, username, password, enablepassword):
Steve McIntyrebbd2ac82015-02-12 03:08:48 +000061 self._username = username
62 self._password = password
63 self._enable_password = enablepassword
Steve McIntyre9295d502015-02-12 04:46:45 +000064 self._switch_connect()
65
Steve McIntyrecc297112014-08-11 18:46:58 +010066 # Log out of the switch and drop the connection and all state
Steve McIntyre9b09b9d2014-09-24 15:08:10 +010067 def switch_disconnect(self):
Steve McIntyrecc297112014-08-11 18:46:58 +010068 self._logout()
Steve McIntyre5fa22652015-04-01 18:01:45 +010069 logging.debug("Closing connection: %s", self.connection)
Steve McIntyrecc297112014-08-11 18:46:58 +010070 self.connection.close(True)
Steve McIntyre11393992014-10-10 15:53:34 +010071 self._ports = []
72 self._prompt_name = ''
Steve McIntyre5fa22652015-04-01 18:01:45 +010073 self.systemdata = []
Steve McIntyrecc297112014-08-11 18:46:58 +010074 del(self)
75
76 # Save the current running config into flash - we want config to
77 # remain across reboots
Steve McIntyre9b09b9d2014-09-24 15:08:10 +010078 def switch_save_running_config(self):
Steve McIntyrec5359e62015-02-12 04:54:07 +000079 try:
80 self._cli("copy running-config startup-config")
81 self.connection.expect("Y/N")
82 self._cli("y")
83 self.connection.expect("succeeded")
Steve McIntyre2d84e522015-02-12 06:44:42 +000084 except (PExpectError, pexpect.EOF, pexpect.TIMEOUT):
Steve McIntyrec5359e62015-02-12 04:54:07 +000085 # recurse on error
86 self._switch_connect()
87 self.switch_save_running_config()
Steve McIntyrecc297112014-08-11 18:46:58 +010088
Steve McIntyre095b4452014-12-19 17:53:43 +000089 # Restart the switch - we need to reload config to do a
90 # roll-back. Do NOT save running-config first if the switch asks -
91 # we're trying to dump recent changes, not save them.
92 #
93 # This will also implicitly cause a connection to be closed
94 def switch_restart(self):
95 self._cli("reload")
96 index = self.connection.expect(['Are you sure', 'will reset'])
97 if index == 0:
98 self._cli("y") # Yes, continue without saving
99 self.connection.expect("reset the whole")
100
101 # Fall through
102 self._cli("y") # Yes, continue to reset
103 self.connection.close(True)
104
Steve McIntyrecc297112014-08-11 18:46:58 +0100105 ################################
106 ### VLAN API functions
107 ################################
108
109 # Create a VLAN with the specified tag
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100110 def vlan_create(self, tag):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100111 logging.debug("Creating VLAN %d", tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100112
Steve McIntyrec5359e62015-02-12 04:54:07 +0000113 try:
114 self._configure()
115 self._cli("vlan database")
116 self._cli("vlan %d" % tag)
117 self._end_configure()
118
119 # Validate it happened
120 vlans = self.vlan_get_list()
121 for vlan in vlans:
122 if vlan == tag:
123 return
Steve McIntyre661af762015-02-12 06:18:07 +0000124 raise IOError("Failed to create VLAN %d" % tag)
Steve McIntyrec5359e62015-02-12 04:54:07 +0000125
126 except PExpectError:
127 # recurse on error
128 self._switch_connect()
129 self.vlan_create(tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100130
131 # Destroy a VLAN with the specified tag
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100132 def vlan_destroy(self, tag):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100133 logging.debug("Destroying VLAN %d", tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100134
Steve McIntyrec5359e62015-02-12 04:54:07 +0000135 try:
136 self._configure()
137 self._cli("no vlan %d" % tag)
138 self._end_configure()
139
140 # Validate it happened
141 vlans = self.vlan_get_list()
142 for vlan in vlans:
143 if vlan == tag:
144 raise IOError("Failed to destroy VLAN %d" % tag)
145
146 except PExpectError:
147 # recurse on error
148 self._switch_connect()
149 self.vlan_destroy(tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100150
151 # Set the name of a VLAN
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100152 def vlan_set_name(self, tag, name):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100153 logging.debug("Setting name of VLAN %d to %s", tag, name)
Steve McIntyrecc297112014-08-11 18:46:58 +0100154
Steve McIntyrec5359e62015-02-12 04:54:07 +0000155 try:
156 self._configure()
157 self._cli("vlan %d" % tag)
158 self._cli("interface vlan %d" % tag)
159 self._cli("name %s" % name)
160 self._end_configure()
161
162 # Validate it happened
163 read_name = self.vlan_get_name(tag)
164 if read_name != name:
165 raise IOError("Failed to set name for VLAN %d (name found is \"%s\", not \"%s\")"
166 % (tag, read_name, name))
167
168 except PExpectError:
169 # recurse on error
170 self._switch_connect()
171 self.vlan_set_name(tag, name)
Steve McIntyrecc297112014-08-11 18:46:58 +0100172
173 # Get a list of the VLAN tags currently registered on the switch
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100174 def vlan_get_list(self):
Steve McIntyrecc297112014-08-11 18:46:58 +0100175 logging.debug("Grabbing list of VLANs")
Steve McIntyrecc297112014-08-11 18:46:58 +0100176
Steve McIntyrec5359e62015-02-12 04:54:07 +0000177 try:
178 vlans = []
179 regex = re.compile('^ *(\d+).*(D|S|G|R)')
Steve McIntyrecc297112014-08-11 18:46:58 +0100180
Steve McIntyrec5359e62015-02-12 04:54:07 +0000181 self._cli("show vlan")
Steve McIntyref5fb22b2015-04-01 18:19:54 +0100182 for line in self._read_long_output("show vlan"):
Steve McIntyrec5359e62015-02-12 04:54:07 +0000183 match = regex.match(line)
184 if match:
185 vlans.append(int(match.group(1)))
186 return vlans
187
188 except PExpectError:
189 # recurse on error
190 self._switch_connect()
191 return self.vlan_get_list()
Steve McIntyrecc297112014-08-11 18:46:58 +0100192
193 # For a given VLAN tag, ask the switch what the associated name is
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100194 def vlan_get_name(self, tag):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100195 logging.debug("Grabbing the name of VLAN %d", tag)
Steve McIntyrec5359e62015-02-12 04:54:07 +0000196
197 try:
198 name = None
199 regex = re.compile('^ *\d+\s+(\S+).*(D|S|G|R)')
200 self._cli("show vlan tag %d" % tag)
Steve McIntyref5fb22b2015-04-01 18:19:54 +0100201 for line in self._read_long_output("show vlan tag"):
Steve McIntyrec5359e62015-02-12 04:54:07 +0000202 match = regex.match(line)
203 if match:
204 name = match.group(1)
205 name.strip()
206 return name
207
208 except PExpectError:
209 # recurse on error
210 self._switch_connect()
211 return self.vlan_get_name(tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100212
213 ################################
214 ### Port API functions
215 ################################
216
Steve McIntyre9936d002014-10-01 15:54:10 +0100217 # Set the mode of a port: access or trunk
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100218 def port_set_mode(self, port, mode):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100219 logging.debug("Setting port %s to %s", port, mode)
Steve McIntyrecc297112014-08-11 18:46:58 +0100220 if not self._is_port_mode_valid(mode):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000221 raise InputError("Port mode %s is not allowed" % mode)
Steve McIntyrecc297112014-08-11 18:46:58 +0100222 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000223 raise InputError("Port name %s not recognised" % port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100224
Steve McIntyrec5359e62015-02-12 04:54:07 +0000225 try:
226 self._configure()
227 self._cli("interface %s" % port)
228 self._cli("switchport mode %s" % mode)
229 self._end_configure()
Steve McIntyrecc297112014-08-11 18:46:58 +0100230
Steve McIntyrec5359e62015-02-12 04:54:07 +0000231 # Validate it happened
232 read_mode = self.port_get_mode(port)
233 if read_mode != mode:
234 raise IOError("Failed to set mode for port %s" % port)
235
236 except PExpectError:
237 # recurse on error
238 self._switch_connect()
239 self.port_set_mode(port, mode)
Steve McIntyrecc297112014-08-11 18:46:58 +0100240
Steve McIntyre9936d002014-10-01 15:54:10 +0100241 # Get the mode of a port: access or trunk
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100242 def port_get_mode(self, port):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100243 logging.debug("Getting mode of port %s", port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100244 mode = ''
245 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000246 raise InputError("Port name %s not recognised" % port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100247 regex = re.compile('Port Mode: (\S+)')
Steve McIntyrec5359e62015-02-12 04:54:07 +0000248
249 try:
250 self._cli("show interfaces switchport %s" % port)
Steve McIntyref5fb22b2015-04-01 18:19:54 +0100251 for line in self._read_long_output("show interfaces switchport"):
Steve McIntyrec5359e62015-02-12 04:54:07 +0000252 match = regex.match(line)
253 if match:
254 mode = match.group(1)
255 return mode.lower()
256
257 except PExpectError:
258 # recurse on error
259 self._switch_connect()
260 return self.port_get_mode(port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100261
Steve McIntyre9936d002014-10-01 15:54:10 +0100262 # Set an access port to be in a specified VLAN (tag)
263 def port_set_access_vlan(self, port, tag):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100264 logging.debug("Setting access port %s to VLAN %d", port, tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100265 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000266 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9936d002014-10-01 15:54:10 +0100267 if not (self.port_get_mode(port) == "access"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000268 raise InputError("Port %s not in access mode" % port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100269
Steve McIntyrec5359e62015-02-12 04:54:07 +0000270 try:
271 self._configure()
272 self._cli("interface %s" % port)
273 self._cli("switchport access vlan %d" % tag)
274 self._end_configure()
Steve McIntyrecc297112014-08-11 18:46:58 +0100275
Steve McIntyrec5359e62015-02-12 04:54:07 +0000276 # Validate things worked
277 read_vlan = int(self.port_get_access_vlan(port))
278 if read_vlan != tag:
279 raise IOError("Failed to move access port %s to VLAN %d - got VLAN %d instead"
280 % (port, tag, read_vlan))
281
282 except PExpectError:
283 # recurse on error
284 self._switch_connect()
285 self.port_set_access_vlan(port, tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100286
Steve McIntyrecc297112014-08-11 18:46:58 +0100287 # Add a trunk port to a specified VLAN (tag)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100288 def port_add_trunk_to_vlan(self, port, tag):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100289 logging.debug("Adding trunk port %s to VLAN %d", port, tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100290 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000291 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100292 if not (self.port_get_mode(port) == "trunk"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000293 raise InputError("Port %s not in trunk mode" % port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100294
Steve McIntyrec5359e62015-02-12 04:54:07 +0000295 try:
296 self._configure()
297 self._cli("interface %s" % port)
298 self._cli("switchport trunk allowed vlan add %d" % tag)
299 self._end_configure()
300
301 # Validate it happened
302 read_vlans = self.port_get_trunk_vlan_list(port)
303 for vlan in read_vlans:
304 if vlan == tag:
305 return
Steve McIntyref1771282015-04-29 18:08:34 +0100306 raise IOError("Failed to add trunk port %s to VLAN %d" % (port, tag))
Steve McIntyrec5359e62015-02-12 04:54:07 +0000307
308 except PExpectError:
309 # recurse on error
310 self._switch_connect()
311 self.port_add_trunk_to_vlan(port, tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100312
313 # Remove a trunk port from a specified VLAN (tag)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100314 def port_remove_trunk_from_vlan(self, port, tag):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100315 logging.debug("Removing trunk port %s from VLAN %d", port, tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100316 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000317 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100318 if not (self.port_get_mode(port) == "trunk"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000319 raise InputError("Port %s not in trunk mode" % port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100320
Steve McIntyrec5359e62015-02-12 04:54:07 +0000321 try:
322 self._configure()
323 self._cli("interface %s" % port)
324 self._cli("switchport trunk allowed vlan remove %d" % tag)
325 self._end_configure()
326
327 # Validate it happened
328 read_vlans = self.port_get_trunk_vlan_list(port)
329 for vlan in read_vlans:
330 if vlan == tag:
331 raise IOError("Failed to remove trunk port %s from VLAN %d" % (port, tag))
332
333 except PExpectError:
334 # recurse on error
335 self._switch_connect()
336 self.port_remove_trunk_from_vlan(port, tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100337
Steve McIntyre9936d002014-10-01 15:54:10 +0100338 # Get the configured VLAN tag for an access port (tag)
339 def port_get_access_vlan(self, port):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100340 logging.debug("Getting VLAN for access port %s", port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100341 vlan = 1
342 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000343 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9936d002014-10-01 15:54:10 +0100344 if not (self.port_get_mode(port) == "access"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000345 raise InputError("Port %s not in access mode" % port)
Steve McIntyre0f893602014-12-24 02:14:53 +0000346 regex = re.compile('(\d+)\s+\S+\s+Untagged\s+(Static|System)')
Steve McIntyrec5359e62015-02-12 04:54:07 +0000347
348 try:
349 self._cli("show interfaces switchport %s" % port)
Steve McIntyref5fb22b2015-04-01 18:19:54 +0100350 for line in self._read_long_output("show interfaces switchport"):
Steve McIntyrec5359e62015-02-12 04:54:07 +0000351 match = regex.match(line)
352 if match:
353 vlan = match.group(1)
354 return int(vlan)
355
356 except PExpectError:
357 # recurse on error
358 self._switch_connect()
359 return self.port_get_access_vlan(port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100360
361 # Get the list of configured VLAN tags for a trunk port
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100362 def port_get_trunk_vlan_list(self, port):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100363 logging.debug("Getting VLANs for trunk port %s", port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100364 vlans = [ ]
365 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000366 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100367 if not (self.port_get_mode(port) == "trunk"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000368 raise InputError("Port %s not in trunk mode" % port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100369 regex = re.compile('(\d+)\s+\S+\s+(Tagged|Untagged)\s+Static')
Steve McIntyrec5359e62015-02-12 04:54:07 +0000370
371 try:
372 self._cli("show interfaces switchport %s" % port)
Steve McIntyref5fb22b2015-04-01 18:19:54 +0100373 for line in self._read_long_output("show interfaces switchport"):
Steve McIntyrec5359e62015-02-12 04:54:07 +0000374 match = regex.match(line)
375 if match:
376 vlans.append (int(match.group(1)))
377 return vlans
378
379 except PExpectError:
380 # recurse on error
381 self._switch_connect()
382 return self.port_get_trunk_vlan_list(port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100383
384 ################################
385 ### Internal functions
386 ################################
387
Steve McIntyreb345fc52015-02-12 06:37:05 +0000388 # Connect to the switch and log in
389 def _switch_connect(self):
390
391 if not self.connection is None:
392 self.connection.close(True)
393 self.connection = None
394
Steve McIntyre5fa22652015-04-01 18:01:45 +0100395 logging.debug("Connecting to Switch with: %s", self.exec_string)
Steve McIntyreb345fc52015-02-12 06:37:05 +0000396 self.connection = pexpect.spawn(self.exec_string, logfile = self.logfile)
397
398 self._login()
399
400 # Avoid paged output
401 self._cli("terminal datadump")
402
403 # And grab details about the switch. in case we need it
404 self._get_systemdata()
405
406 # And also validate them - make sure we're driving a switch of
407 # the correct model! Also store the serial number
408 descr_regex = re.compile('System Description:.\s+(.*)')
409 sn_regex = re.compile('SN:\s+(\S_)')
410 descr = ""
411
Steve McIntyre5fa22652015-04-01 18:01:45 +0100412 for line in self.systemdata:
Steve McIntyreb345fc52015-02-12 06:37:05 +0000413 match = descr_regex.match(line)
414 if match:
415 descr = match.group(1)
416 match = sn_regex.match(line)
417 if match:
418 self.serial_number = match.group(1)
419
420 if not self._expected_descr_re.match(descr):
421 raise IOError("Switch %s not recognised by this driver: abort" % descr)
422
423 # Now build a list of our ports, for later sanity checking
424 self._ports = self._get_port_names()
425 if len(self._ports) < 4:
426 raise IOError("Not enough ports detected - problem!")
427
Steve McIntyrebbd2ac82015-02-12 03:08:48 +0000428 def _login(self):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100429 logging.debug("attempting login with username %s, password %s", self._username, self._password)
Steve McIntyrecc297112014-08-11 18:46:58 +0100430 self._cli("")
431 self.connection.expect("User Name:")
Steve McIntyrebbd2ac82015-02-12 03:08:48 +0000432 self._cli("%s" % self._username)
Steve McIntyrecc297112014-08-11 18:46:58 +0100433 self.connection.expect("Password:")
Steve McIntyrebbd2ac82015-02-12 03:08:48 +0000434 self._cli("%s" % self._password, False)
Steve McIntyreca6c5a32014-12-23 15:34:29 +0000435 self.connection.expect("\*\*")
Steve McIntyrecc297112014-08-11 18:46:58 +0100436 while True:
Steve McIntyre2651aca2015-02-12 05:10:58 +0000437 index = self.connection.expect(['User Name:', 'authentication failed', r'([^#]+)#', 'Password:', '.+'])
Steve McIntyrecc297112014-08-11 18:46:58 +0100438 if index == 0 or index == 1: # Failed to log in!
Steve McIntyre5fa22652015-04-01 18:01:45 +0100439 logging.error("Login failure: %s\n", self.connection.match)
Steve McIntyrecc297112014-08-11 18:46:58 +0100440 raise IOError
441 elif index == 2:
Steve McIntyre65dfe7f2015-06-09 15:57:02 +0100442 self._prompt_name = re.escape(self.connection.match.group(1).strip())
Steve McIntyre5c51bc52015-02-12 05:34:09 +0000443 # Horrible output from the switch at login time may
444 # confuse our pexpect stuff here. If we've somehow got
445 # multiple lines of output, clean up and just take the
446 # *last* line here. Anything before that is going to
447 # just be noise from the "***" password input, etc.
448 prompt_lines = self._prompt_name.split('\r\n')
449 if len(prompt_lines) > 1:
450 self._prompt_name = prompt_lines[-1]
Steve McIntyre5fa22652015-04-01 18:01:45 +0100451 logging.debug("Got prompt name %s", self._prompt_name)
Steve McIntyrecc297112014-08-11 18:46:58 +0100452 return 0
Steve McIntyreca6c5a32014-12-23 15:34:29 +0000453 elif index == 3 or index == 4:
454 self._cli("", False)
Steve McIntyrecc297112014-08-11 18:46:58 +0100455
456 def _logout(self):
457 logging.debug("Logging out")
458 self._cli("exit", False)
459
Steve McIntyrecc297112014-08-11 18:46:58 +0100460 def _configure(self):
461 self._cli("configure terminal")
462
463 def _end_configure(self):
464 self._cli("end")
465
Steve McIntyref5fb22b2015-04-01 18:19:54 +0100466 def _read_long_output(self, text):
Steve McIntyre366046f2014-08-18 18:57:03 +0100467 prompt = self._prompt_name + '#'
Steve McIntyre2eececb2015-02-12 03:09:17 +0000468 try:
469 self.connection.expect(prompt)
Steve McIntyre1d23b492015-02-12 03:47:24 +0000470 except (pexpect.EOF, pexpect.TIMEOUT):
Steve McIntyred0614fb2015-02-12 03:44:42 +0000471 # Something went wrong; logout, log in and try again!
Steve McIntyre7a9c8192015-02-12 07:15:52 +0000472 logging.error("PEXPECT FAILURE, RECONNECT")
Steve McIntyref5fb22b2015-04-01 18:19:54 +0100473 self.errors.log_error_in(text)
Steve McIntyre1a3e2f72015-02-12 03:49:05 +0000474 raise PExpectError("_read_long_output failed")
Steve McIntyre2eececb2015-02-12 03:09:17 +0000475 except:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100476 logging.error("prompt is \"%s\"", prompt)
Steve McIntyre2eececb2015-02-12 03:09:17 +0000477 raise
Steve McIntyredf6aabf2015-02-12 06:33:19 +0000478
Steve McIntyre5fa22652015-04-01 18:01:45 +0100479 longbuf = []
Steve McIntyrea7cdefc2014-12-24 00:48:19 +0000480 for line in self.connection.before.split('\r\n'):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100481 longbuf.append(line.strip())
482 return longbuf
Steve McIntyrecc297112014-08-11 18:46:58 +0100483
484 def _get_port_names(self):
485 logging.debug("Grabbing list of ports")
486 interfaces = []
487
488 # Use "Up" or "Down" to only identify lines in the output that
489 # match interfaces that exist
490 regex = re.compile('^(\w+).*(Up|Down)')
491
Steve McIntyre48ab82c2015-02-12 04:59:10 +0000492 try:
493 self._cli("show interfaces status detailed")
Steve McIntyref5fb22b2015-04-01 18:19:54 +0100494 for line in self._read_long_output("show interfaces status detailed"):
Steve McIntyre48ab82c2015-02-12 04:59:10 +0000495 match = regex.match(line)
496 if match:
497 interfaces.append(match.group(1))
Steve McIntyred601ab82015-07-09 17:42:36 +0100498 logging.debug(" found %d ports on the switch", len(interfaces))
Steve McIntyre48ab82c2015-02-12 04:59:10 +0000499 return interfaces
500 except PExpectError:
501 # recurse on error
502 self._switch_connect()
503 return self._get_port_names()
Steve McIntyrecc297112014-08-11 18:46:58 +0100504
Steve McIntyrecc297112014-08-11 18:46:58 +0100505 def _show_config(self):
506 logging.debug("Grabbing config")
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000507 try:
508 self._cli("show running-config")
Steve McIntyref5fb22b2015-04-01 18:19:54 +0100509 return self._read_long_output("show running-config")
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000510 except PExpectError:
511 # recurse on error
512 self._switch_connect()
513 return self._show_config()
Steve McIntyrecc297112014-08-11 18:46:58 +0100514
515 def _show_clock(self):
516 logging.debug("Grabbing time")
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000517 try:
518 self._cli("show clock")
Steve McIntyref5fb22b2015-04-01 18:19:54 +0100519 return self._read_long_output("show clock")
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000520 except PExpectError:
521 # recurse on error
522 self._switch_connect()
523 return self._show_clock()
Steve McIntyrecc297112014-08-11 18:46:58 +0100524
Steve McIntyrecc297112014-08-11 18:46:58 +0100525 def _get_systemdata(self):
Steve McIntyredf6aabf2015-02-12 06:33:19 +0000526 logging.debug("Grabbing system data")
Steve McIntyrecc297112014-08-11 18:46:58 +0100527
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000528 try:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100529 self.systemdata = []
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000530 self._cli("show system")
Steve McIntyref5fb22b2015-04-01 18:19:54 +0100531 for line in self._read_long_output("show system"):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100532 self.systemdata.append(line)
Steve McIntyre28d34ca2014-08-12 15:40:24 +0100533
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000534 logging.debug("Grabbing system sw and hw versions")
535 self._cli("show version")
Steve McIntyref5fb22b2015-04-01 18:19:54 +0100536 for line in self._read_long_output("show version"):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100537 self.systemdata.append(line)
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000538
539 except PExpectError:
540 # recurse on error
541 self._switch_connect()
542 return self._get_systemdata()
Steve McIntyrecc297112014-08-11 18:46:58 +0100543
Steve McIntyre16f02162014-08-14 17:47:36 +0100544 ######################################
545 # Internal port access helper methods
546 ######################################
547 # N.B. No parameter checking here, for speed reasons - if you're
548 # calling this internal API then you should already have validated
549 # things yourself! Equally, no post-set checks in here - do that
550 # at the higher level.
551 ######################################
552
Steve McIntyrecc297112014-08-11 18:46:58 +0100553 # Wrapper around connection.send - by default, expect() the same
554 # text we've sent, to remove it from the output from the
555 # switch. For the few cases where we don't need that, override
556 # this using echo=False.
557 # Horrible, but seems to work.
558 def _cli(self, text, echo=True):
559 self.connection.send(text + '\r')
560 if echo:
Steve McIntyre9f7f03b2015-02-12 03:10:13 +0000561 try:
562 self.connection.expect(text)
Steve McIntyre1d23b492015-02-12 03:47:24 +0000563 except (pexpect.EOF, pexpect.TIMEOUT):
Steve McIntyre9f7f03b2015-02-12 03:10:13 +0000564 # Something went wrong; logout, log in and try again!
Steve McIntyre7a9c8192015-02-12 07:15:52 +0000565 logging.error("PEXPECT FAILURE, RECONNECT")
Steve McIntyre3c3f4fc2015-02-12 04:26:27 +0000566 self.errors.log_error_out(text)
Steve McIntyre729d9502015-02-12 03:14:10 +0000567 raise PExpectError("_cli failed on %s" % text)
Steve McIntyre9f7f03b2015-02-12 03:10:13 +0000568 except:
Steve McIntyrea67473a2015-02-12 08:26:33 +0000569 logging.error("Unexpected error: %s", sys.exc_info()[0])
Steve McIntyre9f7f03b2015-02-12 03:10:13 +0000570 raise
Steve McIntyrecc297112014-08-11 18:46:58 +0100571
572if __name__ == "__main__":
Steve McIntyref1699322014-08-19 22:49:43 +0100573# p = CiscoSX300('10.172.2.52', 23)
Steve McIntyre48dc6ae2014-12-23 16:08:19 +0000574
575 import optparse
576
577 switch = 'vlandswitch02'
578 parser = optparse.OptionParser()
579 parser.add_option("--switch",
580 dest = "switch",
581 action = "store",
582 nargs = 1,
583 type = "string",
584 help = "specify switch to connect to for testing",
585 metavar = "<switch>")
586 (opts, args) = parser.parse_args()
587 if opts.switch:
588 switch = opts.switch
Steve McIntyre3d92ef72015-07-08 20:02:18 +0100589
590 portname_base = "fa"
591 # Text to match if we're on a SG-series switch, ports all called gi<number>
592 sys_descr_re = re.compile('System Description.*Gigabit')
Steve McIntyre48dc6ae2014-12-23 16:08:19 +0000593
Steve McIntyre3d92ef72015-07-08 20:02:18 +0100594 def _port_name(number):
595 return "%s%d" % (portname_base, number)
596
Steve McIntyre144d51c2015-07-07 17:56:30 +0100597 logging.basicConfig(level = logging.DEBUG,
598 format = '%(asctime)s %(levelname)-8s %(message)s')
Steve McIntyre48dc6ae2014-12-23 16:08:19 +0000599 p = CiscoSX300(switch, 23, debug = True)
Steve McIntyreaea995c2014-12-22 17:17:38 +0000600 p.switch_connect('cisco', 'cisco', None)
Steve McIntyrecc297112014-08-11 18:46:58 +0100601 #buf = p._show_clock()
602 #print "%s" % buf
603 #buf = p._show_config()
Steve McIntyre5fa22652015-04-01 18:01:45 +0100604 #p.dump_list(buf)
Steve McIntyrecc297112014-08-11 18:46:58 +0100605
Steve McIntyre366046f2014-08-18 18:57:03 +0100606 print "System data:"
Steve McIntyre5fa22652015-04-01 18:01:45 +0100607 p.dump_list(p.systemdata)
Steve McIntyre3d92ef72015-07-08 20:02:18 +0100608 for line in p.systemdata:
609 match = sys_descr_re.match(line)
610 if match:
611 print 'Found an SG switch, using "gi" as port name prefix for testing'
612 portname_base = "gi"
613
614 if portname_base == "fa":
615 print 'Found an SF switch, using "fa" as port name prefix for testing'
Steve McIntyrecc297112014-08-11 18:46:58 +0100616
617 print "Creating VLANs for testing:"
618 for i in [ 2, 3, 4, 5, 20 ]:
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100619 p.vlan_create(i)
620 p.vlan_set_name(i, "test%d" % i)
Steve McIntyrecc297112014-08-11 18:46:58 +0100621 print " %d (test%d)" % (i, i)
622
623 #print "And dump config\n"
624 #buf = p._show_config()
625 #print "%s" % buf
626
627 #print "Destroying VLAN 2\n"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100628 #p.vlan_destroy(2)
Steve McIntyrecc297112014-08-11 18:46:58 +0100629
630 #print "And dump config\n"
631 #buf = p._show_config()
632 #print "%s" % buf
633
634 #print "Port names are:"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100635 #buf = p.switch_get_port_names()
Steve McIntyre5fa22652015-04-01 18:01:45 +0100636 #p.dump_list(buf)
Steve McIntyrecc297112014-08-11 18:46:58 +0100637
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100638 #buf = p.vlan_get_name(25)
Steve McIntyrecc297112014-08-11 18:46:58 +0100639 #print "VLAN with tag 25 is called \"%s\"" % buf
640
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100641 #p.vlan_set_name(35, "foo")
Steve McIntyrecc297112014-08-11 18:46:58 +0100642 #print "VLAN with tag 35 is called \"foo\""
643
Steve McIntyre3d92ef72015-07-08 20:02:18 +0100644 #buf = p.port_get_mode(_port_name(12))
645 #print "Port %s is in %s mode" % (_port_name(12), buf)
Steve McIntyrecc297112014-08-11 18:46:58 +0100646
Steve McIntyre9936d002014-10-01 15:54:10 +0100647 # Test access stuff
Steve McIntyre3d92ef72015-07-08 20:02:18 +0100648 print "Set %s to access mode" % _port_name(6)
649 p.port_set_mode(_port_name(6), "access")
650 print "Move %s to VLAN 2" % _port_name(6)
651 p.port_set_access_vlan(_port_name(6), 2)
652 buf = p.port_get_access_vlan(_port_name(6))
653 print "Read from switch: %s is on VLAN %s" % (_port_name(6), buf)
654 print "Move %s back to default VLAN 1" % _port_name(6)
655 p.port_set_access_vlan(_port_name(6), 1)
656 #print "And move %s back to a trunk port" % _port_name(6)
657 #p.port_set_mode(_port_name(6), "trunk")
658 #buf = p.port_get_mode(_port_name(6))
659 #print "Port %s is in %s mode" % (_port_name(6), buf)
Steve McIntyrecc297112014-08-11 18:46:58 +0100660
661 # Test trunk stuff
Steve McIntyre3d92ef72015-07-08 20:02:18 +0100662 print "Set %s to trunk mode" % _port_name(2)
663 p.port_set_mode(_port_name(2), "trunk")
664 print "Add %s to VLAN 2" % _port_name(2)
665 p.port_add_trunk_to_vlan(_port_name(2), 2)
666 print "Add %s to VLAN 3" % _port_name(2)
667 p.port_add_trunk_to_vlan(_port_name(2), 3)
668 print "Add %s to VLAN 4" % _port_name(2)
669 p.port_add_trunk_to_vlan(_port_name(2), 4)
670 print "Read from switch: which VLANs is %s on?" % _port_name(2)
671 buf = p.port_get_trunk_vlan_list(_port_name(2))
Steve McIntyre5fa22652015-04-01 18:01:45 +0100672 p.dump_list(buf)
Steve McIntyrecc297112014-08-11 18:46:58 +0100673
Steve McIntyre3d92ef72015-07-08 20:02:18 +0100674 print "Remove %s from VLANs 3,3,4" % _port_name(2)
675 p.port_remove_trunk_from_vlan(_port_name(2), 3)
676 p.port_remove_trunk_from_vlan(_port_name(2), 3)
677 p.port_remove_trunk_from_vlan(_port_name(2), 4)
678 print "Read from switch: which VLANs is %s on?" % _port_name(2)
679 buf = p.port_get_trunk_vlan_list(_port_name(2))
Steve McIntyre5fa22652015-04-01 18:01:45 +0100680 p.dump_list(buf)
Steve McIntyrecc297112014-08-11 18:46:58 +0100681
682 # print "Adding lots of ports to VLANs"
Steve McIntyre3d92ef72015-07-08 20:02:18 +0100683 # p.port_add_trunk_to_vlan(_port_name(1), 2)
684 # p.port_add_trunk_to_vlan(_port_name(3), 2)
685 # p.port_add_trunk_to_vlan(_port_name(5), 2)
686 # p.port_add_trunk_to_vlan(_port_name(7), 2)
687 # p.port_add_trunk_to_vlan(_port_name(9), 2)
688 # p.port_add_trunk_to_vlan(_port_name(11), 2)
689 # p.port_add_trunk_to_vlan(_port_name(13), 2)
690 # p.port_add_trunk_to_vlan(_port_name(15), 2)
691 # p.port_add_trunk_to_vlan(_port_name(17), 2)
692 # p.port_add_trunk_to_vlan(_port_name(19), 2)
693 # p.port_add_trunk_to_vlan(_port_name(21), 2)
694 # p.port_add_trunk_to_vlan(_port_name(23), 2)
695 # p.port_add_trunk_to_vlan(_port_name(4, 2)
Steve McIntyrecc297112014-08-11 18:46:58 +0100696
697 print "VLANs are:"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100698 buf = p.vlan_get_list()
Steve McIntyre5fa22652015-04-01 18:01:45 +0100699 p.dump_list(buf)
Steve McIntyrecc297112014-08-11 18:46:58 +0100700
Steve McIntyre7460d972014-12-23 14:45:30 +0000701# print 'Restarting switch, to explicitly reset config'
702# p.switch_restart()
Steve McIntyrecc297112014-08-11 18:46:58 +0100703
Steve McIntyrebf5dc882014-12-19 17:54:58 +0000704# p.switch_save_running_config()
Steve McIntyrecc297112014-08-11 18:46:58 +0100705# p._show_config()
706