blob: 083bfcc0ab323a14279d5c6b60461e93d92abe68 [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
Steve McIntyreb79cea52015-07-24 17:03:31 +0100215 read_mode = self._port_get_mode(port)
Steve McIntyrec5359e62015-02-12 04:54:07 +0000216 if read_mode != mode:
217 raise IOError("Failed to set mode for port %s" % port)
218
Steve McIntyre1a7bad52015-07-20 15:21:42 +0100219 # And cache the result
220 self._port_modes[port] = mode
221
Steve McIntyrec5359e62015-02-12 04:54:07 +0000222 except PExpectError:
223 # recurse on error
224 self._switch_connect()
225 self.port_set_mode(port, mode)
Steve McIntyrecc297112014-08-11 18:46:58 +0100226
Steve McIntyre9936d002014-10-01 15:54:10 +0100227 # Set an access port to be in a specified VLAN (tag)
228 def port_set_access_vlan(self, port, tag):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100229 logging.debug("Setting access port %s to VLAN %d", port, tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100230 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000231 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9936d002014-10-01 15:54:10 +0100232 if not (self.port_get_mode(port) == "access"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000233 raise InputError("Port %s not in access mode" % port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100234
Steve McIntyrec5359e62015-02-12 04:54:07 +0000235 try:
236 self._configure()
237 self._cli("interface %s" % port)
238 self._cli("switchport access vlan %d" % tag)
239 self._end_configure()
Steve McIntyrecc297112014-08-11 18:46:58 +0100240
Steve McIntyrec5359e62015-02-12 04:54:07 +0000241 # Validate things worked
242 read_vlan = int(self.port_get_access_vlan(port))
243 if read_vlan != tag:
244 raise IOError("Failed to move access port %s to VLAN %d - got VLAN %d instead"
245 % (port, tag, read_vlan))
246
247 except PExpectError:
248 # recurse on error
249 self._switch_connect()
250 self.port_set_access_vlan(port, tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100251
Steve McIntyrecc297112014-08-11 18:46:58 +0100252 # Add a trunk port to a specified VLAN (tag)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100253 def port_add_trunk_to_vlan(self, port, tag):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100254 logging.debug("Adding trunk port %s to VLAN %d", port, tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100255 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000256 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100257 if not (self.port_get_mode(port) == "trunk"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000258 raise InputError("Port %s not in trunk mode" % port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100259
Steve McIntyrec5359e62015-02-12 04:54:07 +0000260 try:
261 self._configure()
262 self._cli("interface %s" % port)
263 self._cli("switchport trunk allowed vlan add %d" % tag)
264 self._end_configure()
265
266 # Validate it happened
267 read_vlans = self.port_get_trunk_vlan_list(port)
268 for vlan in read_vlans:
269 if vlan == tag:
270 return
Steve McIntyref1771282015-04-29 18:08:34 +0100271 raise IOError("Failed to add trunk port %s to VLAN %d" % (port, tag))
Steve McIntyrec5359e62015-02-12 04:54:07 +0000272
273 except PExpectError:
274 # recurse on error
275 self._switch_connect()
276 self.port_add_trunk_to_vlan(port, tag)
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100277
Steve McIntyrecc297112014-08-11 18:46:58 +0100278 # Remove a trunk port from a specified VLAN (tag)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100279 def port_remove_trunk_from_vlan(self, port, tag):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100280 logging.debug("Removing trunk port %s from VLAN %d", port, tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100281 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000282 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100283 if not (self.port_get_mode(port) == "trunk"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000284 raise InputError("Port %s not in trunk mode" % port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100285
Steve McIntyrec5359e62015-02-12 04:54:07 +0000286 try:
287 self._configure()
288 self._cli("interface %s" % port)
289 self._cli("switchport trunk allowed vlan remove %d" % tag)
290 self._end_configure()
291
292 # Validate it happened
293 read_vlans = self.port_get_trunk_vlan_list(port)
294 for vlan in read_vlans:
295 if vlan == tag:
296 raise IOError("Failed to remove trunk port %s from VLAN %d" % (port, tag))
297
298 except PExpectError:
299 # recurse on error
300 self._switch_connect()
301 self.port_remove_trunk_from_vlan(port, tag)
Steve McIntyrecc297112014-08-11 18:46:58 +0100302
Steve McIntyre9936d002014-10-01 15:54:10 +0100303 # Get the configured VLAN tag for an access port (tag)
304 def port_get_access_vlan(self, port):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100305 logging.debug("Getting VLAN for access port %s", port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100306 vlan = 1
307 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 McIntyre6b4ff2e2015-09-04 14:36:00 +0100311 regex = re.compile(r'(\d+)\s+\S+\s+Untagged\s+(D|S|G|R)')
Steve McIntyrec5359e62015-02-12 04:54:07 +0000312
313 try:
314 self._cli("show interfaces switchport %s" % port)
Steve McIntyref5fb22b2015-04-01 18:19:54 +0100315 for line in self._read_long_output("show interfaces switchport"):
Steve McIntyrec5359e62015-02-12 04:54:07 +0000316 match = regex.match(line)
317 if match:
318 vlan = match.group(1)
319 return int(vlan)
320
321 except PExpectError:
322 # recurse on error
323 self._switch_connect()
324 return self.port_get_access_vlan(port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100325
326 # Get the list of configured VLAN tags for a trunk port
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100327 def port_get_trunk_vlan_list(self, port):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100328 logging.debug("Getting VLANs for trunk port %s", port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100329 vlans = [ ]
330 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000331 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100332 if not (self.port_get_mode(port) == "trunk"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000333 raise InputError("Port %s not in trunk mode" % port)
Steve McIntyre6b4ff2e2015-09-04 14:36:00 +0100334 regex = re.compile(r'(\d+)\s+\S+\s+(Tagged|Untagged)\s+(D|S|G|R)')
Steve McIntyrec5359e62015-02-12 04:54:07 +0000335
336 try:
337 self._cli("show interfaces switchport %s" % port)
Steve McIntyref5fb22b2015-04-01 18:19:54 +0100338 for line in self._read_long_output("show interfaces switchport"):
Steve McIntyrec5359e62015-02-12 04:54:07 +0000339 match = regex.match(line)
340 if match:
341 vlans.append (int(match.group(1)))
342 return vlans
343
344 except PExpectError:
345 # recurse on error
346 self._switch_connect()
347 return self.port_get_trunk_vlan_list(port)
Steve McIntyrecc297112014-08-11 18:46:58 +0100348
349 ################################
350 ### Internal functions
351 ################################
352
Steve McIntyreb345fc52015-02-12 06:37:05 +0000353 # Connect to the switch and log in
354 def _switch_connect(self):
355
356 if not self.connection is None:
357 self.connection.close(True)
358 self.connection = None
359
Steve McIntyre5fa22652015-04-01 18:01:45 +0100360 logging.debug("Connecting to Switch with: %s", self.exec_string)
Steve McIntyre06c644a2015-07-17 17:07:41 +0100361 self.connection = pexpect.spawn(self.exec_string, logfile=self.logger)
Steve McIntyreb345fc52015-02-12 06:37:05 +0000362 self._login()
363
364 # Avoid paged output
365 self._cli("terminal datadump")
366
367 # And grab details about the switch. in case we need it
368 self._get_systemdata()
369
370 # And also validate them - make sure we're driving a switch of
371 # the correct model! Also store the serial number
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100372 descr_regex = re.compile(r'System Description:.\s+(.*)')
373 sn_regex = re.compile(r'SN:\s+(\S_)')
Steve McIntyreb345fc52015-02-12 06:37:05 +0000374 descr = ""
375
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100376 for line in self._systemdata:
Steve McIntyreb345fc52015-02-12 06:37:05 +0000377 match = descr_regex.match(line)
378 if match:
379 descr = match.group(1)
380 match = sn_regex.match(line)
381 if match:
382 self.serial_number = match.group(1)
383
384 if not self._expected_descr_re.match(descr):
385 raise IOError("Switch %s not recognised by this driver: abort" % descr)
386
387 # Now build a list of our ports, for later sanity checking
388 self._ports = self._get_port_names()
389 if len(self._ports) < 4:
390 raise IOError("Not enough ports detected - problem!")
391
Steve McIntyrebbd2ac82015-02-12 03:08:48 +0000392 def _login(self):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100393 logging.debug("attempting login with username %s, password %s", self._username, self._password)
Steve McIntyrecc297112014-08-11 18:46:58 +0100394 self._cli("")
395 self.connection.expect("User Name:")
Steve McIntyrebbd2ac82015-02-12 03:08:48 +0000396 self._cli("%s" % self._username)
Steve McIntyrecc297112014-08-11 18:46:58 +0100397 self.connection.expect("Password:")
Steve McIntyrebbd2ac82015-02-12 03:08:48 +0000398 self._cli("%s" % self._password, False)
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100399 self.connection.expect(r"\*\*")
Steve McIntyrecc297112014-08-11 18:46:58 +0100400 while True:
Steve McIntyre2651aca2015-02-12 05:10:58 +0000401 index = self.connection.expect(['User Name:', 'authentication failed', r'([^#]+)#', 'Password:', '.+'])
Steve McIntyrecc297112014-08-11 18:46:58 +0100402 if index == 0 or index == 1: # Failed to log in!
Steve McIntyre5fa22652015-04-01 18:01:45 +0100403 logging.error("Login failure: %s\n", self.connection.match)
Steve McIntyrecc297112014-08-11 18:46:58 +0100404 raise IOError
405 elif index == 2:
Steve McIntyre65dfe7f2015-06-09 15:57:02 +0100406 self._prompt_name = re.escape(self.connection.match.group(1).strip())
Steve McIntyre5c51bc52015-02-12 05:34:09 +0000407 # Horrible output from the switch at login time may
408 # confuse our pexpect stuff here. If we've somehow got
409 # multiple lines of output, clean up and just take the
410 # *last* line here. Anything before that is going to
411 # just be noise from the "***" password input, etc.
412 prompt_lines = self._prompt_name.split('\r\n')
413 if len(prompt_lines) > 1:
414 self._prompt_name = prompt_lines[-1]
Steve McIntyre5fa22652015-04-01 18:01:45 +0100415 logging.debug("Got prompt name %s", self._prompt_name)
Steve McIntyrecc297112014-08-11 18:46:58 +0100416 return 0
Steve McIntyreca6c5a32014-12-23 15:34:29 +0000417 elif index == 3 or index == 4:
418 self._cli("", False)
Steve McIntyrecc297112014-08-11 18:46:58 +0100419
420 def _logout(self):
421 logging.debug("Logging out")
422 self._cli("exit", False)
423
Steve McIntyrecc297112014-08-11 18:46:58 +0100424 def _configure(self):
425 self._cli("configure terminal")
426
427 def _end_configure(self):
428 self._cli("end")
429
Steve McIntyref5fb22b2015-04-01 18:19:54 +0100430 def _read_long_output(self, text):
Steve McIntyre366046f2014-08-18 18:57:03 +0100431 prompt = self._prompt_name + '#'
Steve McIntyre2eececb2015-02-12 03:09:17 +0000432 try:
433 self.connection.expect(prompt)
Steve McIntyre1d23b492015-02-12 03:47:24 +0000434 except (pexpect.EOF, pexpect.TIMEOUT):
Steve McIntyred0614fb2015-02-12 03:44:42 +0000435 # Something went wrong; logout, log in and try again!
Steve McIntyre7a9c8192015-02-12 07:15:52 +0000436 logging.error("PEXPECT FAILURE, RECONNECT")
Steve McIntyref5fb22b2015-04-01 18:19:54 +0100437 self.errors.log_error_in(text)
Steve McIntyre1a3e2f72015-02-12 03:49:05 +0000438 raise PExpectError("_read_long_output failed")
Steve McIntyre2eececb2015-02-12 03:09:17 +0000439 except:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100440 logging.error("prompt is \"%s\"", prompt)
Steve McIntyre2eececb2015-02-12 03:09:17 +0000441 raise
Steve McIntyredf6aabf2015-02-12 06:33:19 +0000442
Steve McIntyre5fa22652015-04-01 18:01:45 +0100443 longbuf = []
Steve McIntyrea7cdefc2014-12-24 00:48:19 +0000444 for line in self.connection.before.split('\r\n'):
Steve McIntyre5fa22652015-04-01 18:01:45 +0100445 longbuf.append(line.strip())
446 return longbuf
Steve McIntyrecc297112014-08-11 18:46:58 +0100447
448 def _get_port_names(self):
449 logging.debug("Grabbing list of ports")
450 interfaces = []
451
452 # Use "Up" or "Down" to only identify lines in the output that
453 # match interfaces that exist
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100454 regex = re.compile(r'^(\w+).*(Up|Down)')
Steve McIntyrecc297112014-08-11 18:46:58 +0100455
Steve McIntyre48ab82c2015-02-12 04:59:10 +0000456 try:
457 self._cli("show interfaces status detailed")
Steve McIntyref5fb22b2015-04-01 18:19:54 +0100458 for line in self._read_long_output("show interfaces status detailed"):
Steve McIntyre48ab82c2015-02-12 04:59:10 +0000459 match = regex.match(line)
460 if match:
Steve McIntyred5cfde12015-08-05 13:49:42 +0100461 interface = match.group(1)
462 interfaces.append(interface)
463 self._port_numbers[interface] = len(interfaces)
Steve McIntyred601ab82015-07-09 17:42:36 +0100464 logging.debug(" found %d ports on the switch", len(interfaces))
Steve McIntyre48ab82c2015-02-12 04:59:10 +0000465 return interfaces
466 except PExpectError:
467 # recurse on error
468 self._switch_connect()
469 return self._get_port_names()
Steve McIntyrecc297112014-08-11 18:46:58 +0100470
Steve McIntyre1a7bad52015-07-20 15:21:42 +0100471 # Get the mode of a port: access or trunk
472 def _port_get_mode(self, port):
473 logging.debug("Getting mode of port %s", port)
474 mode = ''
475 if not self._is_port_name_valid(port):
476 raise InputError("Port name %s not recognised" % port)
477 regex = re.compile(r'Port Mode: (\S+)')
478
479 try:
480 self._cli("show interfaces switchport %s" % port)
481 for line in self._read_long_output("show interfaces switchport"):
482 match = regex.match(line)
483 if match:
484 mode = match.group(1)
485 return mode.lower()
486
487 except PExpectError:
488 # recurse on error
489 self._switch_connect()
490 return self.port_get_mode(port)
491
Steve McIntyrecc297112014-08-11 18:46:58 +0100492 def _show_config(self):
493 logging.debug("Grabbing config")
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000494 try:
495 self._cli("show running-config")
Steve McIntyref5fb22b2015-04-01 18:19:54 +0100496 return self._read_long_output("show running-config")
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000497 except PExpectError:
498 # recurse on error
499 self._switch_connect()
500 return self._show_config()
Steve McIntyrecc297112014-08-11 18:46:58 +0100501
502 def _show_clock(self):
503 logging.debug("Grabbing time")
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000504 try:
505 self._cli("show clock")
Steve McIntyref5fb22b2015-04-01 18:19:54 +0100506 return self._read_long_output("show clock")
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000507 except PExpectError:
508 # recurse on error
509 self._switch_connect()
510 return self._show_clock()
Steve McIntyrecc297112014-08-11 18:46:58 +0100511
Steve McIntyrecc297112014-08-11 18:46:58 +0100512 def _get_systemdata(self):
Steve McIntyredf6aabf2015-02-12 06:33:19 +0000513 logging.debug("Grabbing system data")
Steve McIntyrecc297112014-08-11 18:46:58 +0100514
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000515 try:
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100516 self._systemdata = []
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000517 self._cli("show system")
Steve McIntyref5fb22b2015-04-01 18:19:54 +0100518 for line in self._read_long_output("show system"):
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100519 self._systemdata.append(line)
Steve McIntyre28d34ca2014-08-12 15:40:24 +0100520
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000521 logging.debug("Grabbing system sw and hw versions")
522 self._cli("show version")
Steve McIntyref5fb22b2015-04-01 18:19:54 +0100523 for line in self._read_long_output("show version"):
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100524 self._systemdata.append(line)
Steve McIntyre0fbb2dc2015-02-12 05:11:27 +0000525
526 except PExpectError:
527 # recurse on error
528 self._switch_connect()
529 return self._get_systemdata()
Steve McIntyrecc297112014-08-11 18:46:58 +0100530
Steve McIntyre16f02162014-08-14 17:47:36 +0100531 ######################################
532 # Internal port access helper methods
533 ######################################
534 # N.B. No parameter checking here, for speed reasons - if you're
535 # calling this internal API then you should already have validated
536 # things yourself! Equally, no post-set checks in here - do that
537 # at the higher level.
538 ######################################
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100539
Steve McIntyrecc297112014-08-11 18:46:58 +0100540 # Wrapper around connection.send - by default, expect() the same
541 # text we've sent, to remove it from the output from the
542 # switch. For the few cases where we don't need that, override
543 # this using echo=False.
544 # Horrible, but seems to work.
545 def _cli(self, text, echo=True):
546 self.connection.send(text + '\r')
547 if echo:
Steve McIntyre9f7f03b2015-02-12 03:10:13 +0000548 try:
549 self.connection.expect(text)
Steve McIntyre1d23b492015-02-12 03:47:24 +0000550 except (pexpect.EOF, pexpect.TIMEOUT):
Steve McIntyre9f7f03b2015-02-12 03:10:13 +0000551 # Something went wrong; logout, log in and try again!
Steve McIntyre7a9c8192015-02-12 07:15:52 +0000552 logging.error("PEXPECT FAILURE, RECONNECT")
Steve McIntyre3c3f4fc2015-02-12 04:26:27 +0000553 self.errors.log_error_out(text)
Steve McIntyre729d9502015-02-12 03:14:10 +0000554 raise PExpectError("_cli failed on %s" % text)
Steve McIntyre9f7f03b2015-02-12 03:10:13 +0000555 except:
Steve McIntyrea67473a2015-02-12 08:26:33 +0000556 logging.error("Unexpected error: %s", sys.exc_info()[0])
Steve McIntyre9f7f03b2015-02-12 03:10:13 +0000557 raise
Steve McIntyrecc297112014-08-11 18:46:58 +0100558
559if __name__ == "__main__":
Steve McIntyref1699322014-08-19 22:49:43 +0100560# p = CiscoSX300('10.172.2.52', 23)
Steve McIntyre48dc6ae2014-12-23 16:08:19 +0000561
562 import optparse
563
564 switch = 'vlandswitch02'
565 parser = optparse.OptionParser()
566 parser.add_option("--switch",
567 dest = "switch",
568 action = "store",
569 nargs = 1,
570 type = "string",
571 help = "specify switch to connect to for testing",
572 metavar = "<switch>")
573 (opts, args) = parser.parse_args()
574 if opts.switch:
575 switch = opts.switch
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100576
Steve McIntyre3d92ef72015-07-08 20:02:18 +0100577 portname_base = "fa"
578 # Text to match if we're on a SG-series switch, ports all called gi<number>
579 sys_descr_re = re.compile('System Description.*Gigabit')
Steve McIntyre48dc6ae2014-12-23 16:08:19 +0000580
Steve McIntyre3d92ef72015-07-08 20:02:18 +0100581 def _port_name(number):
582 return "%s%d" % (portname_base, number)
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100583
Steve McIntyre144d51c2015-07-07 17:56:30 +0100584 logging.basicConfig(level = logging.DEBUG,
585 format = '%(asctime)s %(levelname)-8s %(message)s')
Steve McIntyre48dc6ae2014-12-23 16:08:19 +0000586 p = CiscoSX300(switch, 23, debug = True)
Steve McIntyreaea995c2014-12-22 17:17:38 +0000587 p.switch_connect('cisco', 'cisco', None)
Steve McIntyrecc297112014-08-11 18:46:58 +0100588 #buf = p._show_clock()
589 #print "%s" % buf
590 #buf = p._show_config()
Steve McIntyre5fa22652015-04-01 18:01:45 +0100591 #p.dump_list(buf)
Steve McIntyrecc297112014-08-11 18:46:58 +0100592
Steve McIntyre366046f2014-08-18 18:57:03 +0100593 print "System data:"
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100594 p.dump_list(p._systemdata)
595 for l in p._systemdata:
596 m = sys_descr_re.match(l)
597 if m:
Steve McIntyre3d92ef72015-07-08 20:02:18 +0100598 print 'Found an SG switch, using "gi" as port name prefix for testing'
599 portname_base = "gi"
600
601 if portname_base == "fa":
602 print 'Found an SF switch, using "fa" as port name prefix for testing'
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 McIntyre5fa22652015-04-01 18:01:45 +0100623 #p.dump_list(buf)
Steve McIntyrecc297112014-08-11 18:46:58 +0100624
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 McIntyre3d92ef72015-07-08 20:02:18 +0100631 #buf = p.port_get_mode(_port_name(12))
632 #print "Port %s is in %s mode" % (_port_name(12), buf)
Steve McIntyrecc297112014-08-11 18:46:58 +0100633
Steve McIntyre9936d002014-10-01 15:54:10 +0100634 # Test access stuff
Steve McIntyre3d92ef72015-07-08 20:02:18 +0100635 print "Set %s to access mode" % _port_name(6)
636 p.port_set_mode(_port_name(6), "access")
637 print "Move %s to VLAN 2" % _port_name(6)
638 p.port_set_access_vlan(_port_name(6), 2)
639 buf = p.port_get_access_vlan(_port_name(6))
640 print "Read from switch: %s is on VLAN %s" % (_port_name(6), buf)
641 print "Move %s back to default VLAN 1" % _port_name(6)
642 p.port_set_access_vlan(_port_name(6), 1)
643 #print "And move %s back to a trunk port" % _port_name(6)
644 #p.port_set_mode(_port_name(6), "trunk")
645 #buf = p.port_get_mode(_port_name(6))
646 #print "Port %s is in %s mode" % (_port_name(6), buf)
Steve McIntyrecc297112014-08-11 18:46:58 +0100647
648 # Test trunk stuff
Steve McIntyre3d92ef72015-07-08 20:02:18 +0100649 print "Set %s to trunk mode" % _port_name(2)
650 p.port_set_mode(_port_name(2), "trunk")
651 print "Add %s to VLAN 2" % _port_name(2)
652 p.port_add_trunk_to_vlan(_port_name(2), 2)
653 print "Add %s to VLAN 3" % _port_name(2)
654 p.port_add_trunk_to_vlan(_port_name(2), 3)
655 print "Add %s to VLAN 4" % _port_name(2)
656 p.port_add_trunk_to_vlan(_port_name(2), 4)
657 print "Read from switch: which VLANs is %s on?" % _port_name(2)
658 buf = p.port_get_trunk_vlan_list(_port_name(2))
Steve McIntyre5fa22652015-04-01 18:01:45 +0100659 p.dump_list(buf)
Steve McIntyrecc297112014-08-11 18:46:58 +0100660
Steve McIntyre3d92ef72015-07-08 20:02:18 +0100661 print "Remove %s from VLANs 3,3,4" % _port_name(2)
662 p.port_remove_trunk_from_vlan(_port_name(2), 3)
663 p.port_remove_trunk_from_vlan(_port_name(2), 3)
664 p.port_remove_trunk_from_vlan(_port_name(2), 4)
665 print "Read from switch: which VLANs is %s on?" % _port_name(2)
666 buf = p.port_get_trunk_vlan_list(_port_name(2))
Steve McIntyre5fa22652015-04-01 18:01:45 +0100667 p.dump_list(buf)
Steve McIntyrecc297112014-08-11 18:46:58 +0100668
669 # print "Adding lots of ports to VLANs"
Steve McIntyre3d92ef72015-07-08 20:02:18 +0100670 # p.port_add_trunk_to_vlan(_port_name(1), 2)
671 # p.port_add_trunk_to_vlan(_port_name(3), 2)
672 # p.port_add_trunk_to_vlan(_port_name(5), 2)
673 # p.port_add_trunk_to_vlan(_port_name(7), 2)
674 # p.port_add_trunk_to_vlan(_port_name(9), 2)
675 # p.port_add_trunk_to_vlan(_port_name(11), 2)
676 # p.port_add_trunk_to_vlan(_port_name(13), 2)
677 # p.port_add_trunk_to_vlan(_port_name(15), 2)
678 # p.port_add_trunk_to_vlan(_port_name(17), 2)
679 # p.port_add_trunk_to_vlan(_port_name(19), 2)
680 # p.port_add_trunk_to_vlan(_port_name(21), 2)
681 # p.port_add_trunk_to_vlan(_port_name(23), 2)
682 # p.port_add_trunk_to_vlan(_port_name(4, 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 McIntyre5fa22652015-04-01 18:01:45 +0100686 p.dump_list(buf)
Steve McIntyrecc297112014-08-11 18:46:58 +0100687
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