blob: ab99c2201285fba165d9ce8ac7ed9ab5da813d06 [file] [log] [blame]
Steve McIntyrefdcb3782015-07-06 16:34:56 +01001#! /usr/bin/python
2
3# Copyright 2015 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 re
24
25# Netgear XSM family driver
26# Developed and tested against the XSM7224S in the Linaro LAVA lab
27
28if __name__ == '__main__':
29 import os
30 vlandpath = os.path.abspath(os.path.normpath(os.path.dirname(sys.argv[0])))
31 sys.path.insert(0, vlandpath)
32 sys.path.insert(0, "%s/.." % vlandpath)
33
34from errors import InputError, PExpectError
35from drivers.common import SwitchDriver, SwitchErrors
36
37class NetgearXSM(SwitchDriver):
38
39 connection = None
40 _username = None
41 _password = None
42 _enable_password = None
43
44 _capabilities = [
Steve McIntyrefdcb3782015-07-06 16:34:56 +010045 ]
46
47 # Regexps of expected hardware information - fail if we don't see
48 # this
49 _expected_manuf = re.compile('^Netgear')
50 _expected_model = re.compile('^XSM')
51
Steve McIntyrefdcb3782015-07-06 16:34:56 +010052 def __init__(self, switch_hostname, switch_telnetport=23, debug = False):
Steve McIntyrebb58a272015-07-14 15:39:54 +010053 SwitchDriver.__init__(self, switch_hostname, debug)
Steve McIntyrefdcb3782015-07-06 16:34:56 +010054 self._systemdata = []
Steve McIntyrefdcb3782015-07-06 16:34:56 +010055 self.exec_string = "/usr/bin/telnet %s %d" % (switch_hostname, switch_telnetport)
56 self.errors = SwitchErrors()
57
58 ################################
59 ### Switch-level API functions
60 ################################
61
Steve McIntyrefdcb3782015-07-06 16:34:56 +010062 # Save the current running config into flash - we want config to
63 # remain across reboots
64 def switch_save_running_config(self):
65 try:
66 self._cli("save")
67 self.connection.expect("Are you sure")
68 self._cli("y")
69 self.connection.expect("Configuration Saved!")
70 except (PExpectError, pexpect.EOF):
71 # recurse on error
72 self._switch_connect()
73 self.switch_save_running_config()
74
75 # Restart the switch - we need to reload config to do a
76 # roll-back. Do NOT save running-config first if the switch asks -
77 # we're trying to dump recent changes, not save them.
78 #
Steve McIntyre1c8a3212015-07-14 17:07:31 +010079 # This will also implicitly cause a connection to be closed
Steve McIntyrefdcb3782015-07-06 16:34:56 +010080 def switch_restart(self):
81 self._cli("reload")
Steve McIntyre1c8a3212015-07-14 17:07:31 +010082 self.connection.expect('Are you sure')
Steve McIntyrefdcb3782015-07-06 16:34:56 +010083 self._cli("y") # Yes, continue to reset
84 self.connection.close(True)
85
86 # List the capabilities of the switch (and driver) - some things
87 # make no sense to abstract. Returns a dict of strings, each one
88 # describing an extra feature that that higher levels may care
89 # about
90 def switch_get_capabilities(self):
91 return self._capabilities
92
93 ################################
94 ### VLAN API functions
95 ################################
96
97 # Create a VLAN with the specified tag
98 def vlan_create(self, tag):
99 logging.debug("Creating VLAN %d", tag)
100 try:
101 self._cli("vlan database")
102 self._cli("vlan %d" % tag)
103 self._end_configure()
104
105 # Validate it happened
106 vlans = self.vlan_get_list()
107 for vlan in vlans:
108 if vlan == tag:
109 return
110 raise IOError("Failed to create VLAN %d" % tag)
111
112 except PExpectError:
113 # recurse on error
114 self._switch_connect()
115 self.vlan_create(tag)
116
117 # Destroy a VLAN with the specified tag
118 def vlan_destroy(self, tag):
119 logging.debug("Destroying VLAN %d", tag)
120
121 try:
122 self._cli("vlan database")
123 self._cli("no vlan %d" % tag)
124 self._end_configure()
125
126 # Validate it happened
127 vlans = self.vlan_get_list()
128 for vlan in vlans:
129 if vlan == tag:
130 raise IOError("Failed to destroy VLAN %d" % tag)
131
132 except PExpectError:
133 # recurse on error
134 self._switch_connect()
135 self.vlan_destroy(tag)
136
137 # Set the name of a VLAN
138 def vlan_set_name(self, tag, name):
139 logging.debug("Setting name of VLAN %d to %s", tag, name)
140
141 try:
142 self._cli("vlan database")
143 self._cli("vlan name %d %s" % (tag, name))
144 self._end_configure()
145
146 # Validate it happened
147 read_name = self.vlan_get_name(tag)
148 if read_name != name:
149 raise IOError("Failed to set name for VLAN %d (name found is \"%s\", not \"%s\")"
150 % (tag, read_name, name))
151 except PExpectError:
152 # recurse on error
153 self._switch_connect()
154 self.vlan_set_name(tag, name)
155
156 # Get a list of the VLAN tags currently registered on the switch
157 def vlan_get_list(self):
158 logging.debug("Grabbing list of VLANs")
159
160 try:
161 vlans = []
162
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100163 regex = re.compile(r'^ *(\d+).*(Static)')
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100164
165 self._cli("show vlan brief")
166 for line in self._read_long_output("show vlan brief"):
167 match = regex.match(line)
168 if match:
169 vlans.append(int(match.group(1)))
170 return vlans
171
172 except PExpectError:
173 # recurse on error
174 self._switch_connect()
175 return self.vlan_get_list()
176
177 # For a given VLAN tag, ask the switch what the associated name is
178 def vlan_get_name(self, tag):
179
180 try:
181 logging.debug("Grabbing the name of VLAN %d", tag)
182 name = None
183 regex = re.compile('VLAN Name: (.*)')
184 self._cli("show vlan %d" % tag)
185 for line in self._read_long_output("show vlan"):
186 match = regex.match(line)
187 if match:
188 name = match.group(1)
189 name.strip()
190 return name
191
192 except PExpectError:
193 # recurse on error
194 self._switch_connect()
195 return self.vlan_get_name(tag)
196
197 ################################
198 ### Port API functions
199 ################################
200
201 # Set the mode of a port: access or trunk
202 def port_set_mode(self, port, mode):
Steve McIntyree4c97ec2015-07-16 18:27:59 +0100203 logging.debug("Setting port %s to %s mode", port, mode)
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100204 if not self._is_port_mode_valid(mode):
205 raise InputError("Port mode %s is not allowed" % mode)
206 if not self._is_port_name_valid(port):
207 raise InputError("Port name %s not recognised" % port)
208
209 # This switch does not support specific modes, so we can't
210 # actually change the mode directly. However, we can and
211 # should deal with the PVID and memberships of existing VLANs
212 # etc.
213
214 try:
215 if mode == "trunk":
216 # We define a trunk port thus:
Steve McIntyre4f945292015-07-16 18:31:21 +0100217 # * accept all frames on ingress
218 # * accept packets for all VLANs (no ingress filter)
219 # * tags frames on transmission (do that later when
220 # * adding VLANs to the port)
221 # * PVID should match the default VLAN (1).
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100222 self._configure()
223 self._cli("interface %s" % port)
Steve McIntyred97ab292015-07-07 14:23:32 +0100224 self._cli("vlan acceptframe all")
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100225 self._cli("no vlan ingressfilter")
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100226 self._cli("vlan pvid 1")
Steve McIntyrefc7a6272015-07-07 16:22:32 +0100227 self._end_interface()
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100228 self._end_configure()
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100229
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100230 # We define an access port thus:
Steve McIntyre4f945292015-07-16 18:31:21 +0100231 # * accept only untagged frames on ingress
232 # * accept packets for only desired VLANs (ingress filter)
233 # * exists on one VLAN only (1 by default)
234 # * do not tag frames on transmission (the devices
235 # we're talking to are expecting untagged frames)
236 # * PVID should match the VLAN it's on (1 by default,
237 # but don't do that here)
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100238 if mode == "access":
239 self._configure()
240 self._cli("interface %s" % port)
Steve McIntyreef73fca2015-07-07 14:57:59 +0100241 self._cli("vlan acceptframe admituntaggedonly")
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100242 self._cli("vlan ingressfilter")
Steve McIntyre53d47192015-07-07 16:24:04 +0100243 self._cli("no vlan tagging 1-1023")
244 self._cli("no vlan tagging 1024-2047")
Steve McIntyre533519d2015-07-07 16:17:34 +0100245 self._cli("no vlan tagging 2048-3071")
246 self._cli("no vlan tagging 3072-4093")
Steve McIntyrefc7a6272015-07-07 16:22:32 +0100247 self._end_interface()
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100248 self._end_configure()
249
250 # Validate it happened
251 read_mode = self.port_get_mode(port)
252
253 if read_mode != mode:
254 raise IOError("Failed to set mode for port %s" % port)
255
256 except PExpectError:
257 # recurse on error
258 self._switch_connect()
259 self.port_set_mode(port, mode)
260
261 # Get the mode of a port: access or trunk
262 def port_get_mode(self, port):
263 logging.debug("Getting mode of port %s", port)
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100264
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100265 if not self._is_port_name_valid(port):
266 raise InputError("Port name %s not recognised" % port)
Steve McIntyreef73fca2015-07-07 14:57:59 +0100267 acceptframe_re = re.compile('vlan acceptframe (.*)')
268 ingress_re = re.compile('vlan ingressfilter')
Steve McIntyreef73fca2015-07-07 14:57:59 +0100269
270 acceptframe = None
271 ingressfilter = True
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100272
273 try:
Steve McIntyreef73fca2015-07-07 14:57:59 +0100274 self._cli("show running-config interface %s" % port)
275 for line in self._read_long_output("show running-config interface"):
276
277 match = acceptframe_re.match(line)
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100278 if match:
Steve McIntyreef73fca2015-07-07 14:57:59 +0100279 acceptframe = match.group(1)
280
281 match = ingress_re.match(line)
282 if match:
283 ingressfilter = True
284
Steve McIntyrebcbb4222015-07-09 18:28:02 +0100285 # Simple classifier for now; may need to revisit later...
Steve McIntyre4f945292015-07-16 18:31:21 +0100286 if (ingressfilter and acceptframe == "admituntaggedonly"):
Steve McIntyrebcbb4222015-07-09 18:28:02 +0100287 return "access"
288 else:
289 return "trunk"
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100290
291 except PExpectError:
292 # recurse on error
293 self._switch_connect()
294 return self.port_get_mode(port)
295
Steve McIntyreef73fca2015-07-07 14:57:59 +0100296
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100297 # Set an access port to be in a specified VLAN (tag)
298 def port_set_access_vlan(self, port, tag):
299 logging.debug("Setting access port %s to VLAN %d", port, tag)
300 if not self._is_port_name_valid(port):
301 raise InputError("Port name %s not recognised" % port)
302 if not (self.port_get_mode(port) == "access"):
303 raise InputError("Port %s not in access mode" % port)
304
305 try:
Steve McIntyree6172b02015-07-07 17:59:46 +0100306 current_vlans = self._get_port_vlans(port)
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100307 self._configure()
308 self._cli("interface %s" % port)
Steve McIntyreef73fca2015-07-07 14:57:59 +0100309 self._cli("vlan pvid %s" % tag)
310 # Find the list of VLANs we're currently on, and drop them
311 # all. "auto" mode is fine here, we won't be included
312 # unless we have GVRP configured, and we don't do
313 # that.
Steve McIntyree6172b02015-07-07 17:59:46 +0100314 for current_vlan in current_vlans:
Steve McIntyreef73fca2015-07-07 14:57:59 +0100315 self._cli("vlan participation auto %s" % current_vlan)
316 # Now specifically include the VLAN we want
317 self._cli("vlan participation include %s" % tag)
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100318 self._cli("no shutdown")
Steve McIntyrefc7a6272015-07-07 16:22:32 +0100319 self._end_interface()
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100320 self._end_configure()
321
322 # Finally, validate things worked
323 read_vlan = int(self.port_get_access_vlan(port))
324 if read_vlan != tag:
325 raise IOError("Failed to move access port %d to VLAN %d - got VLAN %d instead"
326 % (port, tag, read_vlan))
327
328 except PExpectError:
329 # recurse on error
330 self._switch_connect()
331 self.port_set_access_vlan(port, tag)
332
Steve McIntyre4f945292015-07-16 18:31:21 +0100333 # Add a trunk port to a specified VLAN (tag)
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100334 def port_add_trunk_to_vlan(self, port, tag):
335 logging.debug("Adding trunk port %s to VLAN %d", port, tag)
336 if not self._is_port_name_valid(port):
337 raise InputError("Port name %s not recognised" % port)
338 if not (self.port_get_mode(port) == "trunk"):
339 raise InputError("Port %s not in trunk mode" % port)
340
341 try:
342 self._configure()
343 self._cli("interface %s" % port)
Steve McIntyreef73fca2015-07-07 14:57:59 +0100344 self._cli("vlan participation include %d" % tag)
Steve McIntyre4f945292015-07-16 18:31:21 +0100345 self._cli("vlan tagging %d" % tag)
Steve McIntyrefc7a6272015-07-07 16:22:32 +0100346 self._end_interface()
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100347 self._end_configure()
348
349 # Validate it happened
350 read_vlans = self.port_get_trunk_vlan_list(port)
351 for vlan in read_vlans:
352 if vlan == tag or vlan == "ALL":
353 return
354 raise IOError("Failed to add trunk port %s to VLAN %d" % (port, tag))
355
356 except PExpectError:
357 # recurse on error
358 self._switch_connect()
359 self.port_add_trunk_to_vlan(port, tag)
360
Steve McIntyre4f945292015-07-16 18:31:21 +0100361 # Remove a trunk port from a specified VLAN (tag)
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100362 def port_remove_trunk_from_vlan(self, port, tag):
363 logging.debug("Removing trunk port %s from VLAN %d", port, tag)
364 if not self._is_port_name_valid(port):
365 raise InputError("Port name %s not recognised" % port)
366 if not (self.port_get_mode(port) == "trunk"):
367 raise InputError("Port %s not in trunk mode" % port)
368
369 try:
370 self._configure()
371 self._cli("interface %s" % port)
Steve McIntyreef73fca2015-07-07 14:57:59 +0100372 self._cli("vlan participation auto %d" % tag)
Steve McIntyre4f945292015-07-16 18:31:21 +0100373 self._cli("no vlan tagging %d" % tag)
Steve McIntyrefc7a6272015-07-07 16:22:32 +0100374 self._end_interface()
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100375 self._end_configure()
376
377 # Validate it happened
378 read_vlans = self.port_get_trunk_vlan_list(port)
379 for vlan in read_vlans:
380 if vlan == tag:
381 raise IOError("Failed to remove trunk port %s from VLAN %d" % (port, tag))
382
383 except PExpectError:
384 # recurse on error
385 self._switch_connect()
386 self.port_remove_trunk_from_vlan(port, tag)
387
388 # Get the configured VLAN tag for an access port (tag)
389 def port_get_access_vlan(self, port):
390 logging.debug("Getting VLAN for access port %s", port)
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100391 if not self._is_port_name_valid(port):
392 raise InputError("Port name %s not recognised" % port)
393 if not (self.port_get_mode(port) == "access"):
394 raise InputError("Port %s not in access mode" % port)
Steve McIntyrec4834c72015-07-07 18:00:17 +0100395 vlans = self._get_port_vlans(port)
396 if (len(vlans) > 1):
397 raise IOError("More than one VLAN on access port %s" % port)
398 return vlans[0]
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100399
400 # Get the list of configured VLAN tags for a trunk port
401 def port_get_trunk_vlan_list(self, port):
Steve McIntyreef73fca2015-07-07 14:57:59 +0100402 logging.debug("Getting VLAN(s) for trunk port %s", port)
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100403 if not self._is_port_name_valid(port):
404 raise InputError("Port name %s not recognised" % port)
405 if not (self.port_get_mode(port) == "trunk"):
406 raise InputError("Port %s not in trunk mode" % port)
Steve McIntyreef73fca2015-07-07 14:57:59 +0100407 return self._get_port_vlans(port)
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100408
409 ################################
410 ### Internal functions
411 ################################
412
413 # Connect to the switch and log in
414 def _switch_connect(self):
415
416 if not self.connection is None:
417 self.connection.close(True)
418 self.connection = None
419
420 logging.debug("Connecting to Switch with: %s", self.exec_string)
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100421 self.connection = pexpect.spawn(self.exec_string, logfile = self.logger)
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100422
423 self._login()
424
425 # Avoid paged output
426 self._cli("terminal length 0")
427
428 # And grab details about the switch. in case we need it
429 self._get_systemdata()
430
431 # And also validate them - make sure we're driving a switch of
432 # the correct model! Also store the serial number
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100433 manuf_regex = re.compile(r'^Manufacturer([\.\s])+(\S+)')
434 model_regex = re.compile(r'^Machine Model([\.\s])+(\S+)')
435 sn_regex = re.compile(r'^Serial Number([\.\s])+(\S+)')
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100436
437 for line in self._systemdata:
438 match1 = manuf_regex.match(line)
439 if match1:
440 manuf = match1.group(2)
441
442 match2 = model_regex.match(line)
443 if match2:
444 model = match2.group(2)
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100445
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100446 match3 = sn_regex.match(line)
447 if match3:
448 self.serial_number = match3.group(2)
449
450 logging.debug("manufacturer is %s", manuf)
451 logging.debug("model is %s", model)
452 logging.debug("serial number is %s", self.serial_number)
453
454 if not (self._expected_manuf.match(manuf) and self._expected_model.match(model)):
455 raise IOError("Switch %s %s not recognised by this driver: abort" % (manuf, model))
456
457 # Now build a list of our ports, for later sanity checking
458 self._ports = self._get_port_names()
459 if len(self._ports) < 4:
460 raise IOError("Not enough ports detected - problem!")
461
462 def _login(self):
463 logging.debug("attempting login with username %s, password %s", self._username, self._password)
464 if self._username is not None:
465 self.connection.expect("User:")
466 self._cli("%s" % self._username)
467 if self._password is not None:
468 self.connection.expect("Password:")
469 self._cli("%s" % self._password, False)
470 while True:
471 index = self.connection.expect(['User:', 'Password:', 'Bad passwords', 'authentication failed', r'(.*)(#|>)'])
472 if index != 4: # Any other means: failed to log in!
473 logging.error("Login failure: index %d\n", index)
474 logging.error("Login failure: %s\n", self.connection.match.before)
475 raise IOError
476
477 # else
478 self._prompt_name = re.escape(self.connection.match.group(1).strip())
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100479 if self.connection.match.group(2) == ">":
480 # Need to enter "enable" mode too
481 self._cli("enable")
482 if self._enable_password is not None:
483 self.connection.expect("Password:")
484 self._cli("%s" % self._enable_password, False)
485 index = self.connection.expect(['Password:', 'Bad passwords', 'authentication failed', r'(.*) *(#|>)'])
486 if index != 3: # Any other means: failed to log in!
487 logging.error("Enable password failure: %s\n", self.connection.match)
488 raise IOError
489 return 0
490
491 def _logout(self):
492 logging.debug("Logging out")
493 self._cli("quit", False)
Steve McIntyredfce73f2015-07-09 17:41:55 +0100494 try:
495 self.connection.expect("Would you like to save them now")
496 self._cli("n")
497 except (pexpect.EOF):
498 pass
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100499
500 def _configure(self):
501 self._cli("configure")
502
503 def _end_configure(self):
504 self._cli("exit")
505
Steve McIntyrefc7a6272015-07-07 16:22:32 +0100506 def _end_interface(self):
507 self._end_configure()
508
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100509 def _read_long_output(self, text):
510 longbuf = []
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100511 prompt = self._prompt_name + r'\s*#'
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100512 while True:
513 try:
514 index = self.connection.expect(['--More--', prompt])
515 if index == 0: # "--More-- or (q)uit"
516 for line in self.connection.before.split('\r\n'):
517 line1 = re.sub('(\x08|\x0D)*', '', line.strip())
518 longbuf.append(line1)
519 self._cli(' ', False)
520 elif index == 1: # Back to a prompt, says output is finished
521 break
522 except (pexpect.EOF, pexpect.TIMEOUT):
523 # Something went wrong; logout, log in and try again!
524 logging.error("PEXPECT FAILURE, RECONNECT")
525 self.errors.log_error_in(text)
526 raise PExpectError("_read_long_output failed")
527 except:
528 logging.error("prompt is \"%s\"", prompt)
529 raise
530
531 for line in self.connection.before.split('\r\n'):
532 longbuf.append(line.strip())
533 return longbuf
534
535 def _get_port_names(self):
536 logging.debug("Grabbing list of ports")
537 interfaces = []
538
539 # Use "Up" or "Down" to only identify lines in the output that
540 # match interfaces that exist
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100541 regex = re.compile(r'^(1\S+)')
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100542
543 try:
544 self._cli("show port all")
545 for line in self._read_long_output("show port all"):
546 match = regex.match(line)
547 if match:
548 interface = match.group(1)
549 interfaces.append(interface)
Steve McIntyred601ab82015-07-09 17:42:36 +0100550 logging.debug(" found %d ports on the switch", len(interfaces))
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100551 return interfaces
552
553 except PExpectError:
554 # recurse on error
555 self._switch_connect()
556 return self._get_port_names()
557
558 def _show_config(self):
559 logging.debug("Grabbing config")
560 try:
561 self._cli("show running-config")
562 return self._read_long_output("show running-config")
563 except PExpectError:
564 # recurse on error
565 self._switch_connect()
566 return self._show_config()
567
568 def _show_clock(self):
569 logging.debug("Grabbing time")
570 try:
571 self._cli("show clock")
572 return self._read_long_output("show clock")
573 except PExpectError:
574 # recurse on error
575 self._switch_connect()
576 return self._show_clock()
577
578 def _get_systemdata(self):
579 logging.debug("Grabbing system sw and hw versions")
580
581 try:
582 self._systemdata = []
583 self._cli("show version")
584 for line in self._read_long_output("show version"):
585 self._systemdata.append(line)
586
587 except PExpectError:
588 # recurse on error
589 self._switch_connect()
590 return self._get_systemdata()
591
592 def _parse_vlan_list(self, inputdata):
593 vlans = []
594
595 if inputdata == "ALL":
596 return ["ALL"]
597 elif inputdata == "NONE":
598 return []
599 else:
600 # Parse the complex list
601 groups = inputdata.split(',')
602 for group in groups:
603 subgroups = group.split('-')
604 if len(subgroups) == 1:
605 vlans.append(int(subgroups[0]))
606 elif len(subgroups) == 2:
607 for i in range (int(subgroups[0]), int(subgroups[1]) + 1):
608 vlans.append(i)
609 else:
610 logging.debug("Can't parse group \"" + group + "\"")
611
612 return vlans
613
Steve McIntyreef73fca2015-07-07 14:57:59 +0100614 def _get_port_vlans(self, port):
615 vlan_text = None
616
Steve McIntyrec08cca22015-07-07 18:00:33 +0100617 vlan_part_re = re.compile('vlan participation include (.*)')
Steve McIntyreef73fca2015-07-07 14:57:59 +0100618
619 try:
620 self._cli("show running-config interface %s" % port)
621 for line in self._read_long_output("show running-config interface"):
622 match = vlan_part_re.match(line)
623 if match:
624 if vlan_text != None:
625 vlan_text += ","
626 vlan_text += (match.group(1))
627 else:
628 vlan_text = match.group(1)
Steve McIntyre8009ffe2015-07-07 18:02:21 +0100629
630 if vlan_text is None:
631 return [1]
632 else:
633 vlans = self._parse_vlan_list(vlan_text)
634 return vlans
Steve McIntyreef73fca2015-07-07 14:57:59 +0100635
636 except PExpectError:
637 # recurse on error
638 self._switch_connect()
639 return self._get_port_vlans(port)
640
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100641 # Wrapper around connection.send - by default, expect() the same
642 # text we've sent, to remove it from the output from the
643 # switch. For the few cases where we don't need that, override
644 # this using echo=False.
645 # Horrible, but seems to work.
646 def _cli(self, text, echo=True):
647 self.connection.send(text + '\r')
648 if echo:
649 try:
650 self.connection.expect(text)
651 except (pexpect.EOF, pexpect.TIMEOUT):
652 # Something went wrong; logout, log in and try again!
653 logging.error("PEXPECT FAILURE, RECONNECT")
654 self.errors.log_error_out(text)
655 raise PExpectError("_cli failed on %s" % text)
656 except:
657 logging.error("Unexpected error: %s", sys.exc_info()[0])
658 raise
659
660if __name__ == "__main__":
661
662 import optparse
663
664 switch = 'vlandswitch05'
665 parser = optparse.OptionParser()
666 parser.add_option("--switch",
667 dest = "switch",
668 action = "store",
669 nargs = 1,
670 type = "string",
671 help = "specify switch to connect to for testing",
672 metavar = "<switch>")
673 (opts, args) = parser.parse_args()
674 if opts.switch:
675 switch = opts.switch
676
Steve McIntyre144d51c2015-07-07 17:56:30 +0100677 logging.basicConfig(level = logging.DEBUG,
678 format = '%(asctime)s %(levelname)-8s %(message)s')
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100679 p = NetgearXSM(switch, 23, debug=True)
680 p.switch_connect('admin', '', None)
681
682 print "VLANs are:"
683 buf = p.vlan_get_list()
684 p.dump_list(buf)
685
686 buf = p.vlan_get_name(2)
687 print "VLAN 2 is named \"%s\"" % buf
688
689 print "Create VLAN 3"
690 p.vlan_create(3)
691
692 buf = p.vlan_get_name(3)
693 print "VLAN 3 is named \"%s\"" % buf
694
695 print "Set name of VLAN 3 to test333"
696 p.vlan_set_name(3, "test333")
697
698 buf = p.vlan_get_name(3)
699 print "VLAN 3 is named \"%s\"" % buf
700
701 print "VLANs are:"
702 buf = p.vlan_get_list()
703 p.dump_list(buf)
704
705 print "Destroy VLAN 3"
706 p.vlan_destroy(3)
707
708 print "VLANs are:"
709 buf = p.vlan_get_list()
710 p.dump_list(buf)
711
712 buf = p.port_get_mode("1/0/10")
713 print "Port 1/0/10 is in %s mode" % buf
714
715 buf = p.port_get_mode("1/0/11")
716 print "Port 1/0/11 is in %s mode" % buf
717
718 # Test access stuff
719 print "Set 1/0/9 to access mode"
720 p.port_set_mode("1/0/9", "access")
721
722 print "Move 1/0/9 to VLAN 4"
723 p.port_set_access_vlan("1/0/9", 4)
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100724
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100725 buf = p.port_get_access_vlan("1/0/9")
726 print "Read from switch: 1/0/9 is on VLAN %s" % buf
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100727
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100728 print "Move 1/0/9 back to VLAN 1"
729 p.port_set_access_vlan("1/0/9", 1)
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100730
Steve McIntyre40484f12015-07-07 18:02:50 +0100731 print "Create VLAN 2"
Steve McIntyre3a1b4832015-07-08 15:42:09 +0100732 p.vlan_create(2)
Steve McIntyre40484f12015-07-07 18:02:50 +0100733
734 print "Create VLAN 3"
735 p.vlan_create(3)
736
737 print "Create VLAN 4"
738 p.vlan_create(4)
739
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100740 # Test access stuff
741 print "Set 1/0/9 to trunk mode"
742 p.port_set_mode("1/0/9", "trunk")
743 print "Read from switch: which VLANs is 1/0/9 on?"
744 buf = p.port_get_trunk_vlan_list("1/0/9")
745 p.dump_list(buf)
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100746
Steve McIntyre12b9ef92015-07-08 15:48:44 +0100747 # The adds below are NOOPs in effect on this switch - no filtering
748 # for "trunk" ports
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100749 print "Add 1/0/9 to VLAN 2"
750 p.port_add_trunk_to_vlan("1/0/9", 2)
751 print "Add 1/0/9 to VLAN 3"
752 p.port_add_trunk_to_vlan("1/0/9", 3)
753 print "Add 1/0/9 to VLAN 4"
754 p.port_add_trunk_to_vlan("1/0/9", 4)
755 print "Read from switch: which VLANs is 1/0/9 on?"
756 buf = p.port_get_trunk_vlan_list("1/0/9")
757 p.dump_list(buf)
758
Steve McIntyre12b9ef92015-07-08 15:48:44 +0100759 # And the same for removals here
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100760 p.port_remove_trunk_from_vlan("1/0/9", 3)
Steve McIntyre60671382015-07-08 15:49:03 +0100761 p.port_remove_trunk_from_vlan("1/0/9", 2)
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100762 p.port_remove_trunk_from_vlan("1/0/9", 4)
763 print "Read from switch: which VLANs is 1/0/9 on?"
764 buf = p.port_get_trunk_vlan_list("1/0/9")
765 p.dump_list(buf)
766
767# print 'Restarting switch, to explicitly reset config'
768# p.switch_restart()
769
770# p.switch_save_running_config()
771
772# p.switch_disconnect()
773# p._show_config()