blob: 5563ef8652a580c76f55eb838c8c757a1a263b46 [file] [log] [blame]
Steve McIntyred6759dd2014-08-12 18:10:00 +01001#! /usr/bin/python
2
3# Copyright 2014 Linaro Limited
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18# MA 02110-1301, USA.
19
20import logging
21import pexpect
22import sys
23import time
24import re
Steve McIntyre3dfd36f2015-02-12 06:37:56 +000025from common import SwitchDriver, SwitchErrors
26
Steve McIntyre72a8bce2015-01-23 18:02:19 +000027if __name__ == '__main__':
Steve McIntyrea67473a2015-02-12 08:26:33 +000028 import os
Steve McIntyre72a8bce2015-01-23 18:02:19 +000029 vlandpath = os.path.abspath(os.path.normpath(os.path.dirname(sys.argv[0])))
30 sys.path.insert(0, vlandpath)
31 sys.path.insert(0, "%s/.." % vlandpath)
32
Steve McIntyre3dfd36f2015-02-12 06:37:56 +000033from errors import CriticalError, InputError, PExpectError
Steve McIntyred6759dd2014-08-12 18:10:00 +010034
35class CiscoCatalyst(SwitchDriver):
36
37 connection = None
Steve McIntyre3dfd36f2015-02-12 06:37:56 +000038 _username = None
39 _password = None
40 _enable_password = None
Steve McIntyre3f287882014-08-18 19:02:15 +010041
42 _capabilities = [
43 'TrunkWildCardVlans' # Trunk ports are on all VLANs by
44 # default, so we shouldn't need to
45 # bugger with them
46 ]
47
Steve McIntyred6759dd2014-08-12 18:10:00 +010048 # Regexp of expected hardware information - fail if we don't see
49 # this
Steve McIntyre3f287882014-08-18 19:02:15 +010050 _expected_descr_re = re.compile('WS-C\S+-\d+P')
Steve McIntyred6759dd2014-08-12 18:10:00 +010051
Steve McIntyred6759dd2014-08-12 18:10:00 +010052 logfile = None
53
Steve McIntyre48dc6ae2014-12-23 16:08:19 +000054 def __init__(self, switch_hostname, switch_telnetport=23, debug = False):
55 if debug:
Steve McIntyre9f5a0232014-12-23 16:14:28 +000056 self.logfile = sys.stderr
Steve McIntyred6759dd2014-08-12 18:10:00 +010057 self.exec_string = "/usr/bin/telnet %s %d" % (switch_hostname, switch_telnetport)
Steve McIntyre3dfd36f2015-02-12 06:37:56 +000058 self.errors = SwitchErrors()
Steve McIntyred6759dd2014-08-12 18:10:00 +010059
60 ################################
61 ### Switch-level API functions
62 ################################
63
64 # Connect to the switch and log in
Steve McIntyre9b09b9d2014-09-24 15:08:10 +010065 def switch_connect(self, username, password, enablepassword):
Steve McIntyre3dfd36f2015-02-12 06:37:56 +000066 self._username = username
67 self._password = password
68 self._enable_password = enablepassword
69 self._switch_connect()
Steve McIntyred6759dd2014-08-12 18:10:00 +010070
71 # Log out of the switch and drop the connection and all state
Steve McIntyre9b09b9d2014-09-24 15:08:10 +010072 def switch_disconnect(self):
Steve McIntyred6759dd2014-08-12 18:10:00 +010073 self._logout()
74 logging.debug("Closing connection: %s" % self.connection)
75 self.connection.close(True)
Steve McIntyre3dfd36f2015-02-12 06:37:56 +000076 self._ports = []
77 self._prompt_name = ''
78 self._systemdata = []
Steve McIntyred6759dd2014-08-12 18:10:00 +010079 del(self)
80
81 # Save the current running config into flash - we want config to
82 # remain across reboots
Steve McIntyre9b09b9d2014-09-24 15:08:10 +010083 def switch_save_running_config(self):
Steve McIntyre3dfd36f2015-02-12 06:37:56 +000084 try:
85 self._cli("copy running-config startup-config")
86 self.connection.expect("startup-config")
87 self._cli("startup-config")
88 self.connection.expect("OK")
Steve McIntyre2d84e522015-02-12 06:44:42 +000089 except (PExpectError, pexpect.EOF):
Steve McIntyre3dfd36f2015-02-12 06:37:56 +000090 # recurse on error
91 self._switch_connect()
92 self.switch_save_running_config()
Steve McIntyred6759dd2014-08-12 18:10:00 +010093
Steve McIntyre095b4452014-12-19 17:53:43 +000094 # Restart the switch - we need to reload config to do a
95 # roll-back. Do NOT save running-config first if the switch asks -
96 # we're trying to dump recent changes, not save them.
97 #
98 # This will also implicitly cause a connection to be closed
99 def switch_restart(self):
100 self._cli("reload")
101 index = self.connection.expect(['has been modified', 'Proceed'])
102 if index == 0:
103 self._cli("n") # No, don't save
104 self.connection.expect("Proceed")
105
106 # Fall through
107 self._cli("y") # Yes, continue to reset
108 self.connection.close(True)
109
Steve McIntyre3f287882014-08-18 19:02:15 +0100110 # List the capabilities of the switch (and driver) - some things
111 # make no sense to abstract. Returns a dict of strings, each one
112 # describing an extra feature that that higher levels may care
113 # about
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100114 def switch_get_capabilities(self):
Steve McIntyre3f287882014-08-18 19:02:15 +0100115 return self._capabilities
Steve McIntyred6759dd2014-08-12 18:10:00 +0100116
117 ################################
118 ### VLAN API functions
119 ################################
120
121 # Create a VLAN with the specified tag
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100122 def vlan_create(self, tag):
Steve McIntyred6759dd2014-08-12 18:10:00 +0100123 logging.debug("Creating VLAN %d" % tag)
Steve McIntyre3dfd36f2015-02-12 06:37:56 +0000124 try:
125 self._configure()
126 self._cli("vlan %d" % tag)
127 self._end_configure()
Steve McIntyred6759dd2014-08-12 18:10:00 +0100128
Steve McIntyre3dfd36f2015-02-12 06:37:56 +0000129 # Validate it happened
130 vlans = self.vlan_get_list()
131 for vlan in vlans:
132 if vlan == tag:
133 return
134 raise IOError("Failed to create VLAN %d" % tag)
135
136 except PExpectError:
137 # recurse on error
138 self._switch_connect()
139 self.vlan_create(tag)
Steve McIntyred6759dd2014-08-12 18:10:00 +0100140
141 # Destroy a VLAN with the specified tag
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100142 def vlan_destroy(self, tag):
Steve McIntyred6759dd2014-08-12 18:10:00 +0100143 logging.debug("Destroying VLAN %d" % tag)
Steve McIntyred6759dd2014-08-12 18:10:00 +0100144
Steve McIntyre3dfd36f2015-02-12 06:37:56 +0000145 try:
146 self._configure()
147 self._cli("no vlan %d" % tag)
148 self._end_configure()
149
150 # Validate it happened
151 vlans = self.vlan_get_list()
152 for vlan in vlans:
153 if vlan == tag:
154 raise IOError("Failed to destroy VLAN %d" % tag)
155
156 except PExpectError:
157 # recurse on error
158 self._switch_connect()
159 self.vlan_destroy(tag)
Steve McIntyred6759dd2014-08-12 18:10:00 +0100160
161 # Set the name of a VLAN
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100162 def vlan_set_name(self, tag, name):
Steve McIntyred6759dd2014-08-12 18:10:00 +0100163 logging.debug("Setting name of VLAN %d to %s" % (tag, name))
Steve McIntyred6759dd2014-08-12 18:10:00 +0100164
Steve McIntyre3dfd36f2015-02-12 06:37:56 +0000165 try:
166 self._configure()
167 self._cli("vlan %d" % tag)
168 self._cli("name %s" % name)
169 self._end_configure()
170
171 # Validate it happened
172 read_name = self.vlan_get_name(tag)
173 if read_name != name:
174 raise IOError("Failed to set name for VLAN %d (name found is \"%s\", not \"%s\")"
175 % (tag, read_name, name))
176 except PExpectError:
177 # recurse on error
178 self._switch_connect()
179 self.vlan_set_name(tag, name)
Steve McIntyred6759dd2014-08-12 18:10:00 +0100180
181 # Get a list of the VLAN tags currently registered on the switch
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100182 def vlan_get_list(self):
Steve McIntyred6759dd2014-08-12 18:10:00 +0100183 logging.debug("Grabbing list of VLANs")
Steve McIntyred6759dd2014-08-12 18:10:00 +0100184
Steve McIntyre3dfd36f2015-02-12 06:37:56 +0000185 try:
186 vlans = []
Steve McIntyred6759dd2014-08-12 18:10:00 +0100187
Steve McIntyre3dfd36f2015-02-12 06:37:56 +0000188 regex = re.compile('^ *(\d+).*(active)')
189
190 self._cli("show vlan brief")
191 for line in self._read_long_output():
192 match = regex.match(line)
193 if match:
194 vlans.append(int(match.group(1)))
195 return vlans
196
197 except PExpectError:
198 # recurse on error
199 self._switch_connect()
200 return self.vlan_get_list()
Steve McIntyred6759dd2014-08-12 18:10:00 +0100201
202 # For a given VLAN tag, ask the switch what the associated name is
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100203 def vlan_get_name(self, tag):
Steve McIntyre3dfd36f2015-02-12 06:37:56 +0000204
205 try:
206 logging.debug("Grabbing the name of VLAN %d" % tag)
207 name = None
208 regex = re.compile('^ *\d+\s+(\S+).*(active)')
209 self._cli("show vlan id %d" % tag)
210 for line in self._read_long_output():
211 match = regex.match(line)
212 if match:
213 name = match.group(1)
214 name.strip()
215 return name
216
217 except PExpectError:
218 # recurse on error
219 self._switch_connect()
220 return self.vlan_get_name(tag)
Steve McIntyred6759dd2014-08-12 18:10:00 +0100221
222 ################################
223 ### Port API functions
Steve McIntyree1bf11a2014-08-14 17:56:25 +0100224 ################################
Steve McIntyred6759dd2014-08-12 18:10:00 +0100225
Steve McIntyre9936d002014-10-01 15:54:10 +0100226 # Set the mode of a port: access or trunk
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100227 def port_set_mode(self, port, mode):
Steve McIntyred6759dd2014-08-12 18:10:00 +0100228 logging.debug("Setting port %s to %s" % (port, mode))
229 if not self._is_port_mode_valid(mode):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000230 raise InputError("Port mode %s is not allowed" % mode)
Steve McIntyred6759dd2014-08-12 18:10:00 +0100231 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000232 raise InputError("Port name %s not recognised" % port)
Steve McIntyre3f287882014-08-18 19:02:15 +0100233
Steve McIntyre3dfd36f2015-02-12 06:37:56 +0000234 try:
235 self._configure()
236 self._cli("interface %s" % port)
237 self._cli("switchport mode %s" % mode)
238 if mode == "trunk":
239 self._cli("switchport trunk encapsulation dot1q")
240 self._end_configure()
Steve McIntyred6759dd2014-08-12 18:10:00 +0100241
Steve McIntyre3dfd36f2015-02-12 06:37:56 +0000242 # Validate it happened
243 read_mode = self.port_get_mode(port)
Steve McIntyre3f287882014-08-18 19:02:15 +0100244
Steve McIntyre3dfd36f2015-02-12 06:37:56 +0000245 if read_mode != mode:
246 raise IOError("Failed to set mode for port %s" % port)
247
248 except PExpectError:
249 # recurse on error
250 self._switch_connect()
251 self.port_set_mode(port, mode)
Steve McIntyred6759dd2014-08-12 18:10:00 +0100252
Steve McIntyre9936d002014-10-01 15:54:10 +0100253 # Get the mode of a port: access or trunk
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100254 def port_get_mode(self, port):
Steve McIntyred6759dd2014-08-12 18:10:00 +0100255 logging.debug("Getting mode of port %s" % port)
256 mode = ''
257 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000258 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9936d002014-10-01 15:54:10 +0100259 regex = re.compile('Administrative Mode: (.*)')
Steve McIntyre3dfd36f2015-02-12 06:37:56 +0000260
261 try:
262 self._cli("show interfaces %s switchport" % port)
263 for line in self._read_long_output():
264 match = regex.match(line)
265 if match:
266 mode = match.group(1)
267 if mode == 'static access':
268 return 'access'
269 if mode == 'dynamic auto':
270 return 'trunk'
271 return mode
272
273 except PExpectError:
274 # recurse on error
275 self._switch_connect()
276 return self.port_get_mode(port)
Steve McIntyred6759dd2014-08-12 18:10:00 +0100277
Steve McIntyre9936d002014-10-01 15:54:10 +0100278 # Set an access port to be in a specified VLAN (tag)
279 def port_set_access_vlan(self, port, tag):
280 logging.debug("Setting access port %s to VLAN %d" % (port, tag))
Steve McIntyred6759dd2014-08-12 18:10:00 +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 McIntyre9936d002014-10-01 15:54:10 +0100283 if not (self.port_get_mode(port) == "access"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000284 raise InputError("Port %s not in access mode" % port)
Steve McIntyre3f287882014-08-18 19:02:15 +0100285
Steve McIntyre3dfd36f2015-02-12 06:37:56 +0000286 try:
287 self._configure()
288 self._cli("interface %s" % port)
289 self._cli("switchport access vlan %d" % tag)
290 self._cli("no shutdown")
291 self._end_configure()
Steve McIntyred6759dd2014-08-12 18:10:00 +0100292
Steve McIntyre3dfd36f2015-02-12 06:37:56 +0000293 # Finally, validate things worked
294 read_vlan = int(self.port_get_access_vlan(port))
295 if read_vlan != tag:
296 raise IOError("Failed to move access port %d to VLAN %d - got VLAN %d instead"
297 % (port, tag, read_vlan))
298
299 except PExpectError:
300 # recurse on error
301 self._switch_connect()
302 self.port_set_access_vlan(port, tag)
Steve McIntyred6759dd2014-08-12 18:10:00 +0100303
Steve McIntyred6759dd2014-08-12 18:10:00 +0100304 # Add a trunk port to a specified VLAN (tag)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100305 def port_add_trunk_to_vlan(self, port, tag):
Steve McIntyred6759dd2014-08-12 18:10:00 +0100306 logging.debug("Adding trunk port %s to VLAN %d" % (port, tag))
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 McIntyre9b09b9d2014-09-24 15:08:10 +0100309 if not (self.port_get_mode(port) == "trunk"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000310 raise InputError("Port %s not in trunk mode" % port)
Steve McIntyred6759dd2014-08-12 18:10:00 +0100311
Steve McIntyre3dfd36f2015-02-12 06:37:56 +0000312 try:
313 self._configure()
314 self._cli("interface %s" % port)
315 self._cli("switchport trunk allowed vlan add %d" % tag)
316 self._end_configure()
317
318 # Validate it happened
319 read_vlans = self.port_get_trunk_vlan_list(port)
320 for vlan in read_vlans:
321 if vlan == tag or vlan == "ALL":
322 return
323 raise IOError("Failed to add trunk port %s to VLAN %d" % (port, tag))
324
325 except PExpectError:
326 # recurse on error
327 self._switch_connect()
328 self.port_add_trunk_to_vlan(port, tag)
329
Steve McIntyred6759dd2014-08-12 18:10:00 +0100330 # Remove a trunk port from a specified VLAN (tag)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100331 def port_remove_trunk_from_vlan(self, port, tag):
Steve McIntyred6759dd2014-08-12 18:10:00 +0100332 logging.debug("Removing trunk port %s from VLAN %d" % (port, tag))
333 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000334 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100335 if not (self.port_get_mode(port) == "trunk"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000336 raise InputError("Port %s not in trunk mode" % port)
Steve McIntyred6759dd2014-08-12 18:10:00 +0100337
Steve McIntyre3dfd36f2015-02-12 06:37:56 +0000338 try:
339 self._configure()
340 self._cli("interface %s" % port)
341 self._cli("switchport trunk allowed vlan remove %d" % tag)
342 self._end_configure()
343
344 # Validate it happened
345 read_vlans = self.port_get_trunk_vlan_list(port)
346 for vlan in read_vlans:
347 if vlan == tag:
348 raise IOError("Failed to remove trunk port %s from VLAN %d" % (port, tag))
349
350 except PExpectError:
351 # recurse on error
352 self._switch_connect()
353 self.port_remove_trunk_from_vlan(port, tag)
Steve McIntyred6759dd2014-08-12 18:10:00 +0100354
Steve McIntyre9936d002014-10-01 15:54:10 +0100355 # Get the configured VLAN tag for an access port (tag)
356 def port_get_access_vlan(self, port):
357 logging.debug("Getting VLAN for access port %s" % port)
Steve McIntyred6759dd2014-08-12 18:10:00 +0100358 vlan = 1
359 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000360 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9936d002014-10-01 15:54:10 +0100361 if not (self.port_get_mode(port) == "access"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000362 raise InputError("Port %s not in access mode" % port)
Steve McIntyre3f287882014-08-18 19:02:15 +0100363 regex = re.compile('Access Mode VLAN: (\d+)')
Steve McIntyre3dfd36f2015-02-12 06:37:56 +0000364
365 try:
366 self._cli("show interfaces %s switchport" % port)
367 for line in self._read_long_output():
368 match = regex.match(line)
369 if match:
370 vlan = match.group(1)
371 return int(vlan)
372
373 except PExpectError:
374 # recurse on error
375 self._switch_connect()
376 return self.port_get_access_vlan(port)
Steve McIntyred6759dd2014-08-12 18:10:00 +0100377
378 # Get the list of configured VLAN tags for a trunk port
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100379 def port_get_trunk_vlan_list(self, port):
Steve McIntyred6759dd2014-08-12 18:10:00 +0100380 logging.debug("Getting VLANs for trunk port %s" % port)
381 vlans = [ ]
382 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000383 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100384 if not (self.port_get_mode(port) == "trunk"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000385 raise InputError("Port %s not in trunk mode" % port)
Steve McIntyre3f287882014-08-18 19:02:15 +0100386 regex_start = re.compile('Trunking VLANs Enabled: (.*)')
387 regex_continue = re.compile('\s*(\d.*)')
Steve McIntyre3f287882014-08-18 19:02:15 +0100388
Steve McIntyre3dfd36f2015-02-12 06:37:56 +0000389 try:
390 self._cli("show interfaces %s switchport" % port)
391
392 # Horrible parsing work - VLAN list may extend over several lines
393 in_match = False
394 vlan_text = ''
395
396 for line in self._read_long_output():
397 if in_match:
398 match = regex_continue.match(line)
399 if match:
400 vlan_text += match.group(1)
401 next
402 else:
403 in_match = False
404 next
Steve McIntyre3f287882014-08-18 19:02:15 +0100405 else:
Steve McIntyre3dfd36f2015-02-12 06:37:56 +0000406 match = regex_start.match(line)
407 if match:
408 vlan_text += match.group(1)
409 in_match = True
Steve McIntyre3f287882014-08-18 19:02:15 +0100410
Steve McIntyre3dfd36f2015-02-12 06:37:56 +0000411 vlans = self._parse_vlan_list(vlan_text)
412 return vlans
Steve McIntyre3f287882014-08-18 19:02:15 +0100413
Steve McIntyre3dfd36f2015-02-12 06:37:56 +0000414 except PExpectError:
415 # recurse on error
416 self._switch_connect()
417 return self.port_get_trunk_vlan_list(port)
Steve McIntyred6759dd2014-08-12 18:10:00 +0100418
419 ################################
420 ### Internal functions
421 ################################
422
Steve McIntyre3dfd36f2015-02-12 06:37:56 +0000423 # Connect to the switch and log in
424 def _switch_connect(self):
425
426 if not self.connection is None:
427 self.connection.close(True)
428 self.connection = None
429
430 logging.debug("Connecting to Switch with: %s" % self.exec_string)
431 self.connection = pexpect.spawn(self.exec_string, logfile = self.logfile)
432
433 self._login()
434
435 # Avoid paged output
436 self._cli("terminal length 0")
437
438 # And grab details about the switch. in case we need it
439 self._get_systemdata()
440
441 # And also validate them - make sure we're driving a switch of
442 # the correct model! Also store the serial number
443 descr_regex = re.compile('^cisco\s+(\S+)')
444 sn_regex = re.compile('System serial number\s+:\s+(\S+)')
445 descr = ""
446
447 for line in self._systemdata:
448 match = descr_regex.match(line)
449 if match:
450 descr = match.group(1)
451 match = sn_regex.match(line)
452 if match:
453 self.serial_number = match.group(1)
454
455 logging.debug("serial number is %s" % self.serial_number)
456 logging.debug("system description is %s" % descr)
457
458 if not self._expected_descr_re.match(descr):
459 raise IOError("Switch %s not recognised by this driver: abort" % descr)
460
461 # Now build a list of our ports, for later sanity checking
462 self._ports = self._get_port_names()
463 if len(self._ports) < 4:
464 raise IOError("Not enough ports detected - problem!")
465
466 def _login(self):
467 logging.debug("attempting login with username %s, password %s" % (self._username, self._password))
Steve McIntyred6759dd2014-08-12 18:10:00 +0100468 self.connection.expect('User Access Verification')
Steve McIntyre3dfd36f2015-02-12 06:37:56 +0000469 if self._username is not None:
Steve McIntyred6759dd2014-08-12 18:10:00 +0100470 self.connection.expect("User Name:")
Steve McIntyre3dfd36f2015-02-12 06:37:56 +0000471 self._cli("%s" % self._username)
472 if self._password is not None:
Steve McIntyred6759dd2014-08-12 18:10:00 +0100473 self.connection.expect("Password:")
Steve McIntyre3dfd36f2015-02-12 06:37:56 +0000474 self._cli("%s" % self._password, False)
Steve McIntyred6759dd2014-08-12 18:10:00 +0100475 while True:
476 index = self.connection.expect(['User Name:', 'Password:', 'Bad passwords', 'authentication failed', r'(.*)(#|>)'])
477 if index != 4: # Any other means: failed to log in!
478 logging.error("Login failure: index %d\n" % index)
479 logging.error("Login failure: %s\n" % self.connection.match.before)
480 raise IOError
481
482 # else
Steve McIntyre3f287882014-08-18 19:02:15 +0100483 self._prompt_name = self.connection.match.group(1).strip()
Steve McIntyred6759dd2014-08-12 18:10:00 +0100484 if self.connection.match.group(2) == ">":
485 # Need to enter "enable" mode too
486 self._cli("enable")
Steve McIntyread12cd72015-02-12 06:41:29 +0000487 if self._enable_password is not None:
Steve McIntyred6759dd2014-08-12 18:10:00 +0100488 self.connection.expect("Password:")
Steve McIntyread12cd72015-02-12 06:41:29 +0000489 self._cli("%s" % self._enable_password, False)
Steve McIntyred6759dd2014-08-12 18:10:00 +0100490 index = self.connection.expect(['Password:', 'Bad passwords', 'authentication failed', r'(.*)(#|>)'])
491 if index != 3: # Any other means: failed to log in!
492 logging.error("Enable password failure: %s\n" % self.connection.match)
493 raise IOError
494 return 0
495
496 def _logout(self):
497 logging.debug("Logging out")
498 self._cli("exit", False)
499
500 def _configure(self):
501 self._cli("configure terminal")
502
503 def _end_configure(self):
504 self._cli("end")
505
Steve McIntyrec68d6d72014-12-24 00:23:36 +0000506 def _read_long_output(self):
Steve McIntyre3f287882014-08-18 19:02:15 +0100507 prompt = self._prompt_name + '#'
Steve McIntyre3dfd36f2015-02-12 06:37:56 +0000508 try:
509 self.connection.expect(prompt)
510 except (pexpect.EOF, pexpect.TIMEOUT):
511 # Something went wrong; logout, log in and try again!
Steve McIntyre7a9c8192015-02-12 07:15:52 +0000512 logging.error("PEXPECT FAILURE, RECONNECT")
Steve McIntyre3dfd36f2015-02-12 06:37:56 +0000513 self.errors.log_error_in("")
514 raise PExpectError("_read_long_output failed")
515 except:
Steve McIntyre7a9c8192015-02-12 07:15:52 +0000516 logging.error("prompt is \"%s\"" % prompt)
Steve McIntyre3dfd36f2015-02-12 06:37:56 +0000517 raise
518
Steve McIntyrea7cdefc2014-12-24 00:48:19 +0000519 buf = []
520 for line in self.connection.before.split('\r\n'):
521 buf.append(line.strip())
522 return buf
Steve McIntyred6759dd2014-08-12 18:10:00 +0100523
524 def _get_port_names(self):
525 logging.debug("Grabbing list of ports")
526 interfaces = []
527
528 # Use "Up" or "Down" to only identify lines in the output that
529 # match interfaces that exist
530 regex = re.compile('^\s*([a-zA-Z0-9_/]*).*(connect)(.*)')
531 regex1 = re.compile('.*Not Present.*')
Steve McIntyre6c279b42014-12-23 22:09:04 +0000532 regex2 = re.compile('.*routed.*')
Steve McIntyred6759dd2014-08-12 18:10:00 +0100533
Steve McIntyre3dfd36f2015-02-12 06:37:56 +0000534 try:
535 self._cli("show interfaces status")
536 for line in self._read_long_output():
537 match = regex.match(line)
538 if match:
539 interface = match.group(1)
540 junk = match.group(3)
541 match1 = regex1.match(junk) # Deliberately drop things
542 # marked as "Not Present"
543 match2 = regex2.match(junk) # Deliberately drop things
544 # marked as "routed"
545 if not match1 and not match2:
546 interfaces.append(interface)
547 return interfaces
548
549 except PExpectError:
550 # recurse on error
551 self._switch_connect()
552 return self._get_port_names()
Steve McIntyred6759dd2014-08-12 18:10:00 +0100553
Steve McIntyred6759dd2014-08-12 18:10:00 +0100554 def _show_config(self):
555 logging.debug("Grabbing config")
Steve McIntyre3dfd36f2015-02-12 06:37:56 +0000556 try:
557 self._cli("show running-config")
558 return self._read_long_output()
559 except PExpectError:
560 # recurse on error
561 self._switch_connect()
562 return self._show_config()
Steve McIntyred6759dd2014-08-12 18:10:00 +0100563
564 def _show_clock(self):
565 logging.debug("Grabbing time")
Steve McIntyre3dfd36f2015-02-12 06:37:56 +0000566 try:
567 self._cli("show clock")
568 return self._read_long_output()
569 except PExpectError:
570 # recurse on error
571 self._switch_connect()
572 return self._show_clock()
Steve McIntyred6759dd2014-08-12 18:10:00 +0100573
Steve McIntyred6759dd2014-08-12 18:10:00 +0100574 def _get_systemdata(self):
Steve McIntyred6759dd2014-08-12 18:10:00 +0100575 logging.debug("Grabbing system sw and hw versions")
Steve McIntyreffb9b5a2014-10-10 16:31:58 +0100576
Steve McIntyre3dfd36f2015-02-12 06:37:56 +0000577 try:
578 self._systemdata = []
579 self._cli("show version")
580 for line in self._read_long_output():
581 self._systemdata.append(line)
582
583 except PExpectError:
584 # recurse on error
585 self._switch_connect()
586 return self._get_systemdata()
Steve McIntyred6759dd2014-08-12 18:10:00 +0100587
Steve McIntyre2b4c07b2014-12-22 16:10:04 +0000588 def _parse_vlan_list(self, inputdata):
Steve McIntyre3f287882014-08-18 19:02:15 +0100589 vlans = []
590
Steve McIntyre2b4c07b2014-12-22 16:10:04 +0000591 if inputdata == "ALL":
Steve McIntyre3f287882014-08-18 19:02:15 +0100592 return ["ALL"]
Steve McIntyre2b4c07b2014-12-22 16:10:04 +0000593 elif inputdata == "NONE":
Steve McIntyre3f287882014-08-18 19:02:15 +0100594 return []
595 else:
596 # Parse the complex list
Steve McIntyre2b4c07b2014-12-22 16:10:04 +0000597 groups = inputdata.split(',')
Steve McIntyre3f287882014-08-18 19:02:15 +0100598 for group in groups:
599 subgroups = group.split('-')
600 if len(subgroups) == 1:
601 vlans.append(int(subgroups[0]))
602 elif len(subgroups) == 2:
603 for i in range (int(subgroups[0]), int(subgroups[1]) + 1):
604 vlans.append(i)
605 else:
Steve McIntyre6d5594f2014-12-23 14:28:47 +0000606 logging.debug("Can't parse group \"" + group + "\"")
Steve McIntyre3f287882014-08-18 19:02:15 +0100607
608 return vlans
Steve McIntyred6759dd2014-08-12 18:10:00 +0100609
610 # Wrapper around connection.send - by default, expect() the same
611 # text we've sent, to remove it from the output from the
612 # switch. For the few cases where we don't need that, override
613 # this using echo=False.
614 # Horrible, but seems to work.
615 def _cli(self, text, echo=True):
616 self.connection.send(text + '\r')
617 if echo:
Steve McIntyre3dfd36f2015-02-12 06:37:56 +0000618 try:
619 self.connection.expect(text)
620 except (pexpect.EOF, pexpect.TIMEOUT):
621 # Something went wrong; logout, log in and try again!
Steve McIntyre7a9c8192015-02-12 07:15:52 +0000622 logging.error("PEXPECT FAILURE, RECONNECT")
Steve McIntyre3dfd36f2015-02-12 06:37:56 +0000623 self.errors.log_error_out(text)
624 raise PExpectError("_cli failed on %s" % text)
625 except:
Steve McIntyrea67473a2015-02-12 08:26:33 +0000626 logging.error("Unexpected error: %s", sys.exc_info()[0])
Steve McIntyre3dfd36f2015-02-12 06:37:56 +0000627 raise
Steve McIntyred6759dd2014-08-12 18:10:00 +0100628
629if __name__ == "__main__":
Steve McIntyre48dc6ae2014-12-23 16:08:19 +0000630
631 import optparse
632
633 switch = 'vlandswitch01'
634 parser = optparse.OptionParser()
635 parser.add_option("--switch",
636 dest = "switch",
637 action = "store",
638 nargs = 1,
639 type = "string",
640 help = "specify switch to connect to for testing",
641 metavar = "<switch>")
642 (opts, args) = parser.parse_args()
643 if opts.switch:
644 switch = opts.switch
645
646 p = CiscoCatalyst(switch, 23, debug=True)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100647 p.switch_connect(None, 'lngvirtual', 'lngenable')
Steve McIntyred6759dd2014-08-12 18:10:00 +0100648
649 print "VLANs are:"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100650 buf = p.vlan_get_list()
Steve McIntyred6759dd2014-08-12 18:10:00 +0100651 p._dump_list(buf)
652
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100653 buf = p.vlan_get_name(2)
Steve McIntyred6759dd2014-08-12 18:10:00 +0100654 print "VLAN 2 is named \"%s\"" % buf
655
656 print "Create VLAN 3"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100657 p.vlan_create(3)
Steve McIntyred6759dd2014-08-12 18:10:00 +0100658
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100659 buf = p.vlan_get_name(3)
Steve McIntyred6759dd2014-08-12 18:10:00 +0100660 print "VLAN 3 is named \"%s\"" % buf
661
662 print "Set name of VLAN 3 to test333"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100663 p.vlan_set_name(3, "test333")
Steve McIntyred6759dd2014-08-12 18:10:00 +0100664
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100665 buf = p.vlan_get_name(3)
Steve McIntyred6759dd2014-08-12 18:10:00 +0100666 print "VLAN 3 is named \"%s\"" % buf
667
668 print "VLANs are:"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100669 buf = p.vlan_get_list()
Steve McIntyred6759dd2014-08-12 18:10:00 +0100670 p._dump_list(buf)
671
672 print "Destroy VLAN 3"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100673 p.vlan_destroy(3)
Steve McIntyred6759dd2014-08-12 18:10:00 +0100674
675 print "VLANs are:"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100676 buf = p.vlan_get_list()
Steve McIntyred6759dd2014-08-12 18:10:00 +0100677 p._dump_list(buf)
678
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100679 buf = p.port_get_mode("Gi1/0/10")
Steve McIntyreb7adc782014-08-13 00:22:21 +0100680 print "Port Gi1/0/10 is in %s mode" % buf
681
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100682 buf = p.port_get_mode("Gi1/0/11")
Steve McIntyreb7adc782014-08-13 00:22:21 +0100683 print "Port Gi1/0/11 is in %s mode" % buf
Steve McIntyred6759dd2014-08-12 18:10:00 +0100684
Steve McIntyre9936d002014-10-01 15:54:10 +0100685 # Test access stuff
686 print "Set Gi1/0/9 to access mode"
687 p.port_set_mode("Gi1/0/9", "access")
Steve McIntyre3f287882014-08-18 19:02:15 +0100688
689 print "Move Gi1/0/9 to VLAN 4"
Steve McIntyre9936d002014-10-01 15:54:10 +0100690 p.port_set_access_vlan("Gi1/0/9", 4)
Steve McIntyre3f287882014-08-18 19:02:15 +0100691
Steve McIntyre9936d002014-10-01 15:54:10 +0100692 buf = p.port_get_access_vlan("Gi1/0/9")
Steve McIntyre3f287882014-08-18 19:02:15 +0100693 print "Read from switch: Gi1/0/9 is on VLAN %s" % buf
694
695 print "Move Gi1/0/9 back to VLAN 1"
Steve McIntyre9936d002014-10-01 15:54:10 +0100696 p.port_set_access_vlan("Gi1/0/9", 1)
Steve McIntyre3f287882014-08-18 19:02:15 +0100697
Steve McIntyre9936d002014-10-01 15:54:10 +0100698 # Test access stuff
Steve McIntyre3f287882014-08-18 19:02:15 +0100699 print "Set Gi1/0/9 to trunk mode"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100700 p.port_set_mode("Gi1/0/9", "trunk")
Steve McIntyre3f287882014-08-18 19:02:15 +0100701 print "Read from switch: which VLANs is Gi1/0/9 on?"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100702 buf = p.port_get_trunk_vlan_list("Gi1/0/9")
Steve McIntyre3f287882014-08-18 19:02:15 +0100703 p._dump_list(buf)
704 print "Add Gi1/0/9 to VLAN 2"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100705 p.port_add_trunk_to_vlan("Gi1/0/9", 2)
Steve McIntyre3f287882014-08-18 19:02:15 +0100706 print "Add Gi1/0/9 to VLAN 3"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100707 p.port_add_trunk_to_vlan("Gi1/0/9", 3)
Steve McIntyre3f287882014-08-18 19:02:15 +0100708 print "Add Gi1/0/9 to VLAN 4"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100709 p.port_add_trunk_to_vlan("Gi1/0/9", 4)
Steve McIntyre3f287882014-08-18 19:02:15 +0100710 print "Read from switch: which VLANs is Gi1/0/9 on?"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100711 buf = p.port_get_trunk_vlan_list("Gi1/0/9")
Steve McIntyre3f287882014-08-18 19:02:15 +0100712 p._dump_list(buf)
713
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100714 p.port_remove_trunk_from_vlan("Gi1/0/9", 3)
715 p.port_remove_trunk_from_vlan("Gi1/0/9", 3)
716 p.port_remove_trunk_from_vlan("Gi1/0/9", 4)
Steve McIntyre3f287882014-08-18 19:02:15 +0100717 print "Read from switch: which VLANs is Gi1/0/9 on?"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100718 buf = p.port_get_trunk_vlan_list("Gi1/0/9")
Steve McIntyre3f287882014-08-18 19:02:15 +0100719 p._dump_list(buf)
720
Steve McIntyre7460d972014-12-23 14:45:30 +0000721# print 'Restarting switch, to explicitly reset config'
722# p.switch_restart()
Steve McIntyre3f287882014-08-18 19:02:15 +0100723
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100724# p.switch_save_running_config()
Steve McIntyred6759dd2014-08-12 18:10:00 +0100725
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100726# p.switch_disconnect()
Steve McIntyred6759dd2014-08-12 18:10:00 +0100727# p._show_config()