blob: 44abe956e920fd2ee1fee4a265d0166c8933fddc [file] [log] [blame]
Steve McIntyref1c04f92014-12-16 18:23:15 +00001import logging
2import os
Steve McIntyre53c7ad92014-12-16 19:21:13 +00003import time
Steve McIntyref1c04f92014-12-16 18:23:15 +00004from db.db import VlanDB
5from errors import CriticalError, InputError, ConfigError, SocketError
6
7class VlanUtil:
8 """VLANd utility functions"""
9
Steve McIntyre5f6f85e2014-12-22 16:42:28 +000010 def get_switch_driver(self, switch_name, config):
11 logging.debug("Trying to find a driver for %s" % switch_name)
12 driver = config.switches[switch_name].driver
Steve McIntyref1c04f92014-12-16 18:23:15 +000013 logging.debug("Driver: %s" % driver)
14 module = __import__("drivers.%s" % driver, fromlist=[driver])
15 class_ = getattr(module, driver)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +000016 return class_(switch_name)
Steve McIntyref1c04f92014-12-16 18:23:15 +000017
Steve McIntyrec68a18e2014-12-17 16:29:28 +000018 def get_all_switches(self, config):
Steve McIntyre5f6f85e2014-12-22 16:42:28 +000019 for switch_name in sorted(config.switches):
20 print "Found switch %s:" % (switch_name)
Steve McIntyrea2a8f792014-12-17 17:34:32 +000021 print " Probing:"
Steve McIntyrec68a18e2014-12-17 16:29:28 +000022
Steve McIntyre5f6f85e2014-12-22 16:42:28 +000023 s = util.get_switch_driver(switch_name, config)
24 s.switch_connect(config.switches[switch_name].username,
25 config.switches[switch_name].password,
26 config.switches[switch_name].enablepassword)
Steve McIntyrea2a8f792014-12-17 17:34:32 +000027 print " Found details of switch:"
28 s._dump_list(s._systemdata)
29 print " Switch has %d ports:" % len(s.switch_get_port_names())
30 for port in s.switch_get_port_names():
31 print " %s" % port
32 if 0 == 1:
33 mode = s.port_get_mode(port)
34 if mode == "trunk":
35 print " port %s is in trunk mode, VLAN(s):" % port
36 vlans = s.port_get_trunk_vlan_list(port)
37 for vlan in vlans:
38 name = s.vlan_get_name(vlan)
39 print " %d (%s)" % (vlan, name)
40 else:
41 vlan = s.port_get_access_vlan(port)
42 name = s.vlan_get_name(vlan)
43 print " port %s is in access mode, VLAN %d (%s):" % (port, vlan, name)
44 s.switch_disconnect()
45 del(s)
Steve McIntyrec68a18e2014-12-17 16:29:28 +000046
Steve McIntyre091e2ac2014-12-16 19:20:07 +000047 # Simple helper wrapper for all the read-only database queries
Steve McIntyre2150bc22014-12-17 13:13:56 +000048 def perform_db_query(self, state, command, data):
Steve McIntyre091e2ac2014-12-16 19:20:07 +000049 print 'perform_db_query'
Steve McIntyref1c04f92014-12-16 18:23:15 +000050 print command
51 print data
52 ret = {}
Steve McIntyre2150bc22014-12-17 13:13:56 +000053 db = state.db
Steve McIntyref1c04f92014-12-16 18:23:15 +000054 try:
Steve McIntyre091e2ac2014-12-16 19:20:07 +000055 if command == 'db.all_switches':
Steve McIntyref1c04f92014-12-16 18:23:15 +000056 ret = db.all_switches()
57 elif command == 'db.all_ports':
58 ret = db.all_ports()
59 elif command == 'db.all_vlans':
60 ret = db.all_vlans()
61 elif command == 'db.get_switch_by_id':
62 ret = db.get_switch_by_id(data['switch_id'])
63 elif command == 'db.get_switch_id_by_name':
64 ret = db.get_switch_id_by_name(data['name'])
65 elif command == 'db.get_switch_name_by_id':
66 ret = db.get_switch_name_by_id(data['switch_id'])
67 elif command == 'db.get_port_by_id':
68 ret = db.get_port_by_id(data['port_id'])
69 elif command == 'db.get_ports_by_switch':
70 ret = db.get_ports_by_switch(data['switch_id'])
71 elif command == 'db.get_port_by_switch_and_name':
72 ret = db.get_port_by_switch_and_name(data['switch_id'], data['name'])
73 elif command == 'db.get_current_vlan_id_by_port':
74 ret = db.get_current_vlan_id_by_port(data['port_id'])
75 elif command == 'db.get_base_vlan_id_by_port':
76 ret = db.get_base_vlan_id_by_port(data['port_id'])
77 elif command == 'db.get_ports_by_current_vlan':
78 ret = db.get_ports_by_current_vlan(data['vlan_id'])
79 elif command == 'db.get_ports_by_base_vlan':
80 ret = db.get_ports_by_base_vlan(data['vlan_id'])
81 elif command == 'db.get_vlan_by_id':
82 ret = db.get_vlan_by_id(data['vlan_id'])
83 elif command == 'db.get_vlan_id_by_name':
84 ret = db.get_vlan_id_by_name(data['name'])
85 elif command == 'db.get_vlan_id_by_tag':
Steve McIntyre07946c22014-12-17 13:14:15 +000086 ret = db.get_vlan_id_by_tag(data['tag'])
Steve McIntyref1c04f92014-12-16 18:23:15 +000087 elif command == 'db.get_vlan_name_by_id':
88 ret = db.get_vlan_name_by_id(data['vlan_id'])
89 else:
Steve McIntyree749fef2014-12-17 16:35:45 +000090 raise InputError("Unknown db_query command \"%s\"" % command)
Steve McIntyref1c04f92014-12-16 18:23:15 +000091
Steve McIntyre5da37fa2014-12-17 13:14:44 +000092 except InputError:
93 raise
94
Steve McIntyref1c04f92014-12-16 18:23:15 +000095 except:
96 raise InputError("Invalid input in query")
97
98 return ret
99
Steve McIntyre53c7ad92014-12-16 19:21:13 +0000100 # Simple helper wrapper for all the read-only daemon state queries
101 def perform_daemon_query(self, state, command, data):
102 print 'perform_daemon_query'
103 print command
104 print data
105 ret = {}
106 try:
107 if command == 'daemon.status':
108 # data ignored
109 ret['running'] = 'ok'
110 elif command == 'daemon.version':
111 # data ignored
112 ret['version'] = state.version
113 elif command == 'daemon.statistics':
114 ret['uptime'] = time.time() - state.starttime
115 else:
Steve McIntyree749fef2014-12-17 16:35:45 +0000116 raise InputError("Unknown daemon_query command \"%s\"" % command)
Steve McIntyre53c7ad92014-12-16 19:21:13 +0000117
Steve McIntyrea590b5b2014-12-17 13:15:14 +0000118 except InputError:
119 raise
120
121 except:
122 raise InputError("Invalid input in query")
123
124 return ret
125
Steve McIntyree749fef2014-12-17 16:35:45 +0000126 # Helper wrapper for API functions modifying database state only
Steve McIntyrea590b5b2014-12-17 13:15:14 +0000127 def perform_db_update(self, state, command, data):
128 print 'perform_db_update'
129 print command
130 print data
131 ret = {}
132 db = state.db
133 try:
134 if command == 'db.create_switch':
135 ret = db.create_switch(data['name'])
136 elif command == 'db.create_port':
137 ret = db.create_port(data['switch_id'], data['name'],
Steve McIntyrefefdbb42014-12-22 16:14:28 +0000138 state.default_vlan_id,
139 state.default_vlan_id)
Steve McIntyrea590b5b2014-12-17 13:15:14 +0000140 elif command == 'db.delete_switch':
141 ret = db.delete_switch(data['switch_id'])
142 elif command == 'db.delete_port':
143 ret = db.delete_port(data['port_id'])
144 elif command == 'db.set_port_is_locked':
145 ret = db.set_port_is_locked(data['port_id'], data['is_locked'])
146 elif command == 'db.set_base_vlan':
147 ret = db.set_base_vlan(data['port_id'], data['base_vlan_id'])
148 else:
Steve McIntyre0f9dea62014-12-17 16:37:01 +0000149 raise InputError("Unknown db_update command \"%s\"" % command)
150
151 except InputError:
152 raise
153
154 except:
155 raise InputError("Invalid input in query")
156
157 return ret
158
159 # Helper wrapper for API functions that modify both database state
160 # and on-switch VLAN state
161 def perform_vlan_update(self, state, command, data):
162 print 'perform_vlan_update'
163 print command
164 print data
165 ret = {}
166 db = state.db
167 try:
168 # All of these are complex commands, so call helpers
169 # rather than inline the code here
170 if command == 'api.create_vlan':
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000171 ret = self.create_vlan(state, data['name'], int(data['tag']), data['is_base_vlan'])
Steve McIntyre0f9dea62014-12-17 16:37:01 +0000172 elif command == 'api.delete_vlan':
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000173 ret = self.delete_vlan(state, int(data['vlan_id']))
Steve McIntyre0f9dea62014-12-17 16:37:01 +0000174 elif command == 'api.set_port_mode':
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000175 ret = self.set_port_mode(state, int(data['port_id']), data['mode'])
Steve McIntyre0f9dea62014-12-17 16:37:01 +0000176 elif command == 'api.set_current_vlan':
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000177 ret = self.set_current_vlan(state, int(data['port_id']), int(data['vlan_id']))
Steve McIntyre0f9dea62014-12-17 16:37:01 +0000178 elif command == 'api.restore_base_vlan':
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000179 ret = self.restore_base_vlan(state, int(data['port_id']))
180 elif command == 'api.auto_import_switch':
181 ret = self.auto_import_switch(state, data['switch'])
Steve McIntyre0f9dea62014-12-17 16:37:01 +0000182 else:
Steve McIntyrea590b5b2014-12-17 13:15:14 +0000183 raise InputError("Unknown query command \"%s\"" % command)
184
Steve McIntyre3256b182014-12-19 15:38:15 +0000185 except InputError as e:
186 print 'got error %s' % e
Steve McIntyrea590b5b2014-12-17 13:15:14 +0000187 raise
188
Steve McIntyre53c7ad92014-12-16 19:21:13 +0000189 except:
190 raise InputError("Invalid input in query")
191
192 return ret
193
194
Steve McIntyre3256b182014-12-19 15:38:15 +0000195 # Complex call
196 # 1. create the VLAN in the DB
197 # 2. Iterate through all switches:
Steve McIntyre1ab8b872014-12-19 18:37:00 +0000198 # a. Create the VLAN
199 # b. Add the VLAN to all trunk ports (if needed)
200 # 3. If all went OK, save config on all the switches
Steve McIntyre3256b182014-12-19 15:38:15 +0000201 #
202 # The VLAN may already exist on some of the switches, that's
Steve McIntyre153157d2014-12-19 18:05:20 +0000203 # fine. If things fail, we attempt to roll back by rebooting
204 # switches then removing the VLAN in the DB.
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000205 def create_vlan(self, state, name, tag, is_base_vlan):
Steve McIntyre3256b182014-12-19 15:38:15 +0000206
207 print 'create_vlan'
208 db = state.db
209 config = state.config
210
Steve McIntyre3256b182014-12-19 15:38:15 +0000211 # 1. Database record first
212 try:
213 print 'Adding DB record first: name %s, tag %d, is_base_vlan %d' % (name, tag, is_base_vlan)
214 vlan_id = db.create_vlan(name, tag, is_base_vlan)
215 print 'Added VLAN tag %d, name %s to the database, created VLAN ID %d' % (tag, name, vlan_id)
216 except InputError:
217 print 'DB creation failed'
218 raise
219
Steve McIntyre153157d2014-12-19 18:05:20 +0000220 # Keep track of which switches we've configured, for later use
221 switches_done = []
222
Steve McIntyre3256b182014-12-19 15:38:15 +0000223 # 2. Now the switches
224 try:
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000225 for switch in db.all_switches():
Steve McIntyre3256b182014-12-19 15:38:15 +0000226 trunk_ports = []
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000227 switch_name = switch.name
Steve McIntyre3256b182014-12-19 15:38:15 +0000228 try:
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000229 print 'Adding new VLAN to switch %s' % switch_name
Steve McIntyre3256b182014-12-19 15:38:15 +0000230 # Get the right driver
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000231 s = self.get_switch_driver(switch_name, config)
232 s.switch_connect(config.switches[switch_name].username,
233 config.switches[switch_name].password,
234 config.switches[switch_name].enablepassword)
Steve McIntyre3256b182014-12-19 15:38:15 +0000235
Steve McIntyre153157d2014-12-19 18:05:20 +0000236 # Mark this switch as one we've touched, for
237 # either config saving or rollback below
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000238 switches_done.append(switch_name)
Steve McIntyre153157d2014-12-19 18:05:20 +0000239
Steve McIntyre3256b182014-12-19 15:38:15 +0000240 # 2a. Create the VLAN on the switch
241 s.vlan_create(tag)
242 s.vlan_set_name(tag, name)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000243 print 'Added VLAN tag %d, name %s to switch %s' % (tag, name, switch_name)
Steve McIntyre3256b182014-12-19 15:38:15 +0000244
245 # 2b. Do we need to worry about trunk ports on this switch?
246 if 'TrunkWildCardVlans' in s.switch_get_capabilities():
247 print 'This switch does not need special trunk port handling'
248 else:
249 print 'This switch needs special trunk port handling'
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000250 trunk_ports = db.get_trunk_port_names_by_switch(switch.switch_id)
Steve McIntyre3256b182014-12-19 15:38:15 +0000251 if trunk_ports is None:
252 print "But it has no trunk ports defined"
253 trunk_ports = []
254 else:
255 print 'Found %d trunk_ports that need adjusting' % len(trunk_ports)
256
257 # Modify any trunk ports as needed
258 for port in trunk_ports:
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000259 print 'Added VLAN tag %d, name %s to switch %s' % (tag, name, switch_name)
Steve McIntyre3256b182014-12-19 15:38:15 +0000260 s.port_add_trunk_to_vlan(port, tag)
261
Steve McIntyre3256b182014-12-19 15:38:15 +0000262 # And now we're done with this switch
263 s.switch_disconnect()
264 del s
265
266 except IOError:
267 raise
268
269 except IOError:
Steve McIntyre153157d2014-12-19 18:05:20 +0000270 # Bugger. Looks like one of the switch calls above
271 # failed. To undo the changes safely, we'll need to reset
272 # all the switches we managed to configure. This could
273 # take some time!
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000274 for switch_name in switches_done:
275 s = self.get_switch_driver(switch_name, config)
276 s.switch_connect(config.switches[switch_name].username,
277 config.switches[switch_name].password,
278 config.switches[switch_name].enablepassword)
Steve McIntyre153157d2014-12-19 18:05:20 +0000279 s.switch_restart() # Will implicitly also close the connection
280 del s
281
Steve McIntyre3256b182014-12-19 15:38:15 +0000282 # Undo the database change
283 print 'Switch access failed. Deleting the new VLAN entry in the database'
284 db.delete_vlan(vlan_id)
285 raise
286
Steve McIntyre153157d2014-12-19 18:05:20 +0000287 # If we've got this far, things were successful. Save config
288 # on all the switches so it will persist across reboots
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000289 for switch_name in switches_done:
290 s = self.get_switch_driver(switch_name, config)
291 s.switch_connect(config.switches[switch_name].username,
292 config.switches[switch_name].password,
293 config.switches[switch_name].enablepassword)
Steve McIntyre153157d2014-12-19 18:05:20 +0000294 s.switch_save_running_config()
295 s.switch_disconnect()
296 del s
297
Steve McIntyre3256b182014-12-19 15:38:15 +0000298 return vlan_id # If we're successful
299
Steve McIntyrefeb64522014-12-19 18:53:02 +0000300 # Complex call
301 # 1. Check in the DB if there are any ports on the VLAN. Bail if so
302 # 2. Iterate through all switches:
303 # a. Remove the VLAN from all trunk ports (if needed)
304 # b. Remove the VLAN
305 # 3. If all went OK, save config on the switches
306 # 4. Remove the VLAN in the DB
307 #
308 # If things fail, we attempt to roll back by rebooting switches.
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000309 def delete_vlan(self, state, vlan_id):
Steve McIntyrefeb64522014-12-19 18:53:02 +0000310
311 print 'delete_vlan'
312 db = state.db
313 config = state.config
314
Steve McIntyrefeb64522014-12-19 18:53:02 +0000315 # 1. Check for database records first
Steve McIntyre4a43ab02014-12-22 01:47:03 +0000316 print 'Checking for ports using VLAN ID %d' % vlan_id
Steve McIntyrefeb64522014-12-19 18:53:02 +0000317 vlan = db.get_vlan_by_id(vlan_id)
318 if vlan is None:
319 raise InputError("VLAN ID %d does not exist" % vlan_id)
320 vlan_tag = vlan.tag
321 ports = db.get_ports_by_current_vlan(vlan_id)
322 if ports is not None:
323 raise InputError("Cannot delete VLAN ID %d when it still has %d ports" %
324 (vlan_id, len(ports)))
325 ports = db.get_ports_by_base_vlan(vlan_id)
326 if ports is not None:
327 raise InputError("Cannot delete VLAN ID %d when it still has %d ports" %
328 (vlan_id, len(ports)))
329
330 # Keep track of which switches we've configured, for later use
331 switches_done = []
332
333 # 2. Now the switches
334 try:
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000335 for switch in db.all_switches():
336 switch_name = switch.name
Steve McIntyrefeb64522014-12-19 18:53:02 +0000337 trunk_ports = []
338 try:
339 # Get the right driver
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000340 s = self.get_switch_driver(switch_name, config)
341 s.switch_connect(config.switches[switch_name].username,
342 config.switches[switch_name].password,
343 config.switches[switch_name].enablepassword)
Steve McIntyrefeb64522014-12-19 18:53:02 +0000344
345 # Mark this switch as one we've touched, for
346 # either config saving or rollback below
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000347 switches_done.append(switch_name)
Steve McIntyrefeb64522014-12-19 18:53:02 +0000348
349 # 2a. Do we need to worry about trunk ports on this switch?
350 if 'TrunkWildCardVlans' in s.switch_get_capabilities():
351 print 'This switch does not need special trunk port handling'
352 else:
353 print 'This switch needs special trunk port handling'
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000354 trunk_ports = db.get_trunk_port_names_by_switch(switch.switch_id)
Steve McIntyrefeb64522014-12-19 18:53:02 +0000355 if trunk_ports is None:
356 print "But it has no trunk ports defined"
357 trunk_ports = []
358 else:
359 print 'Found %d trunk_ports that need adjusting' % len(trunk_ports)
360
361 # Modify any trunk ports as needed
362 for port in trunk_ports:
363 s.port_remove_trunk_from_vlan(port, tag)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000364 print 'Removed VLAN tag %d from switch %s port %s' % (vlan_tag, switch_name, port)
Steve McIntyrefeb64522014-12-19 18:53:02 +0000365
366 # 2b. Remove the VLAN from the switch
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000367 print 'Removing VLAN tag %d from switch %s' % (vlan_tag, switch_name)
Steve McIntyrefeb64522014-12-19 18:53:02 +0000368 s.vlan_destroy(vlan_tag)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000369 print 'Removed VLAN tag %d from switch %s' % (vlan_tag, switch_name)
Steve McIntyrefeb64522014-12-19 18:53:02 +0000370
371 # And now we're done with this switch
372 s.switch_disconnect()
373 del s
374
375 except IOError:
376 raise
377
378 except IOError:
379 # Bugger. Looks like one of the switch calls above
380 # failed. To undo the changes safely, we'll need to reset
381 # all the switches we managed to configure. This could
382 # take some time!
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000383 for switch_name in switches_done:
384 s = self.get_switch_driver(switch_name, config)
385 s.switch_connect(config.switches[switch_name].username,
386 config.switches[switch_name].password,
387 config.switches[switch_name].enablepassword)
Steve McIntyrefeb64522014-12-19 18:53:02 +0000388 s.switch_restart() # Will implicitly also close the connection
389 del s
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000390 raise
Steve McIntyrefeb64522014-12-19 18:53:02 +0000391
392 # 3. If we've got this far, things were successful. Save
393 # config on all the switches so it will persist across reboots
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000394 for switch_name in switches_done:
395 s = self.get_switch_driver(switch_name, config)
396 s.switch_connect(config.switches[switch_name].username,
397 config.switches[switch_name].password,
398 config.switches[switch_name].enablepassword)
Steve McIntyrefeb64522014-12-19 18:53:02 +0000399 s.switch_save_running_config()
400 s.switch_disconnect()
401 del s
402
403 # 4. Finally, remove the VLAN in the DB
404 try:
Steve McIntyre4a43ab02014-12-22 01:47:03 +0000405 print 'Removing DB record: VLAN ID %d' % vlan_id
Steve McIntyrefeb64522014-12-19 18:53:02 +0000406 vlan_id = db.delete_vlan(vlan_id)
Steve McIntyre4a43ab02014-12-22 01:47:03 +0000407 print 'Removed VLAN ID %d from the database OK' % vlan_id
Steve McIntyrefeb64522014-12-19 18:53:02 +0000408 except InputError:
409 print 'DB deletion failed'
410 raise
411
412 return vlan_id # If we're successful
413
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000414 # Complex call, depends on existing state a lot
415 # 1. Check validity of inputs
416 # 2. Switch mode and other config on the port.
417 # a. If switching trunk->access, remove all trunk VLANs from it
418 # (if needed) and switch back to the base VLAN for the
419 # port. Next, switch to access mode.
420 # b. If switching access->trunk, switch back to the base VLAN
421 # for the port. Next, switch mode. Then add all trunk VLANs
422 # to it (if needed)
423 # 3. If all went OK, save config on the switch
424 # 4. Change details of the port in the DB
425 #
426 # If things fail, we attempt to roll back by rebooting the switch
427 def set_port_mode(self, port_id, mode):
Steve McIntyrefeb64522014-12-19 18:53:02 +0000428
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000429 print 'set_port_mode'
430 db = state.db
431 config = state.config
432
433 # 1. Sanity-check inputs
434 if mode is not 'access' and mode is not 'trunk':
435 raise InputError("Port mode %s is not a valid option: try 'access' or 'trunk" % mode)
436 port = db.get_port_by_id(port_id)
437 if port is None:
438 raise InputError("Port ID %d does not exist" % port_id)
439 if mode == 'trunk' and port.is_trunk:
440 raise InputError("Port ID %d is already in trunk mode")
441 if mode == 'access' and not port.is_trunk:
442 raise InputError("Port ID %d is already in access mode")
443 base_vlan_tag = db.get_vlan_tag_by_id(port.base_vlan_id)
444
445 # Get the right driver
446 switch_name = db.get_switch_name_by_id(port.switch_id)
447 s = self.get_switch_driver(switch_name, config)
448
449 # 2. Now start configuring the switch
450 try:
451 s.switch_connect(config.switches[switch_name].username,
452 config.switches[switch_name].password,
453 config.switches[switch_name].enablepassword)
454 except:
455 print 'Failed to talk to switch %s!' % switch_name
456 raise
457
458 try:
459 if port.is_trunk:
460 # 2a. We're going from a trunk port to an access port
461 if 'TrunkWildCardVlans' in s.switch_get_capabilities():
462 print 'This switch does not need special trunk port handling'
463 else:
464 print 'This switch needs special trunk port handling'
465 vlans = s.port_get_trunk_vlan_list(port.name)
466 if vlans is None:
467 print "But it has no VLANs defined on port %s" % port.name
468 vlans = []
469 else:
470 print 'Found %d vlans that need dropping on port %s' % (len(trunk_ports), port.name)
471
472 for vlan in vlans:
473 s.port_remove_trunk_from_vlan(port.name, vlan)
474 s.port_add_trunk_to_vlan(port.name, base_vlan_tag)
475 s.port_set_mode(port.name, "access")
476
477 else:
478 # 2b. We're going from an access port to a trunk port
479 s.port_set_access_vlan(port.name, base_vlan_tag)
480 s.port_set_mode(port.name, "trunk")
481 if 'TrunkWildCardVlans' in s.switch_get_capabilities():
482 print 'This switch does not need special trunk port handling'
483 else:
484 vlans = db.all_vlans()
485 for vlan in vlans:
486 s.port_add_trunk_to_vlan(port.name, vlan.tag)
487
488 except IOError:
489 # Bugger. Looks like one of the switch calls above
490 # failed. To undo the changes safely, we'll need to reset
491 # all the config on this switch
492 s.switch_restart() # Will implicitly also close the connection
493 del s
494 raise
495
496 # 3. All seems to have worked so far!
497 s.switch_save_running_config()
498 s.switch_disconnect()
499 del s
500
501 # 4. And update the DB
502 db.set_port_mode(port_id, mode)
503
504 return port_id # If we're successful
505
506 # Complex call, updating both DB and switch state
507 # 1. Check validity of inputs
508 # 2. Update the port config on the switch
509 # 3. If all went OK, save config on the switch
510 # 4. Change details of the port in the DB
511 #
512 # If things fail, we attempt to roll back by rebooting the switch
513 def set_current_vlan(self, state, port_id, vlan_id):
514
515 print 'set_current_vlan'
516 db = state.db
517 config = state.config
518
519 # 1. Sanity checks!
520 port = db.get_port_by_id(port_id)
521 if port is None:
522 raise InputError("Port ID %d does not exist" % port_id)
523 if port.is_trunk:
524 raise InputError("Port ID %d is not an access port" % port_id)
525 if port.is_locked:
526 raise InputError("Port ID %d is locked" % port_id)
527
528 vlan = db.get_vlan_by_id(vlan_id)
529 if vlan is None:
530 raise InputError("VLAN ID %d does not exist" % vlan_id)
531
532 # Get the right driver
533 switch_name = db.get_switch_name_by_id(port.switch_id)
534 s = self.get_switch_driver(switch_name, config)
535
536 # 2. Now start configuring the switch
537 try:
538 s.switch_connect(config.switches[switch_name].username,
539 config.switches[switch_name].password,
540 config.switches[switch_name].enablepassword)
541 except:
542 print 'Failed to talk to switch %s!' % switch_name
543 raise
544
545 try:
546 s.port_set_access_vlan(port.name, vlan.tag)
547 except IOError:
548 # Bugger. Looks like one of the switch calls above
549 # failed. To undo the changes safely, we'll need to reset
550 # all the config on this switch
551 s.switch_restart() # Will implicitly also close the connection
552 del s
553 raise
554
555 # 3. All seems to have worked so far!
556 s.switch_save_running_config()
557 s.switch_disconnect()
558 del s
559
560 # 4. And update the DB
561 db.set_current_vlan(port_id, vlan_id)
562
563 return port_id # If we're successful
564
565 # Complex call, updating both DB and switch state
566 # 1. Check validity of input
567 # 2. Update the port config on the switch
568 # 3. If all went OK, save config on the switch
569 # 4. Change details of the port in the DB
570 #
571 # If things fail, we attempt to roll back by rebooting the switch
572 def restore_base_vlan(self, state, port_id):
573
574 print 'restore_base_vlan'
575 db = state.db
576 config = state.config
577
578 port_id = int(data['port_id'])
579
580 # 1. Sanity checks!
581 port = db.get_port_by_id(port_id)
582 if port is None:
583 raise InputError("Port ID %d does not exist" % port_id)
584 if port.is_trunk:
585 raise InputError("Port ID %d is not an access port" % port_id)
586 if port.is_locked:
587 raise InputError("Port ID %d is locked" % port_id)
588
589 # Bail out early if we're *already* on the base VLAN. This is
590 # not an error
591 if port.current_vlan_id == port.base_vlan_id:
592 return port_id
593
594 vlan = db.get_vlan_by_id(port.base_vlan_id)
595
596 # Get the right driver
597 switch_name = db.get_switch_name_by_id(port.switch_id)
598 s = self.get_switch_driver(switch_name, config)
599
600 # 2. Now start configuring the switch
601 try:
602 s.switch_connect(config.switches[switch_name].username,
603 config.switches[switch_name].password,
604 config.switches[switch_name].enablepassword)
605 except:
606 print 'Failed to talk to switch %s!' % switch_name
607 raise
608
609 try:
610 s.port_set_access_vlan(port.name, vlan.tag)
611 except IOError:
612 # Bugger. Looks like one of the switch calls above
613 # failed. To undo the changes safely, we'll need to reset
614 # all the config on this switch
615 s.switch_restart() # Will implicitly also close the connection
616 del s
617 raise
618
619 # 3. All seems to have worked so far!
620 s.switch_save_running_config()
621 s.switch_disconnect()
622 del s
623
624 # 4. And update the DB
625 db.set_current_vlan(port_id, port.base_vlan_id)
626
627 return port_id # If we're successful
628
629 # Complex call, updating both DB and switch state
630 # * Check validity of input
631 # * Read all the config from the switch (switch, ports, VLANs)
632 # * Create initial DB entries to match each of those
633 # * Merge VLANs across all switches
634 # * Set up ports appropriately
635 #
636 def auto_import_switch(self, state, switch_name):
637
638 print 'auto_import_switch'
639 db = state.db
640 config = state.config
641
642 # 1. Sanity checks!
643 switch_id = db.get_switch_id_by_name(switch_name)
644 if switch_id is not None:
645 raise InputError("Switch name %s already exists in the DB (ID %d)" % (switch_name, switch_id))
646
647 if not switch in config.switches:
648 raise InputError("Switch name %s not defined in config" % switch_name)
649
650 # 2. Now start reading config from the switch
651 try:
652 s = self.get_switch_driver(switch_name, config)
653 s.switch_connect(config.switches[switch_name].username,
654 config.switches[switch_name].password,
655 config.switches[switch_name].enablepassword)
656 except:
657 print 'Failed to talk to switch %s!' % switch_name
658 raise
659
660 # DON'T create the switch record in the DB first - we'll want
661 # to create VLANs on *other* switches, and it's easier to do that before we've added our new switch
662
663 new_vlan_tags = []
664
665 # Grab the VLANs defined on this switch
666 vlan_tags = s.vlan_get_list()
667 for vlan_tag in vlan_tags:
668 vlan_name = s.vlan_get_name(vlan_tag)
669
670 # If a VLAN is already in the database, then that's easy -
671 # we can just ignore it. However, we have to check that
672 # there is not a different name for the existing VLAN tag
673 # - bail out if so...
674 #
675 # If this VLAN tag is not already in the DB, we'll need to
676 # add it there and to all the other switches (and their
677 # trunk ports!) too.
678 vlan_id = db.get_vlan_id_by_tag(vlan_tag)
679 if vlan_id is not None:
680 vlan_db_name = db.get_vlan_name_by_id(vlan_id)
681 if vlan_name != vlan_db_name:
682 raise InputError("Can't add VLAN tag %d (name %s) for this switch - VLAN tag %d already exists in the database, but with a different name (%s)" % (vlan_tag, vlan_name, vlan_tag, vlan_db_name))
683
684 else:
685 # OK, we'll need to set up the new VLAN now. It can't
686 # be a base VLAN - switches don't have such a concept!
687 # Rather than create individually here, add to a
688 # list. *Only* once we've worked through all the
689 # switch's VLANs successfully (checking for existing
690 # records and possible clashes!) should we start
691 # committing changes
692 new_vlan_tags.append(vlan_tag)
693
694 # Now create the VLAN DB entries
695 for vlan_tag in new_vlan_tags:
696 vlan_name = s.vlan_get_name(vlan_tag)
697 vlan_id = self.create_vlan(state, vlan_name, vlan_tag, False)
698
699 # *Now* add this switch itself to the database, after we've
700 # worked on all the other switches
701 switch_id = db.create_switch(switch_name)
702
703 switch_modified = False
704
705 # And now the ports
706 trunk_ports = []
707 ports = s.switch_get_port_names()
708 for port_name in ports:
709 port_id = None
710 port_mode = s.port_get_mode(port_name)
711 if port_mode == 'access':
712 # Access ports are easy - just create the port, and
713 # set both the current and base VLANs to the current
714 # VLAN on the switch. We'll end up changing this after
715 # import if needed.
716 port_vlan = s.port_get_access_vlan(port_name)
717 port_id = db.create_port(switch_id, port_name, port_vlan, port_vlan)
718 # Nothing further needed
719 elif port_mode == 'trunk':
720 # Trunk ports are a little more involved. First,
721 # create the port in the DB, setting the VLANs to the
722 # first VLAN found on the trunk port. This will *also*
723 # be in access mode by default, and unlocked.
724 port_vlans = s.port_get_trunk_vlan_list(port_name)
725 if port_vlans == 'ALL':
726 port_vlans = (state.default_vlan_id,) # easy for our purposes
727 port_id = db.create_port(switch_id, port_name,
728 port_vlans[0], port_vlans[0])
729 # Append to a list of trunk ports that we will need to
730 # modify once we're done
731 trunk_ports.append(port_id)
732
733 # Done with this switch in the main loop here
734 s.switch_disconnect()
735 del s
736
737 # Now on each trunk port we need to add all the VLANs across
738 # our system, so it's ready to go. Each time we call this, it
739 # will connect, disconnect, save config etc.
740 for port_id in trunk_ports:
741 self.set_port_mode(port_id, 'trunk')
742
743 ret['switch_id'] = switch_id
744 ret['num_ports_added'] = len(ports)
745 ret['num_vlans_added'] = len(new_vlan_tags)
746 return ret # If we're successful