blob: e0620c7e7e40a911d481de21723fd1ca5554cfb8 [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 McIntyre06c644a2015-07-17 17:07:41 +0100421 self.connection = pexpect.spawn(self.exec_string, logfile=self.logger)
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100422 self._login()
423
424 # Avoid paged output
425 self._cli("terminal length 0")
426
427 # And grab details about the switch. in case we need it
428 self._get_systemdata()
429
430 # And also validate them - make sure we're driving a switch of
431 # the correct model! Also store the serial number
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100432 manuf_regex = re.compile(r'^Manufacturer([\.\s])+(\S+)')
433 model_regex = re.compile(r'^Machine Model([\.\s])+(\S+)')
434 sn_regex = re.compile(r'^Serial Number([\.\s])+(\S+)')
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100435
436 for line in self._systemdata:
437 match1 = manuf_regex.match(line)
438 if match1:
439 manuf = match1.group(2)
440
441 match2 = model_regex.match(line)
442 if match2:
443 model = match2.group(2)
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100444
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100445 match3 = sn_regex.match(line)
446 if match3:
447 self.serial_number = match3.group(2)
448
449 logging.debug("manufacturer is %s", manuf)
450 logging.debug("model is %s", model)
451 logging.debug("serial number is %s", self.serial_number)
452
453 if not (self._expected_manuf.match(manuf) and self._expected_model.match(model)):
454 raise IOError("Switch %s %s not recognised by this driver: abort" % (manuf, model))
455
456 # Now build a list of our ports, for later sanity checking
457 self._ports = self._get_port_names()
458 if len(self._ports) < 4:
459 raise IOError("Not enough ports detected - problem!")
460
461 def _login(self):
462 logging.debug("attempting login with username %s, password %s", self._username, self._password)
463 if self._username is not None:
464 self.connection.expect("User:")
465 self._cli("%s" % self._username)
466 if self._password is not None:
467 self.connection.expect("Password:")
468 self._cli("%s" % self._password, False)
469 while True:
470 index = self.connection.expect(['User:', 'Password:', 'Bad passwords', 'authentication failed', r'(.*)(#|>)'])
471 if index != 4: # Any other means: failed to log in!
472 logging.error("Login failure: index %d\n", index)
473 logging.error("Login failure: %s\n", self.connection.match.before)
474 raise IOError
475
476 # else
477 self._prompt_name = re.escape(self.connection.match.group(1).strip())
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100478 if self.connection.match.group(2) == ">":
479 # Need to enter "enable" mode too
480 self._cli("enable")
481 if self._enable_password is not None:
482 self.connection.expect("Password:")
483 self._cli("%s" % self._enable_password, False)
484 index = self.connection.expect(['Password:', 'Bad passwords', 'authentication failed', r'(.*) *(#|>)'])
485 if index != 3: # Any other means: failed to log in!
486 logging.error("Enable password failure: %s\n", self.connection.match)
487 raise IOError
488 return 0
489
490 def _logout(self):
491 logging.debug("Logging out")
492 self._cli("quit", False)
Steve McIntyredfce73f2015-07-09 17:41:55 +0100493 try:
494 self.connection.expect("Would you like to save them now")
495 self._cli("n")
496 except (pexpect.EOF):
497 pass
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100498
499 def _configure(self):
500 self._cli("configure")
501
502 def _end_configure(self):
503 self._cli("exit")
504
Steve McIntyrefc7a6272015-07-07 16:22:32 +0100505 def _end_interface(self):
506 self._end_configure()
507
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100508 def _read_long_output(self, text):
509 longbuf = []
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100510 prompt = self._prompt_name + r'\s*#'
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100511 while True:
512 try:
513 index = self.connection.expect(['--More--', prompt])
514 if index == 0: # "--More-- or (q)uit"
515 for line in self.connection.before.split('\r\n'):
516 line1 = re.sub('(\x08|\x0D)*', '', line.strip())
517 longbuf.append(line1)
518 self._cli(' ', False)
519 elif index == 1: # Back to a prompt, says output is finished
520 break
521 except (pexpect.EOF, pexpect.TIMEOUT):
522 # Something went wrong; logout, log in and try again!
523 logging.error("PEXPECT FAILURE, RECONNECT")
524 self.errors.log_error_in(text)
525 raise PExpectError("_read_long_output failed")
526 except:
527 logging.error("prompt is \"%s\"", prompt)
528 raise
529
530 for line in self.connection.before.split('\r\n'):
531 longbuf.append(line.strip())
532 return longbuf
533
534 def _get_port_names(self):
535 logging.debug("Grabbing list of ports")
536 interfaces = []
537
538 # Use "Up" or "Down" to only identify lines in the output that
539 # match interfaces that exist
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100540 regex = re.compile(r'^(1\S+)')
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100541
542 try:
543 self._cli("show port all")
544 for line in self._read_long_output("show port all"):
545 match = regex.match(line)
546 if match:
547 interface = match.group(1)
548 interfaces.append(interface)
Steve McIntyred601ab82015-07-09 17:42:36 +0100549 logging.debug(" found %d ports on the switch", len(interfaces))
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100550 return interfaces
551
552 except PExpectError:
553 # recurse on error
554 self._switch_connect()
555 return self._get_port_names()
556
557 def _show_config(self):
558 logging.debug("Grabbing config")
559 try:
560 self._cli("show running-config")
561 return self._read_long_output("show running-config")
562 except PExpectError:
563 # recurse on error
564 self._switch_connect()
565 return self._show_config()
566
567 def _show_clock(self):
568 logging.debug("Grabbing time")
569 try:
570 self._cli("show clock")
571 return self._read_long_output("show clock")
572 except PExpectError:
573 # recurse on error
574 self._switch_connect()
575 return self._show_clock()
576
577 def _get_systemdata(self):
578 logging.debug("Grabbing system sw and hw versions")
579
580 try:
581 self._systemdata = []
582 self._cli("show version")
583 for line in self._read_long_output("show version"):
584 self._systemdata.append(line)
585
586 except PExpectError:
587 # recurse on error
588 self._switch_connect()
589 return self._get_systemdata()
590
591 def _parse_vlan_list(self, inputdata):
592 vlans = []
593
594 if inputdata == "ALL":
595 return ["ALL"]
596 elif inputdata == "NONE":
597 return []
598 else:
599 # Parse the complex list
600 groups = inputdata.split(',')
601 for group in groups:
602 subgroups = group.split('-')
603 if len(subgroups) == 1:
604 vlans.append(int(subgroups[0]))
605 elif len(subgroups) == 2:
606 for i in range (int(subgroups[0]), int(subgroups[1]) + 1):
607 vlans.append(i)
608 else:
609 logging.debug("Can't parse group \"" + group + "\"")
610
611 return vlans
612
Steve McIntyreef73fca2015-07-07 14:57:59 +0100613 def _get_port_vlans(self, port):
614 vlan_text = None
615
Steve McIntyrec08cca22015-07-07 18:00:33 +0100616 vlan_part_re = re.compile('vlan participation include (.*)')
Steve McIntyreef73fca2015-07-07 14:57:59 +0100617
618 try:
619 self._cli("show running-config interface %s" % port)
620 for line in self._read_long_output("show running-config interface"):
621 match = vlan_part_re.match(line)
622 if match:
623 if vlan_text != None:
624 vlan_text += ","
625 vlan_text += (match.group(1))
626 else:
627 vlan_text = match.group(1)
Steve McIntyre8009ffe2015-07-07 18:02:21 +0100628
629 if vlan_text is None:
630 return [1]
631 else:
632 vlans = self._parse_vlan_list(vlan_text)
633 return vlans
Steve McIntyreef73fca2015-07-07 14:57:59 +0100634
635 except PExpectError:
636 # recurse on error
637 self._switch_connect()
638 return self._get_port_vlans(port)
639
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100640 # Wrapper around connection.send - by default, expect() the same
641 # text we've sent, to remove it from the output from the
642 # switch. For the few cases where we don't need that, override
643 # this using echo=False.
644 # Horrible, but seems to work.
645 def _cli(self, text, echo=True):
646 self.connection.send(text + '\r')
647 if echo:
648 try:
649 self.connection.expect(text)
650 except (pexpect.EOF, pexpect.TIMEOUT):
651 # Something went wrong; logout, log in and try again!
652 logging.error("PEXPECT FAILURE, RECONNECT")
653 self.errors.log_error_out(text)
654 raise PExpectError("_cli failed on %s" % text)
655 except:
656 logging.error("Unexpected error: %s", sys.exc_info()[0])
657 raise
658
659if __name__ == "__main__":
660
661 import optparse
662
663 switch = 'vlandswitch05'
664 parser = optparse.OptionParser()
665 parser.add_option("--switch",
666 dest = "switch",
667 action = "store",
668 nargs = 1,
669 type = "string",
670 help = "specify switch to connect to for testing",
671 metavar = "<switch>")
672 (opts, args) = parser.parse_args()
673 if opts.switch:
674 switch = opts.switch
675
Steve McIntyre144d51c2015-07-07 17:56:30 +0100676 logging.basicConfig(level = logging.DEBUG,
677 format = '%(asctime)s %(levelname)-8s %(message)s')
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100678 p = NetgearXSM(switch, 23, debug=True)
679 p.switch_connect('admin', '', None)
680
681 print "VLANs are:"
682 buf = p.vlan_get_list()
683 p.dump_list(buf)
684
685 buf = p.vlan_get_name(2)
686 print "VLAN 2 is named \"%s\"" % buf
687
688 print "Create VLAN 3"
689 p.vlan_create(3)
690
691 buf = p.vlan_get_name(3)
692 print "VLAN 3 is named \"%s\"" % buf
693
694 print "Set name of VLAN 3 to test333"
695 p.vlan_set_name(3, "test333")
696
697 buf = p.vlan_get_name(3)
698 print "VLAN 3 is named \"%s\"" % buf
699
700 print "VLANs are:"
701 buf = p.vlan_get_list()
702 p.dump_list(buf)
703
704 print "Destroy VLAN 3"
705 p.vlan_destroy(3)
706
707 print "VLANs are:"
708 buf = p.vlan_get_list()
709 p.dump_list(buf)
710
711 buf = p.port_get_mode("1/0/10")
712 print "Port 1/0/10 is in %s mode" % buf
713
714 buf = p.port_get_mode("1/0/11")
715 print "Port 1/0/11 is in %s mode" % buf
716
717 # Test access stuff
718 print "Set 1/0/9 to access mode"
719 p.port_set_mode("1/0/9", "access")
720
721 print "Move 1/0/9 to VLAN 4"
722 p.port_set_access_vlan("1/0/9", 4)
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100723
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100724 buf = p.port_get_access_vlan("1/0/9")
725 print "Read from switch: 1/0/9 is on VLAN %s" % buf
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100726
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100727 print "Move 1/0/9 back to VLAN 1"
728 p.port_set_access_vlan("1/0/9", 1)
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100729
Steve McIntyre40484f12015-07-07 18:02:50 +0100730 print "Create VLAN 2"
Steve McIntyre3a1b4832015-07-08 15:42:09 +0100731 p.vlan_create(2)
Steve McIntyre40484f12015-07-07 18:02:50 +0100732
733 print "Create VLAN 3"
734 p.vlan_create(3)
735
736 print "Create VLAN 4"
737 p.vlan_create(4)
738
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100739 # Test access stuff
740 print "Set 1/0/9 to trunk mode"
741 p.port_set_mode("1/0/9", "trunk")
742 print "Read from switch: which VLANs is 1/0/9 on?"
743 buf = p.port_get_trunk_vlan_list("1/0/9")
744 p.dump_list(buf)
Steve McIntyre1c8a3212015-07-14 17:07:31 +0100745
Steve McIntyre12b9ef92015-07-08 15:48:44 +0100746 # The adds below are NOOPs in effect on this switch - no filtering
747 # for "trunk" ports
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100748 print "Add 1/0/9 to VLAN 2"
749 p.port_add_trunk_to_vlan("1/0/9", 2)
750 print "Add 1/0/9 to VLAN 3"
751 p.port_add_trunk_to_vlan("1/0/9", 3)
752 print "Add 1/0/9 to VLAN 4"
753 p.port_add_trunk_to_vlan("1/0/9", 4)
754 print "Read from switch: which VLANs is 1/0/9 on?"
755 buf = p.port_get_trunk_vlan_list("1/0/9")
756 p.dump_list(buf)
757
Steve McIntyre12b9ef92015-07-08 15:48:44 +0100758 # And the same for removals here
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100759 p.port_remove_trunk_from_vlan("1/0/9", 3)
Steve McIntyre60671382015-07-08 15:49:03 +0100760 p.port_remove_trunk_from_vlan("1/0/9", 2)
Steve McIntyrefdcb3782015-07-06 16:34:56 +0100761 p.port_remove_trunk_from_vlan("1/0/9", 4)
762 print "Read from switch: which VLANs is 1/0/9 on?"
763 buf = p.port_get_trunk_vlan_list("1/0/9")
764 p.dump_list(buf)
765
766# print 'Restarting switch, to explicitly reset config'
767# p.switch_restart()
768
769# p.switch_save_running_config()
770
771# p.switch_disconnect()
772# p._show_config()