blob: ca2fcd42ce8d10b9cf026e25c9056a525c95482c [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
25from common import SwitchDriver
Steve McIntyre72a8bce2015-01-23 18:02:19 +000026if __name__ == '__main__':
27 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
31from errors import CriticalError, InputError
Steve McIntyred6759dd2014-08-12 18:10:00 +010032
33class CiscoCatalyst(SwitchDriver):
34
35 connection = None
Steve McIntyre3f287882014-08-18 19:02:15 +010036
37 _capabilities = [
38 'TrunkWildCardVlans' # Trunk ports are on all VLANs by
39 # default, so we shouldn't need to
40 # bugger with them
41 ]
42
Steve McIntyred6759dd2014-08-12 18:10:00 +010043 # Regexp of expected hardware information - fail if we don't see
44 # this
Steve McIntyre3f287882014-08-18 19:02:15 +010045 _expected_descr_re = re.compile('WS-C\S+-\d+P')
Steve McIntyred6759dd2014-08-12 18:10:00 +010046
Steve McIntyred6759dd2014-08-12 18:10:00 +010047 logfile = None
48
Steve McIntyre48dc6ae2014-12-23 16:08:19 +000049 def __init__(self, switch_hostname, switch_telnetport=23, debug = False):
50 if debug:
Steve McIntyre9f5a0232014-12-23 16:14:28 +000051 self.logfile = sys.stderr
Steve McIntyred6759dd2014-08-12 18:10:00 +010052 self.exec_string = "/usr/bin/telnet %s %d" % (switch_hostname, switch_telnetport)
53
54 ################################
55 ### Switch-level API functions
56 ################################
57
58 # Connect to the switch and log in
Steve McIntyre9b09b9d2014-09-24 15:08:10 +010059 def switch_connect(self, username, password, enablepassword):
Steve McIntyred6759dd2014-08-12 18:10:00 +010060 logging.debug("Connecting to Switch with: %s" % self.exec_string)
61 self.connection = pexpect.spawn(self.exec_string, logfile = self.logfile)
62 self._login(username, password, enablepassword)
63
64 # Try to avoid paged output
Steve McIntyredd0c0012014-12-24 00:10:17 +000065 self._cli("terminal length 0")
Steve McIntyred6759dd2014-08-12 18:10:00 +010066
67 # And grab details about the switch. in case we need it
Steve McIntyre3f287882014-08-18 19:02:15 +010068 self._get_systemdata()
Steve McIntyred6759dd2014-08-12 18:10:00 +010069
70 # And also validate them - make sure we're driving a switch of
71 # the correct model! Also store the serial number
72 descr_regex = re.compile('^cisco\s+(\S+)')
73 sn_regex = re.compile('System serial number\s+:\s+(\S+)')
74 descr = ""
75
Steve McIntyre3f287882014-08-18 19:02:15 +010076 for line in self._systemdata:
Steve McIntyred6759dd2014-08-12 18:10:00 +010077 match = descr_regex.match(line)
78 if match:
79 descr = match.group(1)
80 match = sn_regex.match(line)
81 if match:
82 self.serial_number = match.group(1)
83
Steve McIntyre6d5594f2014-12-23 14:28:47 +000084 logging.debug("serial number is %s" % self.serial_number)
85 logging.debug("system description is %s" % descr)
Steve McIntyred6759dd2014-08-12 18:10:00 +010086
Steve McIntyre3f287882014-08-18 19:02:15 +010087 if not self._expected_descr_re.match(descr):
Steve McIntyred6759dd2014-08-12 18:10:00 +010088 raise IOError("Switch %s not recognised by this driver: abort" % descr)
89
90 # Now build a list of our ports, for later sanity checking
Steve McIntyre3f287882014-08-18 19:02:15 +010091 self._ports = self._get_port_names()
92 if len(self._ports) < 4:
Steve McIntyred6759dd2014-08-12 18:10:00 +010093 raise IOError("Not enough ports detected - problem!")
94
95 # Log out of the switch and drop the connection and all state
Steve McIntyre9b09b9d2014-09-24 15:08:10 +010096 def switch_disconnect(self):
Steve McIntyred6759dd2014-08-12 18:10:00 +010097 self._logout()
98 logging.debug("Closing connection: %s" % self.connection)
99 self.connection.close(True)
100 del(self)
101
102 # Save the current running config into flash - we want config to
103 # remain across reboots
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100104 def switch_save_running_config(self):
Steve McIntyred6759dd2014-08-12 18:10:00 +0100105 self._cli("copy running-config startup-config")
Steve McIntyree1bf11a2014-08-14 17:56:25 +0100106 self.connection.expect("startup-config")
107 self._cli("startup-config")
108 self.connection.expect("OK")
Steve McIntyred6759dd2014-08-12 18:10:00 +0100109
Steve McIntyre095b4452014-12-19 17:53:43 +0000110 # Restart the switch - we need to reload config to do a
111 # roll-back. Do NOT save running-config first if the switch asks -
112 # we're trying to dump recent changes, not save them.
113 #
114 # This will also implicitly cause a connection to be closed
115 def switch_restart(self):
116 self._cli("reload")
117 index = self.connection.expect(['has been modified', 'Proceed'])
118 if index == 0:
119 self._cli("n") # No, don't save
120 self.connection.expect("Proceed")
121
122 # Fall through
123 self._cli("y") # Yes, continue to reset
124 self.connection.close(True)
125
Steve McIntyre3f287882014-08-18 19:02:15 +0100126 # List the capabilities of the switch (and driver) - some things
127 # make no sense to abstract. Returns a dict of strings, each one
128 # describing an extra feature that that higher levels may care
129 # about
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100130 def switch_get_capabilities(self):
Steve McIntyre3f287882014-08-18 19:02:15 +0100131 return self._capabilities
Steve McIntyred6759dd2014-08-12 18:10:00 +0100132
133 ################################
134 ### VLAN API functions
135 ################################
136
137 # Create a VLAN with the specified tag
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100138 def vlan_create(self, tag):
Steve McIntyred6759dd2014-08-12 18:10:00 +0100139 logging.debug("Creating VLAN %d" % tag)
140 self._configure()
141 self._cli("vlan %d" % tag)
142 self._end_configure()
143
144 # Validate it happened
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100145 vlans = self.vlan_get_list()
Steve McIntyred6759dd2014-08-12 18:10:00 +0100146 for vlan in vlans:
147 if vlan == tag:
148 return
149 raise IOError("Failed to create VLAN %d" % tag)
150
151 # Destroy a VLAN with the specified tag
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100152 def vlan_destroy(self, tag):
Steve McIntyred6759dd2014-08-12 18:10:00 +0100153 logging.debug("Destroying VLAN %d" % tag)
154 self._configure()
155 self._cli("no vlan %d" % tag)
156 self._end_configure()
157
158 # Validate it happened
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100159 vlans = self.vlan_get_list()
Steve McIntyred6759dd2014-08-12 18:10:00 +0100160 for vlan in vlans:
161 if vlan == tag:
162 raise IOError("Failed to destroy VLAN %d" % tag)
163
164 # Set the name of a VLAN
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100165 def vlan_set_name(self, tag, name):
Steve McIntyred6759dd2014-08-12 18:10:00 +0100166 logging.debug("Setting name of VLAN %d to %s" % (tag, name))
167 self._configure()
168 self._cli("vlan %d" % tag)
169 self._cli("name %s" % name)
170 self._end_configure()
171
172 # Validate it happened
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100173 read_name = self.vlan_get_name(tag)
Steve McIntyred6759dd2014-08-12 18:10:00 +0100174 if read_name != name:
175 raise IOError("Failed to set name for VLAN %d (name found is \"%s\", not \"%s\")"
176 % (tag, read_name, name))
177
178 # Get a list of the VLAN tags currently registered on the switch
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100179 def vlan_get_list(self):
Steve McIntyred6759dd2014-08-12 18:10:00 +0100180 logging.debug("Grabbing list of VLANs")
181 vlans = []
182
183 regex = re.compile('^ *(\d+).*(active)')
184
185 self._cli("show vlan brief")
Steve McIntyrec68d6d72014-12-24 00:23:36 +0000186 for line in self._read_long_output():
Steve McIntyred6759dd2014-08-12 18:10:00 +0100187 match = regex.match(line)
188 if match:
189 vlans.append(int(match.group(1)))
190 return vlans
191
192 # For a given VLAN tag, ask the switch what the associated name is
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100193 def vlan_get_name(self, tag):
Steve McIntyred6759dd2014-08-12 18:10:00 +0100194 logging.debug("Grabbing the name of VLAN %d" % tag)
195 name = None
196 regex = re.compile('^ *\d+\s+(\S+).*(active)')
197 self._cli("show vlan id %d" % tag)
Steve McIntyrec68d6d72014-12-24 00:23:36 +0000198 for line in self._read_long_output():
Steve McIntyred6759dd2014-08-12 18:10:00 +0100199 match = regex.match(line)
200 if match:
201 name = match.group(1)
202 name.strip()
203 return name
204
205
206 ################################
207 ### Port API functions
Steve McIntyree1bf11a2014-08-14 17:56:25 +0100208 ################################
Steve McIntyred6759dd2014-08-12 18:10:00 +0100209
Steve McIntyre9936d002014-10-01 15:54:10 +0100210 # Set the mode of a port: access or trunk
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100211 def port_set_mode(self, port, mode):
Steve McIntyred6759dd2014-08-12 18:10:00 +0100212 logging.debug("Setting port %s to %s" % (port, mode))
213 if not self._is_port_mode_valid(mode):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000214 raise InputError("Port mode %s is not allowed" % mode)
Steve McIntyred6759dd2014-08-12 18:10:00 +0100215 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000216 raise InputError("Port name %s not recognised" % port)
Steve McIntyre3f287882014-08-18 19:02:15 +0100217
Steve McIntyred6759dd2014-08-12 18:10:00 +0100218 self._configure()
219 self._cli("interface %s" % port)
Steve McIntyre9936d002014-10-01 15:54:10 +0100220 self._cli("switchport mode %s" % mode)
221 if mode == "trunk":
222 self._cli("switchport trunk encapsulation dot1q")
Steve McIntyred6759dd2014-08-12 18:10:00 +0100223 self._end_configure()
224
225 # Validate it happened
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100226 read_mode = self.port_get_mode(port)
Steve McIntyre3f287882014-08-18 19:02:15 +0100227
Steve McIntyred6759dd2014-08-12 18:10:00 +0100228 if read_mode != mode:
229 raise IOError("Failed to set mode for port %s" % port)
230
Steve McIntyre9936d002014-10-01 15:54:10 +0100231 # Get the mode of a port: access or trunk
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100232 def port_get_mode(self, port):
Steve McIntyred6759dd2014-08-12 18:10:00 +0100233 logging.debug("Getting mode of port %s" % port)
234 mode = ''
235 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000236 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9936d002014-10-01 15:54:10 +0100237 regex = re.compile('Administrative Mode: (.*)')
Steve McIntyreb7adc782014-08-13 00:22:21 +0100238 self._cli("show interfaces %s switchport" % port)
Steve McIntyrec68d6d72014-12-24 00:23:36 +0000239 for line in self._read_long_output():
Steve McIntyred6759dd2014-08-12 18:10:00 +0100240 match = regex.match(line)
241 if match:
242 mode = match.group(1)
Steve McIntyre9936d002014-10-01 15:54:10 +0100243 if mode == 'static access':
244 return 'access'
Steve McIntyre6c279b42014-12-23 22:09:04 +0000245 if mode == 'dynamic auto':
246 return 'trunk'
Steve McIntyre3f287882014-08-18 19:02:15 +0100247 return mode
Steve McIntyred6759dd2014-08-12 18:10:00 +0100248
Steve McIntyre9936d002014-10-01 15:54:10 +0100249 # Set an access port to be in a specified VLAN (tag)
250 def port_set_access_vlan(self, port, tag):
251 logging.debug("Setting access port %s to VLAN %d" % (port, tag))
Steve McIntyred6759dd2014-08-12 18:10:00 +0100252 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000253 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9936d002014-10-01 15:54:10 +0100254 if not (self.port_get_mode(port) == "access"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000255 raise InputError("Port %s not in access mode" % port)
Steve McIntyre3f287882014-08-18 19:02:15 +0100256
Steve McIntyred6759dd2014-08-12 18:10:00 +0100257 self._configure()
258 self._cli("interface %s" % port)
Steve McIntyre3f287882014-08-18 19:02:15 +0100259 self._cli("switchport access vlan %d" % tag)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100260 self._cli("no shutdown")
Steve McIntyred6759dd2014-08-12 18:10:00 +0100261 self._end_configure()
Steve McIntyred6759dd2014-08-12 18:10:00 +0100262
Steve McIntyre3f287882014-08-18 19:02:15 +0100263 # Finally, validate things worked
Steve McIntyre9936d002014-10-01 15:54:10 +0100264 read_vlan = int(self.port_get_access_vlan(port))
Steve McIntyred6759dd2014-08-12 18:10:00 +0100265 if read_vlan != tag:
Steve McIntyre9936d002014-10-01 15:54:10 +0100266 raise IOError("Failed to move access port %d to VLAN %d - got VLAN %d instead"
Steve McIntyred6759dd2014-08-12 18:10:00 +0100267 % (port, tag, read_vlan))
268
Steve McIntyred6759dd2014-08-12 18:10:00 +0100269 # Add a trunk port to a specified VLAN (tag)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100270 def port_add_trunk_to_vlan(self, port, tag):
Steve McIntyred6759dd2014-08-12 18:10:00 +0100271 logging.debug("Adding trunk port %s to VLAN %d" % (port, tag))
272 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000273 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100274 if not (self.port_get_mode(port) == "trunk"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000275 raise InputError("Port %s not in trunk mode" % port)
Steve McIntyred6759dd2014-08-12 18:10:00 +0100276 self._configure()
277 self._cli("interface %s" % port)
278 self._cli("switchport trunk allowed vlan add %d" % tag)
279 self._end_configure()
280
281 # Validate it happened
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100282 read_vlans = self.port_get_trunk_vlan_list(port)
Steve McIntyred6759dd2014-08-12 18:10:00 +0100283 for vlan in read_vlans:
Steve McIntyre3f287882014-08-18 19:02:15 +0100284 if vlan == tag or vlan == "ALL":
Steve McIntyred6759dd2014-08-12 18:10:00 +0100285 return
Steve McIntyre3f287882014-08-18 19:02:15 +0100286 raise IOError("Failed to add trunk port %s to VLAN %d" % (port, tag))
Steve McIntyred6759dd2014-08-12 18:10:00 +0100287
288 # Remove a trunk port from a specified VLAN (tag)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100289 def port_remove_trunk_from_vlan(self, port, tag):
Steve McIntyred6759dd2014-08-12 18:10:00 +0100290 logging.debug("Removing trunk port %s from VLAN %d" % (port, tag))
291 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000292 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100293 if not (self.port_get_mode(port) == "trunk"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000294 raise InputError("Port %s not in trunk mode" % port)
Steve McIntyred6759dd2014-08-12 18:10:00 +0100295 self._configure()
296 self._cli("interface %s" % port)
297 self._cli("switchport trunk allowed vlan remove %d" % tag)
298 self._end_configure()
299
300 # Validate it happened
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100301 read_vlans = self.port_get_trunk_vlan_list(port)
Steve McIntyred6759dd2014-08-12 18:10:00 +0100302 for vlan in read_vlans:
303 if vlan == tag:
Steve McIntyre3f287882014-08-18 19:02:15 +0100304 raise IOError("Failed to remove trunk port %s from VLAN %d" % (port, tag))
Steve McIntyred6759dd2014-08-12 18:10:00 +0100305
Steve McIntyre9936d002014-10-01 15:54:10 +0100306 # Get the configured VLAN tag for an access port (tag)
307 def port_get_access_vlan(self, port):
308 logging.debug("Getting VLAN for access port %s" % port)
Steve McIntyred6759dd2014-08-12 18:10:00 +0100309 vlan = 1
310 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000311 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9936d002014-10-01 15:54:10 +0100312 if not (self.port_get_mode(port) == "access"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000313 raise InputError("Port %s not in access mode" % port)
Steve McIntyre3f287882014-08-18 19:02:15 +0100314 regex = re.compile('Access Mode VLAN: (\d+)')
315 self._cli("show interfaces %s switchport" % port)
Steve McIntyrec68d6d72014-12-24 00:23:36 +0000316 for line in self._read_long_output():
Steve McIntyred6759dd2014-08-12 18:10:00 +0100317 match = regex.match(line)
318 if match:
319 vlan = match.group(1)
320 return int(vlan)
321
322 # Get the list of configured VLAN tags for a trunk port
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100323 def port_get_trunk_vlan_list(self, port):
Steve McIntyred6759dd2014-08-12 18:10:00 +0100324 logging.debug("Getting VLANs for trunk port %s" % port)
325 vlans = [ ]
326 if not self._is_port_name_valid(port):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000327 raise InputError("Port name %s not recognised" % port)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100328 if not (self.port_get_mode(port) == "trunk"):
Steve McIntyre72a8bce2015-01-23 18:02:19 +0000329 raise InputError("Port %s not in trunk mode" % port)
Steve McIntyre3f287882014-08-18 19:02:15 +0100330 regex_start = re.compile('Trunking VLANs Enabled: (.*)')
331 regex_continue = re.compile('\s*(\d.*)')
332 self._cli("show interfaces %s switchport" % port)
333
334 # Horrible parsing work - VLAN list may extend over several lines
335 in_match = False
336 vlan_text = ''
337
Steve McIntyrec68d6d72014-12-24 00:23:36 +0000338 for line in self._read_long_output():
Steve McIntyre3f287882014-08-18 19:02:15 +0100339 if in_match:
340 match = regex_continue.match(line)
341 if match:
342 vlan_text += match.group(1)
343 next
344 else:
345 in_match = False
346 next
347 else:
348 match = regex_start.match(line)
349 if match:
350 vlan_text += match.group(1)
351 in_match = True
352
353 vlans = self._parse_vlan_list(vlan_text)
354
Steve McIntyred6759dd2014-08-12 18:10:00 +0100355 return vlans
356
357 ################################
358 ### Internal functions
359 ################################
360
361 def _login(self, username, password, enablepassword):
362 logging.debug("attempting login with username %s, password %s" % (username, password))
363 self.connection.expect('User Access Verification')
364 if username is not None:
365 self.connection.expect("User Name:")
366 self._cli("%s" % username)
367 if password is not None:
368 self.connection.expect("Password:")
369 self._cli("%s" % password, False)
370 while True:
371 index = self.connection.expect(['User Name:', 'Password:', 'Bad passwords', 'authentication failed', r'(.*)(#|>)'])
372 if index != 4: # Any other means: failed to log in!
373 logging.error("Login failure: index %d\n" % index)
374 logging.error("Login failure: %s\n" % self.connection.match.before)
375 raise IOError
376
377 # else
Steve McIntyre3f287882014-08-18 19:02:15 +0100378 self._prompt_name = self.connection.match.group(1).strip()
Steve McIntyred6759dd2014-08-12 18:10:00 +0100379 if self.connection.match.group(2) == ">":
380 # Need to enter "enable" mode too
381 self._cli("enable")
382 if enablepassword is not None:
383 self.connection.expect("Password:")
384 self._cli("%s" % enablepassword, False)
385 index = self.connection.expect(['Password:', 'Bad passwords', 'authentication failed', r'(.*)(#|>)'])
386 if index != 3: # Any other means: failed to log in!
387 logging.error("Enable password failure: %s\n" % self.connection.match)
388 raise IOError
389 return 0
390
391 def _logout(self):
392 logging.debug("Logging out")
393 self._cli("exit", False)
394
395 def _configure(self):
396 self._cli("configure terminal")
397
398 def _end_configure(self):
399 self._cli("end")
400
Steve McIntyrec68d6d72014-12-24 00:23:36 +0000401 def _read_long_output(self):
Steve McIntyre3f287882014-08-18 19:02:15 +0100402 prompt = self._prompt_name + '#'
Steve McIntyrec68d6d72014-12-24 00:23:36 +0000403 self.connection.expect(prompt)
Steve McIntyrea7cdefc2014-12-24 00:48:19 +0000404 buf = []
405 for line in self.connection.before.split('\r\n'):
406 buf.append(line.strip())
407 return buf
Steve McIntyred6759dd2014-08-12 18:10:00 +0100408
409 def _get_port_names(self):
410 logging.debug("Grabbing list of ports")
411 interfaces = []
412
413 # Use "Up" or "Down" to only identify lines in the output that
414 # match interfaces that exist
415 regex = re.compile('^\s*([a-zA-Z0-9_/]*).*(connect)(.*)')
416 regex1 = re.compile('.*Not Present.*')
Steve McIntyre6c279b42014-12-23 22:09:04 +0000417 regex2 = re.compile('.*routed.*')
Steve McIntyred6759dd2014-08-12 18:10:00 +0100418
419 self._cli("show interfaces status")
Steve McIntyreac5047e2014-12-24 00:26:40 +0000420 for line in self._read_long_output():
Steve McIntyred6759dd2014-08-12 18:10:00 +0100421 match = regex.match(line)
422 if match:
423 interface = match.group(1)
424 junk = match.group(3)
425 match1 = regex1.match(junk) # Deliberately drop things
426 # marked as "Not Present"
Steve McIntyre6c279b42014-12-23 22:09:04 +0000427 match2 = regex2.match(junk) # Deliberately drop things
428 # marked as "routed"
429 if not match1 and not match2:
Steve McIntyred6759dd2014-08-12 18:10:00 +0100430 interfaces.append(interface)
431 return interfaces
432
Steve McIntyred6759dd2014-08-12 18:10:00 +0100433 def _show_config(self):
434 logging.debug("Grabbing config")
435 self._cli("show running-config")
Steve McIntyreac5047e2014-12-24 00:26:40 +0000436 return self._read_long_output()
Steve McIntyred6759dd2014-08-12 18:10:00 +0100437
438 def _show_clock(self):
439 logging.debug("Grabbing time")
440 self._cli("show clock")
Steve McIntyreac5047e2014-12-24 00:26:40 +0000441 return self._read_long_output()
Steve McIntyred6759dd2014-08-12 18:10:00 +0100442
Steve McIntyred6759dd2014-08-12 18:10:00 +0100443 def _get_systemdata(self):
Steve McIntyred6759dd2014-08-12 18:10:00 +0100444 logging.debug("Grabbing system sw and hw versions")
Steve McIntyreffb9b5a2014-10-10 16:31:58 +0100445
Steve McIntyred6759dd2014-08-12 18:10:00 +0100446 self._cli("show version")
Steve McIntyreffb9b5a2014-10-10 16:31:58 +0100447 self._systemdata = []
Steve McIntyreac5047e2014-12-24 00:26:40 +0000448 for line in self._read_long_output():
Steve McIntyre3f287882014-08-18 19:02:15 +0100449 self._systemdata.append(line)
Steve McIntyred6759dd2014-08-12 18:10:00 +0100450
Steve McIntyre2b4c07b2014-12-22 16:10:04 +0000451 def _parse_vlan_list(self, inputdata):
Steve McIntyre3f287882014-08-18 19:02:15 +0100452 vlans = []
453
Steve McIntyre2b4c07b2014-12-22 16:10:04 +0000454 if inputdata == "ALL":
Steve McIntyre3f287882014-08-18 19:02:15 +0100455 return ["ALL"]
Steve McIntyre2b4c07b2014-12-22 16:10:04 +0000456 elif inputdata == "NONE":
Steve McIntyre3f287882014-08-18 19:02:15 +0100457 return []
458 else:
459 # Parse the complex list
Steve McIntyre2b4c07b2014-12-22 16:10:04 +0000460 groups = inputdata.split(',')
Steve McIntyre3f287882014-08-18 19:02:15 +0100461 for group in groups:
462 subgroups = group.split('-')
463 if len(subgroups) == 1:
464 vlans.append(int(subgroups[0]))
465 elif len(subgroups) == 2:
466 for i in range (int(subgroups[0]), int(subgroups[1]) + 1):
467 vlans.append(i)
468 else:
Steve McIntyre6d5594f2014-12-23 14:28:47 +0000469 logging.debug("Can't parse group \"" + group + "\"")
Steve McIntyre3f287882014-08-18 19:02:15 +0100470
471 return vlans
Steve McIntyred6759dd2014-08-12 18:10:00 +0100472
473 # Wrapper around connection.send - by default, expect() the same
474 # text we've sent, to remove it from the output from the
475 # switch. For the few cases where we don't need that, override
476 # this using echo=False.
477 # Horrible, but seems to work.
478 def _cli(self, text, echo=True):
479 self.connection.send(text + '\r')
480 if echo:
481 self.connection.expect(text)
482
483if __name__ == "__main__":
Steve McIntyre48dc6ae2014-12-23 16:08:19 +0000484
485 import optparse
486
487 switch = 'vlandswitch01'
488 parser = optparse.OptionParser()
489 parser.add_option("--switch",
490 dest = "switch",
491 action = "store",
492 nargs = 1,
493 type = "string",
494 help = "specify switch to connect to for testing",
495 metavar = "<switch>")
496 (opts, args) = parser.parse_args()
497 if opts.switch:
498 switch = opts.switch
499
500 p = CiscoCatalyst(switch, 23, debug=True)
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100501 p.switch_connect(None, 'lngvirtual', 'lngenable')
Steve McIntyred6759dd2014-08-12 18:10:00 +0100502
503 print "VLANs are:"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100504 buf = p.vlan_get_list()
Steve McIntyred6759dd2014-08-12 18:10:00 +0100505 p._dump_list(buf)
506
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100507 buf = p.vlan_get_name(2)
Steve McIntyred6759dd2014-08-12 18:10:00 +0100508 print "VLAN 2 is named \"%s\"" % buf
509
510 print "Create VLAN 3"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100511 p.vlan_create(3)
Steve McIntyred6759dd2014-08-12 18:10:00 +0100512
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100513 buf = p.vlan_get_name(3)
Steve McIntyred6759dd2014-08-12 18:10:00 +0100514 print "VLAN 3 is named \"%s\"" % buf
515
516 print "Set name of VLAN 3 to test333"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100517 p.vlan_set_name(3, "test333")
Steve McIntyred6759dd2014-08-12 18:10:00 +0100518
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100519 buf = p.vlan_get_name(3)
Steve McIntyred6759dd2014-08-12 18:10:00 +0100520 print "VLAN 3 is named \"%s\"" % buf
521
522 print "VLANs are:"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100523 buf = p.vlan_get_list()
Steve McIntyred6759dd2014-08-12 18:10:00 +0100524 p._dump_list(buf)
525
526 print "Destroy VLAN 3"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100527 p.vlan_destroy(3)
Steve McIntyred6759dd2014-08-12 18:10:00 +0100528
529 print "VLANs are:"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100530 buf = p.vlan_get_list()
Steve McIntyred6759dd2014-08-12 18:10:00 +0100531 p._dump_list(buf)
532
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100533 buf = p.port_get_mode("Gi1/0/10")
Steve McIntyreb7adc782014-08-13 00:22:21 +0100534 print "Port Gi1/0/10 is in %s mode" % buf
535
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100536 buf = p.port_get_mode("Gi1/0/11")
Steve McIntyreb7adc782014-08-13 00:22:21 +0100537 print "Port Gi1/0/11 is in %s mode" % buf
Steve McIntyred6759dd2014-08-12 18:10:00 +0100538
Steve McIntyre9936d002014-10-01 15:54:10 +0100539 # Test access stuff
540 print "Set Gi1/0/9 to access mode"
541 p.port_set_mode("Gi1/0/9", "access")
Steve McIntyre3f287882014-08-18 19:02:15 +0100542
543 print "Move Gi1/0/9 to VLAN 4"
Steve McIntyre9936d002014-10-01 15:54:10 +0100544 p.port_set_access_vlan("Gi1/0/9", 4)
Steve McIntyre3f287882014-08-18 19:02:15 +0100545
Steve McIntyre9936d002014-10-01 15:54:10 +0100546 buf = p.port_get_access_vlan("Gi1/0/9")
Steve McIntyre3f287882014-08-18 19:02:15 +0100547 print "Read from switch: Gi1/0/9 is on VLAN %s" % buf
548
549 print "Move Gi1/0/9 back to VLAN 1"
Steve McIntyre9936d002014-10-01 15:54:10 +0100550 p.port_set_access_vlan("Gi1/0/9", 1)
Steve McIntyre3f287882014-08-18 19:02:15 +0100551
Steve McIntyre9936d002014-10-01 15:54:10 +0100552 # Test access stuff
Steve McIntyre3f287882014-08-18 19:02:15 +0100553 print "Set Gi1/0/9 to trunk mode"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100554 p.port_set_mode("Gi1/0/9", "trunk")
Steve McIntyre3f287882014-08-18 19:02:15 +0100555 print "Read from switch: which VLANs is Gi1/0/9 on?"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100556 buf = p.port_get_trunk_vlan_list("Gi1/0/9")
Steve McIntyre3f287882014-08-18 19:02:15 +0100557 p._dump_list(buf)
558 print "Add Gi1/0/9 to VLAN 2"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100559 p.port_add_trunk_to_vlan("Gi1/0/9", 2)
Steve McIntyre3f287882014-08-18 19:02:15 +0100560 print "Add Gi1/0/9 to VLAN 3"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100561 p.port_add_trunk_to_vlan("Gi1/0/9", 3)
Steve McIntyre3f287882014-08-18 19:02:15 +0100562 print "Add Gi1/0/9 to VLAN 4"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100563 p.port_add_trunk_to_vlan("Gi1/0/9", 4)
Steve McIntyre3f287882014-08-18 19:02:15 +0100564 print "Read from switch: which VLANs is Gi1/0/9 on?"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100565 buf = p.port_get_trunk_vlan_list("Gi1/0/9")
Steve McIntyre3f287882014-08-18 19:02:15 +0100566 p._dump_list(buf)
567
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100568 p.port_remove_trunk_from_vlan("Gi1/0/9", 3)
569 p.port_remove_trunk_from_vlan("Gi1/0/9", 3)
570 p.port_remove_trunk_from_vlan("Gi1/0/9", 4)
Steve McIntyre3f287882014-08-18 19:02:15 +0100571 print "Read from switch: which VLANs is Gi1/0/9 on?"
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100572 buf = p.port_get_trunk_vlan_list("Gi1/0/9")
Steve McIntyre3f287882014-08-18 19:02:15 +0100573 p._dump_list(buf)
574
Steve McIntyre7460d972014-12-23 14:45:30 +0000575# print 'Restarting switch, to explicitly reset config'
576# p.switch_restart()
Steve McIntyre3f287882014-08-18 19:02:15 +0100577
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100578# p.switch_save_running_config()
Steve McIntyred6759dd2014-08-12 18:10:00 +0100579
Steve McIntyre9b09b9d2014-09-24 15:08:10 +0100580# p.switch_disconnect()
Steve McIntyred6759dd2014-08-12 18:10:00 +0100581# p._show_config()