blob: fd04009b3a705bb3cf5980ea39e96e22a90683a8 [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 = [
45 'TrunkWildCardVlans' # Trunk ports are on all VLANs by
46 # default, so we shouldn't need to
47 # bugger with them
48 ]
49
50 # Regexps of expected hardware information - fail if we don't see
51 # this
52 _expected_manuf = re.compile('^Netgear')
53 _expected_model = re.compile('^XSM')
54
55 logfile = None
56
57 def __init__(self, switch_hostname, switch_telnetport=23, debug = False):
58 SwitchDriver.__init__(self)
59 self._systemdata = []
60 if debug:
61 self.logfile = sys.stderr
62 self.exec_string = "/usr/bin/telnet %s %d" % (switch_hostname, switch_telnetport)
63 self.errors = SwitchErrors()
64
65 ################################
66 ### Switch-level API functions
67 ################################
68
69 # Connect to the switch and log in
70 def switch_connect(self, username, password, enablepassword):
71 self._username = username
72 self._password = password
73 self._enable_password = enablepassword
74 self._switch_connect()
75
76 # Log out of the switch and drop the connection and all state
77 def switch_disconnect(self):
78 self._logout()
79 logging.debug("Closing connection: %s", self.connection)
80 self.connection.close(True)
81 self._ports = []
82 self._prompt_name = ''
83 self._systemdata = []
84 del(self)
85
86 # Save the current running config into flash - we want config to
87 # remain across reboots
88 def switch_save_running_config(self):
89 try:
90 self._cli("save")
91 self.connection.expect("Are you sure")
92 self._cli("y")
93 self.connection.expect("Configuration Saved!")
94 except (PExpectError, pexpect.EOF):
95 # recurse on error
96 self._switch_connect()
97 self.switch_save_running_config()
98
99 # Restart the switch - we need to reload config to do a
100 # roll-back. Do NOT save running-config first if the switch asks -
101 # we're trying to dump recent changes, not save them.
102 #
103 # This will also implicitly cause a connection to be closed
104 def switch_restart(self):
105 self._cli("reload")
106 index = self.connection.expect('Are you sure')
107 self._cli("y") # Yes, continue to reset
108 self.connection.close(True)
109
110 # 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
114 def switch_get_capabilities(self):
115 return self._capabilities
116
117 ################################
118 ### VLAN API functions
119 ################################
120
121 # Create a VLAN with the specified tag
122 def vlan_create(self, tag):
123 logging.debug("Creating VLAN %d", tag)
124 try:
125 self._cli("vlan database")
126 self._cli("vlan %d" % tag)
127 self._end_configure()
128
129 # 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)
140
141 # Destroy a VLAN with the specified tag
142 def vlan_destroy(self, tag):
143 logging.debug("Destroying VLAN %d", tag)
144
145 try:
146 self._cli("vlan database")
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)
160
161 # Set the name of a VLAN
162 def vlan_set_name(self, tag, name):
163 logging.debug("Setting name of VLAN %d to %s", tag, name)
164
165 try:
166 self._cli("vlan database")
167 self._cli("vlan name %d %s" % (tag, name))
168 self._end_configure()
169
170 # Validate it happened
171 read_name = self.vlan_get_name(tag)
172 if read_name != name:
173 raise IOError("Failed to set name for VLAN %d (name found is \"%s\", not \"%s\")"
174 % (tag, read_name, name))
175 except PExpectError:
176 # recurse on error
177 self._switch_connect()
178 self.vlan_set_name(tag, name)
179
180 # Get a list of the VLAN tags currently registered on the switch
181 def vlan_get_list(self):
182 logging.debug("Grabbing list of VLANs")
183
184 try:
185 vlans = []
186
187 regex = re.compile('^ *(\d+).*(Static)')
188
189 self._cli("show vlan brief")
190 for line in self._read_long_output("show vlan brief"):
191 match = regex.match(line)
192 if match:
193 vlans.append(int(match.group(1)))
194 return vlans
195
196 except PExpectError:
197 # recurse on error
198 self._switch_connect()
199 return self.vlan_get_list()
200
201 # For a given VLAN tag, ask the switch what the associated name is
202 def vlan_get_name(self, tag):
203
204 try:
205 logging.debug("Grabbing the name of VLAN %d", tag)
206 name = None
207 regex = re.compile('VLAN Name: (.*)')
208 self._cli("show vlan %d" % tag)
209 for line in self._read_long_output("show vlan"):
210 match = regex.match(line)
211 if match:
212 name = match.group(1)
213 name.strip()
214 return name
215
216 except PExpectError:
217 # recurse on error
218 self._switch_connect()
219 return self.vlan_get_name(tag)
220
221 ################################
222 ### Port API functions
223 ################################
224
225 # Set the mode of a port: access or trunk
226 def port_set_mode(self, port, mode):
227 logging.debug("Setting port %s to %s", port, mode)
228 if not self._is_port_mode_valid(mode):
229 raise InputError("Port mode %s is not allowed" % mode)
230 if not self._is_port_name_valid(port):
231 raise InputError("Port name %s not recognised" % port)
232
233 # This switch does not support specific modes, so we can't
234 # actually change the mode directly. However, we can and
235 # should deal with the PVID and memberships of existing VLANs
236 # etc.
237
238 try:
239 if mode == "trunk":
240 # We define a trunk port thus:
Steve McIntyred97ab292015-07-07 14:23:32 +0100241 # accept all frames on ingress
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100242 # exist on all current VLANs
243 # tag outgoing frames
244 # PVID should match the default VLAN (1).
245 self._configure()
246 self._cli("interface %s" % port)
Steve McIntyred97ab292015-07-07 14:23:32 +0100247 self._cli("vlan acceptframe all")
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100248 self._cli("no vlan ingressfilter")
Steve McIntyre53d47192015-07-07 16:24:04 +0100249 self._cli("vlan tagging 1-1023")
250 self._cli("vlan tagging 1024-2047")
Steve McIntyre533519d2015-07-07 16:17:34 +0100251 self._cli("vlan tagging 2048-3071")
252 self._cli("vlan tagging 3072-4093")
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100253 self._cli("vlan pvid 1")
Steve McIntyrefc7a6272015-07-07 16:22:32 +0100254 self._end_interface()
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100255 self._end_configure()
256
257 # We define an access port thus:
258 # accept only untagged frames on ingress
259 # exists on one VLAN only (1 by default)
260 # do not tag outgoing frames
261 # PVID should match the VLAN it's on (1 by default)
262 if mode == "access":
263 self._configure()
264 self._cli("interface %s" % port)
Steve McIntyreef73fca2015-07-07 14:57:59 +0100265 self._cli("vlan acceptframe admituntaggedonly")
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100266 self._cli("vlan ingressfilter")
Steve McIntyre53d47192015-07-07 16:24:04 +0100267 self._cli("no vlan tagging 1-1023")
268 self._cli("no vlan tagging 1024-2047")
Steve McIntyre533519d2015-07-07 16:17:34 +0100269 self._cli("no vlan tagging 2048-3071")
270 self._cli("no vlan tagging 3072-4093")
Steve McIntyreef73fca2015-07-07 14:57:59 +0100271 self._cli("vlan pvid 1") # Need to find the right VLAN first?
Steve McIntyrefc7a6272015-07-07 16:22:32 +0100272 self._end_interface()
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100273 self._end_configure()
274
275 # Validate it happened
276 read_mode = self.port_get_mode(port)
277
278 if read_mode != mode:
279 raise IOError("Failed to set mode for port %s" % port)
280
281 except PExpectError:
282 # recurse on error
283 self._switch_connect()
284 self.port_set_mode(port, mode)
285
286 # Get the mode of a port: access or trunk
287 def port_get_mode(self, port):
288 logging.debug("Getting mode of port %s", port)
289 mode = ''
290 if not self._is_port_name_valid(port):
291 raise InputError("Port name %s not recognised" % port)
Steve McIntyreef73fca2015-07-07 14:57:59 +0100292 acceptframe_re = re.compile('vlan acceptframe (.*)')
293 ingress_re = re.compile('vlan ingressfilter')
294 tagging_re = re.compile('vlan tagging (.*)')
295
296 acceptframe = None
297 ingressfilter = True
298 tagging = None
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100299
300 try:
Steve McIntyreef73fca2015-07-07 14:57:59 +0100301 self._cli("show running-config interface %s" % port)
302 for line in self._read_long_output("show running-config interface"):
303
304 match = acceptframe_re.match(line)
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100305 if match:
Steve McIntyreef73fca2015-07-07 14:57:59 +0100306 acceptframe = match.group(1)
Steve McIntyre00dd3d22015-07-07 16:45:11 +0100307 logging.debug("acceptframe is %s", acceptframe)
Steve McIntyreef73fca2015-07-07 14:57:59 +0100308
309 match = ingress_re.match(line)
310 if match:
311 ingressfilter = True
Steve McIntyre00dd3d22015-07-07 16:45:11 +0100312 logging.debug("ingressfilter is %s", ingressfilter)
Steve McIntyreef73fca2015-07-07 14:57:59 +0100313
314 match = tagging_re.match(line)
315 if match:
316 tagging = match.group(1)
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100317
318 except PExpectError:
319 # recurse on error
320 self._switch_connect()
321 return self.port_get_mode(port)
322
Steve McIntyreef73fca2015-07-07 14:57:59 +0100323 # Simple classifier for now; may need to revisit later...
324 if (acceptframe == "untaggedonly" and ingressfilter == True):
325 return "access"
326 else:
327 return "trunk"
328
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100329 # Set an access port to be in a specified VLAN (tag)
330 def port_set_access_vlan(self, port, tag):
331 logging.debug("Setting access port %s to VLAN %d", port, tag)
332 if not self._is_port_name_valid(port):
333 raise InputError("Port name %s not recognised" % port)
334 if not (self.port_get_mode(port) == "access"):
335 raise InputError("Port %s not in access mode" % port)
336
337 try:
338 self._configure()
339 self._cli("interface %s" % port)
Steve McIntyreef73fca2015-07-07 14:57:59 +0100340 self._cli("vlan pvid %s" % tag)
341 # Find the list of VLANs we're currently on, and drop them
342 # all. "auto" mode is fine here, we won't be included
343 # unless we have GVRP configured, and we don't do
344 # that.
345 for current_vlan in self._get_port_vlans(port):
346 self._cli("vlan participation auto %s" % current_vlan)
347 # Now specifically include the VLAN we want
348 self._cli("vlan participation include %s" % tag)
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100349 self._cli("no shutdown")
Steve McIntyrefc7a6272015-07-07 16:22:32 +0100350 self._end_interface()
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100351 self._end_configure()
352
353 # Finally, validate things worked
354 read_vlan = int(self.port_get_access_vlan(port))
355 if read_vlan != tag:
356 raise IOError("Failed to move access port %d to VLAN %d - got VLAN %d instead"
357 % (port, tag, read_vlan))
358
359 except PExpectError:
360 # recurse on error
361 self._switch_connect()
362 self.port_set_access_vlan(port, tag)
363
Steve McIntyreef73fca2015-07-07 14:57:59 +0100364 # Add a trunk port to a specified VLAN (tag). Shouldn't be needed
365 # on this switch as we don't do VLAN ingress filtering on "trunk"
366 # ports, but...
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100367 def port_add_trunk_to_vlan(self, port, tag):
368 logging.debug("Adding trunk port %s to VLAN %d", port, tag)
369 if not self._is_port_name_valid(port):
370 raise InputError("Port name %s not recognised" % port)
371 if not (self.port_get_mode(port) == "trunk"):
372 raise InputError("Port %s not in trunk mode" % port)
373
374 try:
375 self._configure()
376 self._cli("interface %s" % port)
Steve McIntyreef73fca2015-07-07 14:57:59 +0100377 self._cli("vlan participation include %d" % tag)
Steve McIntyrefc7a6272015-07-07 16:22:32 +0100378 self._end_interface()
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100379 self._end_configure()
380
381 # Validate it happened
382 read_vlans = self.port_get_trunk_vlan_list(port)
383 for vlan in read_vlans:
384 if vlan == tag or vlan == "ALL":
385 return
386 raise IOError("Failed to add trunk port %s to VLAN %d" % (port, tag))
387
388 except PExpectError:
389 # recurse on error
390 self._switch_connect()
391 self.port_add_trunk_to_vlan(port, tag)
392
Steve McIntyreef73fca2015-07-07 14:57:59 +0100393 # Remove a trunk port from a specified VLAN (tag). Shouldn't be
394 # needed on this switch as we don't do VLAN ingress filtering on
395 # "trunk" ports, but...
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100396 def port_remove_trunk_from_vlan(self, port, tag):
397 logging.debug("Removing trunk port %s from VLAN %d", port, tag)
398 if not self._is_port_name_valid(port):
399 raise InputError("Port name %s not recognised" % port)
400 if not (self.port_get_mode(port) == "trunk"):
401 raise InputError("Port %s not in trunk mode" % port)
402
403 try:
404 self._configure()
405 self._cli("interface %s" % port)
Steve McIntyreef73fca2015-07-07 14:57:59 +0100406 self._cli("vlan participation auto %d" % tag)
Steve McIntyrefc7a6272015-07-07 16:22:32 +0100407 self._end_interface()
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100408 self._end_configure()
409
410 # Validate it happened
411 read_vlans = self.port_get_trunk_vlan_list(port)
412 for vlan in read_vlans:
413 if vlan == tag:
414 raise IOError("Failed to remove trunk port %s from VLAN %d" % (port, tag))
415
416 except PExpectError:
417 # recurse on error
418 self._switch_connect()
419 self.port_remove_trunk_from_vlan(port, tag)
420
421 # Get the configured VLAN tag for an access port (tag)
422 def port_get_access_vlan(self, port):
423 logging.debug("Getting VLAN for access port %s", port)
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100424 if not self._is_port_name_valid(port):
425 raise InputError("Port name %s not recognised" % port)
426 if not (self.port_get_mode(port) == "access"):
427 raise InputError("Port %s not in access mode" % port)
Steve McIntyreef73fca2015-07-07 14:57:59 +0100428 return self._get_port_vlans(port)
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100429
430 # Get the list of configured VLAN tags for a trunk port
431 def port_get_trunk_vlan_list(self, port):
Steve McIntyreef73fca2015-07-07 14:57:59 +0100432 logging.debug("Getting VLAN(s) for trunk port %s", port)
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100433 if not self._is_port_name_valid(port):
434 raise InputError("Port name %s not recognised" % port)
435 if not (self.port_get_mode(port) == "trunk"):
436 raise InputError("Port %s not in trunk mode" % port)
Steve McIntyreef73fca2015-07-07 14:57:59 +0100437 return self._get_port_vlans(port)
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100438
439 ################################
440 ### Internal functions
441 ################################
442
443 # Connect to the switch and log in
444 def _switch_connect(self):
445
446 if not self.connection is None:
447 self.connection.close(True)
448 self.connection = None
449
450 logging.debug("Connecting to Switch with: %s", self.exec_string)
451 self.connection = pexpect.spawn(self.exec_string, logfile = self.logfile)
452
453 self._login()
454
455 # Avoid paged output
456 self._cli("terminal length 0")
457
458 # And grab details about the switch. in case we need it
459 self._get_systemdata()
460
461 # And also validate them - make sure we're driving a switch of
462 # the correct model! Also store the serial number
463 manuf_regex = re.compile('^Manufacturer([\.\s])+(\S+)')
464 model_regex = re.compile('^Machine Model([\.\s])+(\S+)')
465 sn_regex = re.compile('^Serial Number([\.\s])+(\S+)')
466 descr = ""
467
468 for line in self._systemdata:
469 match1 = manuf_regex.match(line)
470 if match1:
471 manuf = match1.group(2)
472
473 match2 = model_regex.match(line)
474 if match2:
475 model = match2.group(2)
476
477 match3 = sn_regex.match(line)
478 if match3:
479 self.serial_number = match3.group(2)
480
481 logging.debug("manufacturer is %s", manuf)
482 logging.debug("model is %s", model)
483 logging.debug("serial number is %s", self.serial_number)
484
485 if not (self._expected_manuf.match(manuf) and self._expected_model.match(model)):
486 raise IOError("Switch %s %s not recognised by this driver: abort" % (manuf, model))
487
488 # Now build a list of our ports, for later sanity checking
489 self._ports = self._get_port_names()
490 if len(self._ports) < 4:
491 raise IOError("Not enough ports detected - problem!")
492
493 def _login(self):
494 logging.debug("attempting login with username %s, password %s", self._username, self._password)
495 if self._username is not None:
496 self.connection.expect("User:")
497 self._cli("%s" % self._username)
498 if self._password is not None:
499 self.connection.expect("Password:")
500 self._cli("%s" % self._password, False)
501 while True:
502 index = self.connection.expect(['User:', 'Password:', 'Bad passwords', 'authentication failed', r'(.*)(#|>)'])
503 if index != 4: # Any other means: failed to log in!
504 logging.error("Login failure: index %d\n", index)
505 logging.error("Login failure: %s\n", self.connection.match.before)
506 raise IOError
507
508 # else
509 self._prompt_name = re.escape(self.connection.match.group(1).strip())
510 logging.error('\n\nSet _prompt_name to "%s"\n\n\n' % self._prompt_name)
511 if self.connection.match.group(2) == ">":
512 # Need to enter "enable" mode too
513 self._cli("enable")
514 if self._enable_password is not None:
515 self.connection.expect("Password:")
516 self._cli("%s" % self._enable_password, False)
517 index = self.connection.expect(['Password:', 'Bad passwords', 'authentication failed', r'(.*) *(#|>)'])
518 if index != 3: # Any other means: failed to log in!
519 logging.error("Enable password failure: %s\n", self.connection.match)
520 raise IOError
521 return 0
522
523 def _logout(self):
524 logging.debug("Logging out")
525 self._cli("quit", False)
526 self.connection.expect("Would you like to save them now")
527 self._cli("n")
528
529 def _configure(self):
530 self._cli("configure")
531
532 def _end_configure(self):
533 self._cli("exit")
534
Steve McIntyrefc7a6272015-07-07 16:22:32 +0100535 def _end_interface(self):
536 self._end_configure()
537
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100538 def _read_long_output(self, text):
539 longbuf = []
540 prompt = self._prompt_name + '\s*#'
541 while True:
542 try:
543 index = self.connection.expect(['--More--', prompt])
544 if index == 0: # "--More-- or (q)uit"
545 for line in self.connection.before.split('\r\n'):
546 line1 = re.sub('(\x08|\x0D)*', '', line.strip())
547 longbuf.append(line1)
548 self._cli(' ', False)
549 elif index == 1: # Back to a prompt, says output is finished
550 break
551 except (pexpect.EOF, pexpect.TIMEOUT):
552 # Something went wrong; logout, log in and try again!
553 logging.error("PEXPECT FAILURE, RECONNECT")
554 self.errors.log_error_in(text)
555 raise PExpectError("_read_long_output failed")
556 except:
557 logging.error("prompt is \"%s\"", prompt)
558 raise
559
560 for line in self.connection.before.split('\r\n'):
561 longbuf.append(line.strip())
562 return longbuf
563
564 def _get_port_names(self):
565 logging.debug("Grabbing list of ports")
566 interfaces = []
567
568 # Use "Up" or "Down" to only identify lines in the output that
569 # match interfaces that exist
570 regex = re.compile('^(1\S+)')
571
572 try:
573 self._cli("show port all")
574 for line in self._read_long_output("show port all"):
575 match = regex.match(line)
576 if match:
577 interface = match.group(1)
578 interfaces.append(interface)
579 return interfaces
580
581 except PExpectError:
582 # recurse on error
583 self._switch_connect()
584 return self._get_port_names()
585
586 def _show_config(self):
587 logging.debug("Grabbing config")
588 try:
589 self._cli("show running-config")
590 return self._read_long_output("show running-config")
591 except PExpectError:
592 # recurse on error
593 self._switch_connect()
594 return self._show_config()
595
596 def _show_clock(self):
597 logging.debug("Grabbing time")
598 try:
599 self._cli("show clock")
600 return self._read_long_output("show clock")
601 except PExpectError:
602 # recurse on error
603 self._switch_connect()
604 return self._show_clock()
605
606 def _get_systemdata(self):
607 logging.debug("Grabbing system sw and hw versions")
608
609 try:
610 self._systemdata = []
611 self._cli("show version")
612 for line in self._read_long_output("show version"):
613 self._systemdata.append(line)
614
615 except PExpectError:
616 # recurse on error
617 self._switch_connect()
618 return self._get_systemdata()
619
620 def _parse_vlan_list(self, inputdata):
621 vlans = []
622
623 if inputdata == "ALL":
624 return ["ALL"]
625 elif inputdata == "NONE":
626 return []
627 else:
628 # Parse the complex list
629 groups = inputdata.split(',')
630 for group in groups:
631 subgroups = group.split('-')
632 if len(subgroups) == 1:
633 vlans.append(int(subgroups[0]))
634 elif len(subgroups) == 2:
635 for i in range (int(subgroups[0]), int(subgroups[1]) + 1):
636 vlans.append(i)
637 else:
638 logging.debug("Can't parse group \"" + group + "\"")
639
640 return vlans
641
Steve McIntyreef73fca2015-07-07 14:57:59 +0100642 def _get_port_vlans(self, port):
643 vlan_text = None
644
645 vlan_part_re = re.compile('vlan participation (.*)')
646
647 try:
648 self._cli("show running-config interface %s" % port)
649 for line in self._read_long_output("show running-config interface"):
650 match = vlan_part_re.match(line)
651 if match:
652 if vlan_text != None:
653 vlan_text += ","
654 vlan_text += (match.group(1))
655 else:
656 vlan_text = match.group(1)
657
658 vlans = self._parse_vlan_list(vlan_text)
659 return vlans
660
661 except PExpectError:
662 # recurse on error
663 self._switch_connect()
664 return self._get_port_vlans(port)
665
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100666 # Wrapper around connection.send - by default, expect() the same
667 # text we've sent, to remove it from the output from the
668 # switch. For the few cases where we don't need that, override
669 # this using echo=False.
670 # Horrible, but seems to work.
671 def _cli(self, text, echo=True):
672 self.connection.send(text + '\r')
673 if echo:
674 try:
675 self.connection.expect(text)
676 except (pexpect.EOF, pexpect.TIMEOUT):
677 # Something went wrong; logout, log in and try again!
678 logging.error("PEXPECT FAILURE, RECONNECT")
679 self.errors.log_error_out(text)
680 raise PExpectError("_cli failed on %s" % text)
681 except:
682 logging.error("Unexpected error: %s", sys.exc_info()[0])
683 raise
684
685if __name__ == "__main__":
686
687 import optparse
688
689 switch = 'vlandswitch05'
690 parser = optparse.OptionParser()
691 parser.add_option("--switch",
692 dest = "switch",
693 action = "store",
694 nargs = 1,
695 type = "string",
696 help = "specify switch to connect to for testing",
697 metavar = "<switch>")
698 (opts, args) = parser.parse_args()
699 if opts.switch:
700 switch = opts.switch
701
Steve McIntyre144d51c2015-07-07 17:56:30 +0100702 logging.basicConfig(level = logging.DEBUG,
703 format = '%(asctime)s %(levelname)-8s %(message)s')
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100704 p = NetgearXSM(switch, 23, debug=True)
705 p.switch_connect('admin', '', None)
706
707 print "VLANs are:"
708 buf = p.vlan_get_list()
709 p.dump_list(buf)
710
711 buf = p.vlan_get_name(2)
712 print "VLAN 2 is named \"%s\"" % buf
713
714 print "Create VLAN 3"
715 p.vlan_create(3)
716
717 buf = p.vlan_get_name(3)
718 print "VLAN 3 is named \"%s\"" % buf
719
720 print "Set name of VLAN 3 to test333"
721 p.vlan_set_name(3, "test333")
722
723 buf = p.vlan_get_name(3)
724 print "VLAN 3 is named \"%s\"" % buf
725
726 print "VLANs are:"
727 buf = p.vlan_get_list()
728 p.dump_list(buf)
729
730 print "Destroy VLAN 3"
731 p.vlan_destroy(3)
732
733 print "VLANs are:"
734 buf = p.vlan_get_list()
735 p.dump_list(buf)
736
737 buf = p.port_get_mode("1/0/10")
738 print "Port 1/0/10 is in %s mode" % buf
739
740 buf = p.port_get_mode("1/0/11")
741 print "Port 1/0/11 is in %s mode" % buf
742
743 # Test access stuff
744 print "Set 1/0/9 to access mode"
745 p.port_set_mode("1/0/9", "access")
746
747 print "Move 1/0/9 to VLAN 4"
748 p.port_set_access_vlan("1/0/9", 4)
749
750 buf = p.port_get_access_vlan("1/0/9")
751 print "Read from switch: 1/0/9 is on VLAN %s" % buf
752
753 print "Move 1/0/9 back to VLAN 1"
754 p.port_set_access_vlan("1/0/9", 1)
755
756 # Test access stuff
757 print "Set 1/0/9 to trunk mode"
758 p.port_set_mode("1/0/9", "trunk")
759 print "Read from switch: which VLANs is 1/0/9 on?"
760 buf = p.port_get_trunk_vlan_list("1/0/9")
761 p.dump_list(buf)
762 print "Add 1/0/9 to VLAN 2"
763 p.port_add_trunk_to_vlan("1/0/9", 2)
764 print "Add 1/0/9 to VLAN 3"
765 p.port_add_trunk_to_vlan("1/0/9", 3)
766 print "Add 1/0/9 to VLAN 4"
767 p.port_add_trunk_to_vlan("1/0/9", 4)
768 print "Read from switch: which VLANs is 1/0/9 on?"
769 buf = p.port_get_trunk_vlan_list("1/0/9")
770 p.dump_list(buf)
771
772 p.port_remove_trunk_from_vlan("1/0/9", 3)
773 p.port_remove_trunk_from_vlan("1/0/9", 3)
774 p.port_remove_trunk_from_vlan("1/0/9", 4)
775 print "Read from switch: which VLANs is 1/0/9 on?"
776 buf = p.port_get_trunk_vlan_list("1/0/9")
777 p.dump_list(buf)
778
779# print 'Restarting switch, to explicitly reset config'
780# p.switch_restart()
781
782# p.switch_save_running_config()
783
784# p.switch_disconnect()
785# p._show_config()