blob: ce41f0ca63fdb498106d47d58ccef9cccd676608 [file] [log] [blame]
Steve McIntyref1c04f92014-12-16 18:23:15 +00001import logging
Steve McIntyre53c7ad92014-12-16 19:21:13 +00002import time
Steve McIntyref1c04f92014-12-16 18:23:15 +00003from errors import CriticalError, InputError, ConfigError, SocketError
4
5class VlanUtil:
6 """VLANd utility functions"""
7
Steve McIntyre5f6f85e2014-12-22 16:42:28 +00008 def get_switch_driver(self, switch_name, config):
Steve McIntyre5fa22652015-04-01 18:01:45 +01009 logging.debug("Trying to find a driver for %s", switch_name)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +000010 driver = config.switches[switch_name].driver
Steve McIntyre5fa22652015-04-01 18:01:45 +010011 logging.debug("Driver: %s", driver)
Steve McIntyref1c04f92014-12-16 18:23:15 +000012 module = __import__("drivers.%s" % driver, fromlist=[driver])
13 class_ = getattr(module, driver)
Steve McIntyre0f38f0e2015-07-14 15:26:05 +010014 return class_(switch_name, debug = config.switches[switch_name].debug)
Steve McIntyref1c04f92014-12-16 18:23:15 +000015
Steve McIntyre519158e2014-12-23 13:44:44 +000016 def probe_switches(self, state):
17 config = state.config
Steve McIntyree8d80582014-12-23 16:53:39 +000018 ret = {}
Steve McIntyre5f6f85e2014-12-22 16:42:28 +000019 for switch_name in sorted(config.switches):
Steve McIntyre5fa22652015-04-01 18:01:45 +010020 logging.debug("Found switch %s:", switch_name)
Steve McIntyre7cf80982015-02-12 07:03:40 +000021 logging.debug(" Probing...")
Steve McIntyre519158e2014-12-23 13:44:44 +000022
Steve McIntyre4b4ab652014-12-22 17:19:09 +000023 s = self.get_switch_driver(switch_name, config)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +000024 s.switch_connect(config.switches[switch_name].username,
25 config.switches[switch_name].password,
Steve McIntyre3b655af2014-12-23 13:43:19 +000026 config.switches[switch_name].enable_password)
Steve McIntyre27d4b582014-12-23 22:51:00 +000027 ret[switch_name] = 'Found %d ports: ' % len(s.switch_get_port_names())
28 for name in s.switch_get_port_names():
29 ret[switch_name] += '%s ' % name
Steve McIntyrea2a8f792014-12-17 17:34:32 +000030 s.switch_disconnect()
Steve McIntyre2ed90f52015-07-21 17:59:52 +010031 del s
Steve McIntyrea2020cb2014-12-23 16:56:40 +000032 return ret
Steve McIntyrec68a18e2014-12-17 16:29:28 +000033
Steve McIntyre091e2ac2014-12-16 19:20:07 +000034 # Simple helper wrapper for all the read-only database queries
Steve McIntyre2150bc22014-12-17 13:13:56 +000035 def perform_db_query(self, state, command, data):
Steve McIntyre7cf80982015-02-12 07:03:40 +000036 logging.debug('perform_db_query')
37 logging.debug(command)
38 logging.debug(data)
Steve McIntyref1c04f92014-12-16 18:23:15 +000039 ret = {}
Steve McIntyre2150bc22014-12-17 13:13:56 +000040 db = state.db
Steve McIntyref1c04f92014-12-16 18:23:15 +000041 try:
Steve McIntyre091e2ac2014-12-16 19:20:07 +000042 if command == 'db.all_switches':
Steve McIntyref1c04f92014-12-16 18:23:15 +000043 ret = db.all_switches()
44 elif command == 'db.all_ports':
45 ret = db.all_ports()
46 elif command == 'db.all_vlans':
47 ret = db.all_vlans()
Steve McIntyrec4890132015-08-07 15:19:11 +010048 elif command == 'db.all_trunks':
49 ret = db.all_trunks()
Steve McIntyref1c04f92014-12-16 18:23:15 +000050 elif command == 'db.get_switch_by_id':
51 ret = db.get_switch_by_id(data['switch_id'])
52 elif command == 'db.get_switch_id_by_name':
53 ret = db.get_switch_id_by_name(data['name'])
54 elif command == 'db.get_switch_name_by_id':
55 ret = db.get_switch_name_by_id(data['switch_id'])
56 elif command == 'db.get_port_by_id':
57 ret = db.get_port_by_id(data['port_id'])
58 elif command == 'db.get_ports_by_switch':
59 ret = db.get_ports_by_switch(data['switch_id'])
60 elif command == 'db.get_port_by_switch_and_name':
61 ret = db.get_port_by_switch_and_name(data['switch_id'], data['name'])
Steve McIntyre45f55012015-08-05 13:55:15 +010062 elif command == 'db.get_port_by_switch_and_number':
63 ret = db.get_port_by_switch_and_number(data['switch_id'], int(data['number']))
Steve McIntyref1c04f92014-12-16 18:23:15 +000064 elif command == 'db.get_current_vlan_id_by_port':
65 ret = db.get_current_vlan_id_by_port(data['port_id'])
66 elif command == 'db.get_base_vlan_id_by_port':
67 ret = db.get_base_vlan_id_by_port(data['port_id'])
68 elif command == 'db.get_ports_by_current_vlan':
69 ret = db.get_ports_by_current_vlan(data['vlan_id'])
70 elif command == 'db.get_ports_by_base_vlan':
71 ret = db.get_ports_by_base_vlan(data['vlan_id'])
Steve McIntyrec4890132015-08-07 15:19:11 +010072 elif command == 'db.get_ports_by_trunk':
73 ret = db.get_ports_by_trunk(data['trunk_id'])
Steve McIntyref1c04f92014-12-16 18:23:15 +000074 elif command == 'db.get_vlan_by_id':
75 ret = db.get_vlan_by_id(data['vlan_id'])
Steve McIntyre65533d72015-01-23 18:01:17 +000076 elif command == 'db.get_vlan_tag_by_id':
77 ret = db.get_vlan_tag_by_id(data['vlan_id'])
Steve McIntyref1c04f92014-12-16 18:23:15 +000078 elif command == 'db.get_vlan_id_by_name':
79 ret = db.get_vlan_id_by_name(data['name'])
80 elif command == 'db.get_vlan_id_by_tag':
Steve McIntyre07946c22014-12-17 13:14:15 +000081 ret = db.get_vlan_id_by_tag(data['tag'])
Steve McIntyref1c04f92014-12-16 18:23:15 +000082 elif command == 'db.get_vlan_name_by_id':
83 ret = db.get_vlan_name_by_id(data['vlan_id'])
Steve McIntyrec4890132015-08-07 15:19:11 +010084 elif command == 'db.get_trunk_by_id':
85 ret = db.get_trunk_by_id(data['trunk_id'])
Steve McIntyref1c04f92014-12-16 18:23:15 +000086 else:
Steve McIntyree749fef2014-12-17 16:35:45 +000087 raise InputError("Unknown db_query command \"%s\"" % command)
Steve McIntyref1c04f92014-12-16 18:23:15 +000088
Steve McIntyre5da37fa2014-12-17 13:14:44 +000089 except InputError:
90 raise
91
Steve McIntyre798af842014-12-23 22:29:46 +000092# except:
93# raise InputError("Invalid input in query")
Steve McIntyref1c04f92014-12-16 18:23:15 +000094
95 return ret
96
Steve McIntyre53c7ad92014-12-16 19:21:13 +000097 # Simple helper wrapper for all the read-only daemon state queries
98 def perform_daemon_query(self, state, command, data):
Steve McIntyre7cf80982015-02-12 07:03:40 +000099 logging.debug('perform_daemon_query')
100 logging.debug(command)
101 logging.debug(data)
Steve McIntyre53c7ad92014-12-16 19:21:13 +0000102 ret = {}
103 try:
104 if command == 'daemon.status':
105 # data ignored
106 ret['running'] = 'ok'
107 elif command == 'daemon.version':
108 # data ignored
109 ret['version'] = state.version
110 elif command == 'daemon.statistics':
111 ret['uptime'] = time.time() - state.starttime
Steve McIntyre88b79df2014-12-23 13:45:08 +0000112 elif command == 'daemon.probe_switches':
113 ret = self.probe_switches(state)
Steve McIntyre06fe6422015-01-23 17:55:43 +0000114 elif command == 'daemon.shutdown':
115 # data ignored
116 ret['shutdown'] = 'Shutting down'
117 state.running = False
Steve McIntyre53c7ad92014-12-16 19:21:13 +0000118 else:
Steve McIntyree749fef2014-12-17 16:35:45 +0000119 raise InputError("Unknown daemon_query command \"%s\"" % command)
Steve McIntyre53c7ad92014-12-16 19:21:13 +0000120
Steve McIntyrea590b5b2014-12-17 13:15:14 +0000121 except InputError:
122 raise
123
Steve McIntyre798af842014-12-23 22:29:46 +0000124# except:
125# raise InputError("Invalid input in query")
Steve McIntyrea590b5b2014-12-17 13:15:14 +0000126
127 return ret
128
Steve McIntyree749fef2014-12-17 16:35:45 +0000129 # Helper wrapper for API functions modifying database state only
Steve McIntyrea590b5b2014-12-17 13:15:14 +0000130 def perform_db_update(self, state, command, data):
Steve McIntyre7cf80982015-02-12 07:03:40 +0000131 logging.debug('perform_db_update')
132 logging.debug(command)
133 logging.debug(data)
Steve McIntyrea590b5b2014-12-17 13:15:14 +0000134 ret = {}
135 db = state.db
136 try:
137 if command == 'db.create_switch':
138 ret = db.create_switch(data['name'])
139 elif command == 'db.create_port':
Steve McIntyreca6adfc2015-08-06 15:08:58 +0100140 try:
141 number = int(data['number'])
142 except ValueError:
143 raise InputError("Invalid value for port number (%s) - must be numeric only!" % data['number'])
Steve McIntyrea590b5b2014-12-17 13:15:14 +0000144 ret = db.create_port(data['switch_id'], data['name'],
Steve McIntyreca6adfc2015-08-06 15:08:58 +0100145 number,
Steve McIntyrefefdbb42014-12-22 16:14:28 +0000146 state.default_vlan_id,
147 state.default_vlan_id)
Steve McIntyrec4890132015-08-07 15:19:11 +0100148 if command == 'db.create_trunk':
149 ret = db.create_trunk(data['port_id1'], data['port_id2'])
Steve McIntyrea590b5b2014-12-17 13:15:14 +0000150 elif command == 'db.delete_switch':
151 ret = db.delete_switch(data['switch_id'])
152 elif command == 'db.delete_port':
153 ret = db.delete_port(data['port_id'])
154 elif command == 'db.set_port_is_locked':
155 ret = db.set_port_is_locked(data['port_id'], data['is_locked'])
156 elif command == 'db.set_base_vlan':
157 ret = db.set_base_vlan(data['port_id'], data['base_vlan_id'])
Steve McIntyrec4890132015-08-07 15:19:11 +0100158 elif command == 'db.delete_trunk':
159 ret = db.delete_trunk(data['trunk_id'])
Steve McIntyrea590b5b2014-12-17 13:15:14 +0000160 else:
Steve McIntyre0f9dea62014-12-17 16:37:01 +0000161 raise InputError("Unknown db_update command \"%s\"" % command)
162
163 except InputError:
164 raise
165
Steve McIntyre798af842014-12-23 22:29:46 +0000166# except:
167# raise InputError("Invalid input in query")
Steve McIntyre0f9dea62014-12-17 16:37:01 +0000168
169 return ret
170
171 # Helper wrapper for API functions that modify both database state
172 # and on-switch VLAN state
173 def perform_vlan_update(self, state, command, data):
Steve McIntyre7cf80982015-02-12 07:03:40 +0000174 logging.debug('perform_vlan_update')
175 logging.debug(command)
176 logging.debug(data)
Steve McIntyre0f9dea62014-12-17 16:37:01 +0000177 ret = {}
Steve McIntyre4b4ab652014-12-22 17:19:09 +0000178
Steve McIntyre0f9dea62014-12-17 16:37:01 +0000179 try:
180 # All of these are complex commands, so call helpers
181 # rather than inline the code here
182 if command == 'api.create_vlan':
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000183 ret = self.create_vlan(state, data['name'], int(data['tag']), data['is_base_vlan'])
Steve McIntyre0f9dea62014-12-17 16:37:01 +0000184 elif command == 'api.delete_vlan':
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000185 ret = self.delete_vlan(state, int(data['vlan_id']))
Steve McIntyre0f9dea62014-12-17 16:37:01 +0000186 elif command == 'api.set_port_mode':
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000187 ret = self.set_port_mode(state, int(data['port_id']), data['mode'])
Steve McIntyre0f9dea62014-12-17 16:37:01 +0000188 elif command == 'api.set_current_vlan':
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000189 ret = self.set_current_vlan(state, int(data['port_id']), int(data['vlan_id']))
Steve McIntyre0f9dea62014-12-17 16:37:01 +0000190 elif command == 'api.restore_base_vlan':
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000191 ret = self.restore_base_vlan(state, int(data['port_id']))
192 elif command == 'api.auto_import_switch':
193 ret = self.auto_import_switch(state, data['switch'])
Steve McIntyre0f9dea62014-12-17 16:37:01 +0000194 else:
Steve McIntyrea590b5b2014-12-17 13:15:14 +0000195 raise InputError("Unknown query command \"%s\"" % command)
196
Steve McIntyre3256b182014-12-19 15:38:15 +0000197 except InputError as e:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100198 logging.debug('got error %s', e)
Steve McIntyrea590b5b2014-12-17 13:15:14 +0000199 raise
200
Steve McIntyre798af842014-12-23 22:29:46 +0000201# except:
202# raise InputError("Invalid input in query")
Steve McIntyre53c7ad92014-12-16 19:21:13 +0000203
204 return ret
205
206
Steve McIntyre3256b182014-12-19 15:38:15 +0000207 # Complex call
208 # 1. create the VLAN in the DB
209 # 2. Iterate through all switches:
Steve McIntyre1ab8b872014-12-19 18:37:00 +0000210 # a. Create the VLAN
211 # b. Add the VLAN to all trunk ports (if needed)
212 # 3. If all went OK, save config on all the switches
Steve McIntyre3256b182014-12-19 15:38:15 +0000213 #
214 # The VLAN may already exist on some of the switches, that's
Steve McIntyre153157d2014-12-19 18:05:20 +0000215 # fine. If things fail, we attempt to roll back by rebooting
216 # switches then removing the VLAN in the DB.
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000217 def create_vlan(self, state, name, tag, is_base_vlan):
Steve McIntyre3256b182014-12-19 15:38:15 +0000218
Steve McIntyre7cf80982015-02-12 07:03:40 +0000219 logging.debug('create_vlan')
Steve McIntyre3256b182014-12-19 15:38:15 +0000220 db = state.db
221 config = state.config
222
Steve McIntyre6b8d3862015-08-03 19:28:25 +0100223 # Check for tag == -1, i.e. use the next available tag
224 if tag == -1:
225 tag = db.find_lowest_unused_vlan_tag()
226 logging.debug('create_vlan called with a tag of -1, found first unused tag %d', tag)
227
Steve McIntyre3256b182014-12-19 15:38:15 +0000228 # 1. Database record first
229 try:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100230 logging.debug('Adding DB record first: name %s, tag %d, is_base_vlan %d', name, tag, is_base_vlan)
Steve McIntyre3256b182014-12-19 15:38:15 +0000231 vlan_id = db.create_vlan(name, tag, is_base_vlan)
Steve McIntyre5fa22652015-04-01 18:01:45 +0100232 logging.debug('Added VLAN tag %d, name %s to the database, created VLAN ID %d', tag, name, vlan_id)
Steve McIntyre3256b182014-12-19 15:38:15 +0000233 except InputError:
Steve McIntyre7cf80982015-02-12 07:03:40 +0000234 logging.debug('DB creation failed')
Steve McIntyre3256b182014-12-19 15:38:15 +0000235 raise
236
Steve McIntyre153157d2014-12-19 18:05:20 +0000237 # Keep track of which switches we've configured, for later use
238 switches_done = []
239
Steve McIntyre3256b182014-12-19 15:38:15 +0000240 # 2. Now the switches
241 try:
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000242 for switch in db.all_switches():
Steve McIntyre3256b182014-12-19 15:38:15 +0000243 trunk_ports = []
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000244 switch_name = switch.name
Steve McIntyre3256b182014-12-19 15:38:15 +0000245 try:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100246 logging.debug('Adding new VLAN to switch %s', switch_name)
Steve McIntyre3256b182014-12-19 15:38:15 +0000247 # Get the right driver
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000248 s = self.get_switch_driver(switch_name, config)
249 s.switch_connect(config.switches[switch_name].username,
250 config.switches[switch_name].password,
Steve McIntyre3b655af2014-12-23 13:43:19 +0000251 config.switches[switch_name].enable_password)
Steve McIntyre3256b182014-12-19 15:38:15 +0000252
Steve McIntyre153157d2014-12-19 18:05:20 +0000253 # Mark this switch as one we've touched, for
254 # either config saving or rollback below
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000255 switches_done.append(switch_name)
Steve McIntyre153157d2014-12-19 18:05:20 +0000256
Steve McIntyre3256b182014-12-19 15:38:15 +0000257 # 2a. Create the VLAN on the switch
258 s.vlan_create(tag)
259 s.vlan_set_name(tag, name)
Steve McIntyre5fa22652015-04-01 18:01:45 +0100260 logging.debug('Added VLAN tag %d, name %s to switch %s', tag, name, switch_name)
Steve McIntyre3256b182014-12-19 15:38:15 +0000261
262 # 2b. Do we need to worry about trunk ports on this switch?
263 if 'TrunkWildCardVlans' in s.switch_get_capabilities():
Steve McIntyre7cf80982015-02-12 07:03:40 +0000264 logging.debug('This switch does not need special trunk port handling')
Steve McIntyre3256b182014-12-19 15:38:15 +0000265 else:
Steve McIntyre7cf80982015-02-12 07:03:40 +0000266 logging.debug('This switch needs special trunk port handling')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000267 trunk_ports = db.get_trunk_port_names_by_switch(switch.switch_id)
Steve McIntyre3256b182014-12-19 15:38:15 +0000268 if trunk_ports is None:
Steve McIntyre7cf80982015-02-12 07:03:40 +0000269 logging.debug("But it has no trunk ports defined")
Steve McIntyre3256b182014-12-19 15:38:15 +0000270 trunk_ports = []
271 else:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100272 logging.debug('Found %d trunk_ports that need adjusting', len(trunk_ports))
Steve McIntyre3256b182014-12-19 15:38:15 +0000273
274 # Modify any trunk ports as needed
275 for port in trunk_ports:
Steve McIntyre1ce09f92015-09-04 14:35:13 +0100276 logging.debug('Adding VLAN tag %d, name %s to switch %s port %s', tag, name, switch_name, port)
Steve McIntyre3256b182014-12-19 15:38:15 +0000277 s.port_add_trunk_to_vlan(port, tag)
278
Steve McIntyre3256b182014-12-19 15:38:15 +0000279 # And now we're done with this switch
280 s.switch_disconnect()
281 del s
282
Steve McIntyre8f27ae12015-09-04 14:35:38 +0100283 except IOError as e:
284 logging.error('Failed to add VLAN %d to switch ID %d (%s): %s', tag, switch.switch_id, switch.name)
Steve McIntyre3256b182014-12-19 15:38:15 +0000285 raise
Steve McIntyreb826fc72015-07-27 17:57:40 +0100286
Steve McIntyre3256b182014-12-19 15:38:15 +0000287 except IOError:
Steve McIntyre153157d2014-12-19 18:05:20 +0000288 # Bugger. Looks like one of the switch calls above
289 # failed. To undo the changes safely, we'll need to reset
290 # all the switches we managed to configure. This could
291 # take some time!
Steve McIntyre38a24e52015-07-21 17:54:58 +0100292 logging.error('create_vlan failed, resetting all switches to recover')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000293 for switch_name in switches_done:
294 s = self.get_switch_driver(switch_name, config)
295 s.switch_connect(config.switches[switch_name].username,
296 config.switches[switch_name].password,
Steve McIntyre3b655af2014-12-23 13:43:19 +0000297 config.switches[switch_name].enable_password)
Steve McIntyre153157d2014-12-19 18:05:20 +0000298 s.switch_restart() # Will implicitly also close the connection
299 del s
300
Steve McIntyre3256b182014-12-19 15:38:15 +0000301 # Undo the database change
Steve McIntyre7cf80982015-02-12 07:03:40 +0000302 logging.debug('Switch access failed. Deleting the new VLAN entry in the database')
Steve McIntyre3256b182014-12-19 15:38:15 +0000303 db.delete_vlan(vlan_id)
304 raise
305
Steve McIntyre153157d2014-12-19 18:05:20 +0000306 # If we've got this far, things were successful. Save config
307 # on all the switches so it will persist across reboots
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000308 for switch_name in switches_done:
309 s = self.get_switch_driver(switch_name, config)
310 s.switch_connect(config.switches[switch_name].username,
311 config.switches[switch_name].password,
Steve McIntyre3b655af2014-12-23 13:43:19 +0000312 config.switches[switch_name].enable_password)
Steve McIntyre153157d2014-12-19 18:05:20 +0000313 s.switch_save_running_config()
314 s.switch_disconnect()
315 del s
316
Steve McIntyre6b8d3862015-08-03 19:28:25 +0100317 return (vlan_id, tag) # If we're successful
Steve McIntyre3256b182014-12-19 15:38:15 +0000318
Steve McIntyrefeb64522014-12-19 18:53:02 +0000319 # Complex call
320 # 1. Check in the DB if there are any ports on the VLAN. Bail if so
321 # 2. Iterate through all switches:
322 # a. Remove the VLAN from all trunk ports (if needed)
323 # b. Remove the VLAN
324 # 3. If all went OK, save config on the switches
325 # 4. Remove the VLAN in the DB
326 #
327 # If things fail, we attempt to roll back by rebooting switches.
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000328 def delete_vlan(self, state, vlan_id):
Steve McIntyrefeb64522014-12-19 18:53:02 +0000329
Steve McIntyre7cf80982015-02-12 07:03:40 +0000330 logging.debug('delete_vlan')
Steve McIntyrefeb64522014-12-19 18:53:02 +0000331 db = state.db
332 config = state.config
333
Steve McIntyrefeb64522014-12-19 18:53:02 +0000334 # 1. Check for database records first
Steve McIntyre5fa22652015-04-01 18:01:45 +0100335 logging.debug('Checking for ports using VLAN ID %d', vlan_id)
Steve McIntyrefeb64522014-12-19 18:53:02 +0000336 vlan = db.get_vlan_by_id(vlan_id)
337 if vlan is None:
338 raise InputError("VLAN ID %d does not exist" % vlan_id)
339 vlan_tag = vlan.tag
340 ports = db.get_ports_by_current_vlan(vlan_id)
341 if ports is not None:
342 raise InputError("Cannot delete VLAN ID %d when it still has %d ports" %
343 (vlan_id, len(ports)))
344 ports = db.get_ports_by_base_vlan(vlan_id)
345 if ports is not None:
346 raise InputError("Cannot delete VLAN ID %d when it still has %d ports" %
347 (vlan_id, len(ports)))
348
349 # Keep track of which switches we've configured, for later use
350 switches_done = []
351
352 # 2. Now the switches
353 try:
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000354 for switch in db.all_switches():
355 switch_name = switch.name
Steve McIntyrefeb64522014-12-19 18:53:02 +0000356 trunk_ports = []
357 try:
358 # Get the right driver
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000359 s = self.get_switch_driver(switch_name, config)
360 s.switch_connect(config.switches[switch_name].username,
361 config.switches[switch_name].password,
Steve McIntyre3b655af2014-12-23 13:43:19 +0000362 config.switches[switch_name].enable_password)
Steve McIntyrefeb64522014-12-19 18:53:02 +0000363
364 # Mark this switch as one we've touched, for
365 # either config saving or rollback below
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000366 switches_done.append(switch_name)
Steve McIntyrefeb64522014-12-19 18:53:02 +0000367
368 # 2a. Do we need to worry about trunk ports on this switch?
369 if 'TrunkWildCardVlans' in s.switch_get_capabilities():
Steve McIntyre7cf80982015-02-12 07:03:40 +0000370 logging.debug('This switch does not need special trunk port handling')
Steve McIntyrefeb64522014-12-19 18:53:02 +0000371 else:
Steve McIntyre7cf80982015-02-12 07:03:40 +0000372 logging.debug('This switch needs special trunk port handling')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000373 trunk_ports = db.get_trunk_port_names_by_switch(switch.switch_id)
Steve McIntyrefeb64522014-12-19 18:53:02 +0000374 if trunk_ports is None:
Steve McIntyre7cf80982015-02-12 07:03:40 +0000375 logging.debug("But it has no trunk ports defined")
Steve McIntyrefeb64522014-12-19 18:53:02 +0000376 trunk_ports = []
377 else:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100378 logging.debug('Found %d trunk_ports that need adjusting', len(trunk_ports))
Steve McIntyrefeb64522014-12-19 18:53:02 +0000379
380 # Modify any trunk ports as needed
381 for port in trunk_ports:
Steve McIntyre4b4ab652014-12-22 17:19:09 +0000382 s.port_remove_trunk_from_vlan(port, vlan_tag)
Steve McIntyre5fa22652015-04-01 18:01:45 +0100383 logging.debug('Removed VLAN tag %d from switch %s port %s', vlan_tag, switch_name, port)
Steve McIntyrefeb64522014-12-19 18:53:02 +0000384
385 # 2b. Remove the VLAN from the switch
Steve McIntyre5fa22652015-04-01 18:01:45 +0100386 logging.debug('Removing VLAN tag %d from switch %s', vlan_tag, switch_name)
Steve McIntyrefeb64522014-12-19 18:53:02 +0000387 s.vlan_destroy(vlan_tag)
Steve McIntyre5fa22652015-04-01 18:01:45 +0100388 logging.debug('Removed VLAN tag %d from switch %s', vlan_tag, switch_name)
Steve McIntyrefeb64522014-12-19 18:53:02 +0000389
390 # And now we're done with this switch
391 s.switch_disconnect()
392 del s
393
394 except IOError:
395 raise
396
397 except IOError:
398 # Bugger. Looks like one of the switch calls above
399 # failed. To undo the changes safely, we'll need to reset
400 # all the switches we managed to configure. This could
401 # take some time!
Steve McIntyre38a24e52015-07-21 17:54:58 +0100402 logging.error('delete_vlan failed, resetting all switches to recover')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000403 for switch_name in switches_done:
404 s = self.get_switch_driver(switch_name, config)
405 s.switch_connect(config.switches[switch_name].username,
406 config.switches[switch_name].password,
Steve McIntyre3b655af2014-12-23 13:43:19 +0000407 config.switches[switch_name].enable_password)
Steve McIntyrefeb64522014-12-19 18:53:02 +0000408 s.switch_restart() # Will implicitly also close the connection
409 del s
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000410 raise
Steve McIntyrefeb64522014-12-19 18:53:02 +0000411
412 # 3. If we've got this far, things were successful. Save
413 # config on all the switches so it will persist across reboots
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000414 for switch_name in switches_done:
415 s = self.get_switch_driver(switch_name, config)
416 s.switch_connect(config.switches[switch_name].username,
417 config.switches[switch_name].password,
Steve McIntyre3b655af2014-12-23 13:43:19 +0000418 config.switches[switch_name].enable_password)
Steve McIntyrefeb64522014-12-19 18:53:02 +0000419 s.switch_save_running_config()
420 s.switch_disconnect()
421 del s
422
423 # 4. Finally, remove the VLAN in the DB
424 try:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100425 logging.debug('Removing DB record: VLAN ID %d', vlan_id)
Steve McIntyrefeb64522014-12-19 18:53:02 +0000426 vlan_id = db.delete_vlan(vlan_id)
Steve McIntyre5fa22652015-04-01 18:01:45 +0100427 logging.debug('Removed VLAN ID %d from the database OK', vlan_id)
Steve McIntyrefeb64522014-12-19 18:53:02 +0000428 except InputError:
Steve McIntyre7cf80982015-02-12 07:03:40 +0000429 logging.debug('DB deletion failed')
Steve McIntyrefeb64522014-12-19 18:53:02 +0000430 raise
431
432 return vlan_id # If we're successful
433
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000434 # Complex call, depends on existing state a lot
435 # 1. Check validity of inputs
436 # 2. Switch mode and other config on the port.
437 # a. If switching trunk->access, remove all trunk VLANs from it
438 # (if needed) and switch back to the base VLAN for the
439 # port. Next, switch to access mode.
440 # b. If switching access->trunk, switch back to the base VLAN
441 # for the port. Next, switch mode. Then add all trunk VLANs
442 # to it (if needed)
443 # 3. If all went OK, save config on the switch
444 # 4. Change details of the port in the DB
445 #
446 # If things fail, we attempt to roll back by rebooting the switch
Steve McIntyre4b4ab652014-12-22 17:19:09 +0000447 def set_port_mode(self, state, port_id, mode):
Steve McIntyrefeb64522014-12-19 18:53:02 +0000448
Steve McIntyre7cf80982015-02-12 07:03:40 +0000449 logging.debug('set_port_mode')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000450 db = state.db
451 config = state.config
452
453 # 1. Sanity-check inputs
Steve McIntyre5b0de002015-01-23 18:05:13 +0000454 if mode != 'access' and mode != 'trunk':
455 raise InputError("Port mode '%s' is not a valid option: try 'access' or 'trunk'" % mode)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000456 port = db.get_port_by_id(port_id)
457 if port is None:
458 raise InputError("Port ID %d does not exist" % port_id)
Steve McIntyre28114092015-02-13 03:04:40 +0000459 if port.is_locked:
460 raise InputError("Port ID %d is locked" % port_id)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000461 if mode == 'trunk' and port.is_trunk:
Steve McIntyre3aff7da2015-02-13 04:00:11 +0000462 raise InputError("Port ID %d is already in trunk mode" % port_id)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000463 if mode == 'access' and not port.is_trunk:
Steve McIntyre3aff7da2015-02-13 04:00:11 +0000464 raise InputError("Port ID %d is already in access mode" % port_id)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000465 base_vlan_tag = db.get_vlan_tag_by_id(port.base_vlan_id)
466
467 # Get the right driver
468 switch_name = db.get_switch_name_by_id(port.switch_id)
469 s = self.get_switch_driver(switch_name, config)
470
471 # 2. Now start configuring the switch
472 try:
473 s.switch_connect(config.switches[switch_name].username,
474 config.switches[switch_name].password,
Steve McIntyre3b655af2014-12-23 13:43:19 +0000475 config.switches[switch_name].enable_password)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000476 except:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100477 logging.debug('Failed to talk to switch %s!', switch_name)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000478 raise
479
480 try:
481 if port.is_trunk:
482 # 2a. We're going from a trunk port to an access port
483 if 'TrunkWildCardVlans' in s.switch_get_capabilities():
Steve McIntyre7cf80982015-02-12 07:03:40 +0000484 logging.debug('This switch does not need special trunk port handling')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000485 else:
Steve McIntyre7cf80982015-02-12 07:03:40 +0000486 logging.debug('This switch needs special trunk port handling')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000487 vlans = s.port_get_trunk_vlan_list(port.name)
488 if vlans is None:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100489 logging.debug("But it has no VLANs defined on port %s", port.name)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000490 vlans = []
491 else:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100492 logging.debug('Found %d vlans that may need dropping on port %s', len(vlans), port.name)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000493
494 for vlan in vlans:
Steve McIntyre97f5e872015-01-23 18:07:05 +0000495 if vlan != state.config.vland.default_vlan_tag:
496 s.port_remove_trunk_from_vlan(port.name, vlan)
Steve McIntyref903b692015-07-09 18:29:05 +0100497
498 s.port_set_mode(port.name, "access")
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000499
500 else:
501 # 2b. We're going from an access port to a trunk port
502 s.port_set_access_vlan(port.name, base_vlan_tag)
503 s.port_set_mode(port.name, "trunk")
504 if 'TrunkWildCardVlans' in s.switch_get_capabilities():
Steve McIntyre7cf80982015-02-12 07:03:40 +0000505 logging.debug('This switch does not need special trunk port handling')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000506 else:
507 vlans = db.all_vlans()
508 for vlan in vlans:
Steve McIntyreee742d92015-09-03 18:16:09 +0100509 if vlan.tag != state.config.vland.default_vlan_tag:
Steve McIntyre36ae6ac2015-09-03 18:10:33 +0100510 s.port_add_trunk_to_vlan(port.name, vlan.tag)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000511
512 except IOError:
Steve McIntyre38a24e52015-07-21 17:54:58 +0100513 logging.error('set_port_mode failed, resetting switch to recover')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000514 # Bugger. Looks like one of the switch calls above
515 # failed. To undo the changes safely, we'll need to reset
516 # all the config on this switch
517 s.switch_restart() # Will implicitly also close the connection
518 del s
519 raise
520
521 # 3. All seems to have worked so far!
522 s.switch_save_running_config()
523 s.switch_disconnect()
524 del s
525
526 # 4. And update the DB
527 db.set_port_mode(port_id, mode)
528
529 return port_id # If we're successful
530
531 # Complex call, updating both DB and switch state
532 # 1. Check validity of inputs
533 # 2. Update the port config on the switch
534 # 3. If all went OK, save config on the switch
535 # 4. Change details of the port in the DB
536 #
537 # If things fail, we attempt to roll back by rebooting the switch
538 def set_current_vlan(self, state, port_id, vlan_id):
539
Steve McIntyre7cf80982015-02-12 07:03:40 +0000540 logging.debug('set_current_vlan')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000541 db = state.db
542 config = state.config
543
544 # 1. Sanity checks!
545 port = db.get_port_by_id(port_id)
546 if port is None:
547 raise InputError("Port ID %d does not exist" % port_id)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000548 if port.is_locked:
549 raise InputError("Port ID %d is locked" % port_id)
Steve McIntyre3bb7bbd2015-02-13 03:06:01 +0000550 if port.is_trunk:
551 raise InputError("Port ID %d is not an access port" % port_id)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000552
553 vlan = db.get_vlan_by_id(vlan_id)
554 if vlan is None:
555 raise InputError("VLAN ID %d does not exist" % vlan_id)
556
557 # Get the right driver
558 switch_name = db.get_switch_name_by_id(port.switch_id)
559 s = self.get_switch_driver(switch_name, config)
560
561 # 2. Now start configuring the switch
562 try:
563 s.switch_connect(config.switches[switch_name].username,
564 config.switches[switch_name].password,
Steve McIntyre3b655af2014-12-23 13:43:19 +0000565 config.switches[switch_name].enable_password)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000566 except:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100567 logging.debug('Failed to talk to switch %s!', switch_name)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000568 raise
569
570 try:
571 s.port_set_access_vlan(port.name, vlan.tag)
572 except IOError:
Steve McIntyre38a24e52015-07-21 17:54:58 +0100573 logging.error('set_current_vlan failed, resetting switch to recover')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000574 # Bugger. Looks like one of the switch calls above
575 # failed. To undo the changes safely, we'll need to reset
576 # all the config on this switch
577 s.switch_restart() # Will implicitly also close the connection
578 del s
579 raise
580
581 # 3. All seems to have worked so far!
582 s.switch_save_running_config()
583 s.switch_disconnect()
584 del s
585
586 # 4. And update the DB
587 db.set_current_vlan(port_id, vlan_id)
588
589 return port_id # If we're successful
590
591 # Complex call, updating both DB and switch state
592 # 1. Check validity of input
593 # 2. Update the port config on the switch
594 # 3. If all went OK, save config on the switch
595 # 4. Change details of the port in the DB
596 #
597 # If things fail, we attempt to roll back by rebooting the switch
598 def restore_base_vlan(self, state, port_id):
599
Steve McIntyre7cf80982015-02-12 07:03:40 +0000600 logging.debug('restore_base_vlan')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000601 db = state.db
602 config = state.config
603
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000604 # 1. Sanity checks!
605 port = db.get_port_by_id(port_id)
606 if port is None:
607 raise InputError("Port ID %d does not exist" % port_id)
608 if port.is_trunk:
609 raise InputError("Port ID %d is not an access port" % port_id)
610 if port.is_locked:
611 raise InputError("Port ID %d is locked" % port_id)
612
613 # Bail out early if we're *already* on the base VLAN. This is
614 # not an error
615 if port.current_vlan_id == port.base_vlan_id:
616 return port_id
617
618 vlan = db.get_vlan_by_id(port.base_vlan_id)
619
620 # Get the right driver
621 switch_name = db.get_switch_name_by_id(port.switch_id)
622 s = self.get_switch_driver(switch_name, config)
623
624 # 2. Now start configuring the switch
625 try:
626 s.switch_connect(config.switches[switch_name].username,
627 config.switches[switch_name].password,
Steve McIntyre3b655af2014-12-23 13:43:19 +0000628 config.switches[switch_name].enable_password)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000629 except:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100630 logging.debug('Failed to talk to switch %s!', switch_name)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000631 raise
632
633 try:
634 s.port_set_access_vlan(port.name, vlan.tag)
635 except IOError:
Steve McIntyre38a24e52015-07-21 17:54:58 +0100636 logging.error('restore_base_vlan failed, resetting switch to recover')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000637 # Bugger. Looks like one of the switch calls above
638 # failed. To undo the changes safely, we'll need to reset
639 # all the config on this switch
640 s.switch_restart() # Will implicitly also close the connection
641 del s
642 raise
643
644 # 3. All seems to have worked so far!
645 s.switch_save_running_config()
646 s.switch_disconnect()
647 del s
648
649 # 4. And update the DB
650 db.set_current_vlan(port_id, port.base_vlan_id)
651
652 return port_id # If we're successful
653
654 # Complex call, updating both DB and switch state
655 # * Check validity of input
656 # * Read all the config from the switch (switch, ports, VLANs)
657 # * Create initial DB entries to match each of those
658 # * Merge VLANs across all switches
659 # * Set up ports appropriately
660 #
661 def auto_import_switch(self, state, switch_name):
662
Steve McIntyre7cf80982015-02-12 07:03:40 +0000663 logging.debug('auto_import_switch')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000664 db = state.db
665 config = state.config
666
Steve McIntyrea8fe1de2015-02-11 17:13:12 +0000667 port_vlans = {}
668
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000669 # 1. Sanity checks!
670 switch_id = db.get_switch_id_by_name(switch_name)
671 if switch_id is not None:
672 raise InputError("Switch name %s already exists in the DB (ID %d)" % (switch_name, switch_id))
673
Steve McIntyre4b4ab652014-12-22 17:19:09 +0000674 if not switch_name in config.switches:
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000675 raise InputError("Switch name %s not defined in config" % switch_name)
676
677 # 2. Now start reading config from the switch
678 try:
679 s = self.get_switch_driver(switch_name, config)
680 s.switch_connect(config.switches[switch_name].username,
681 config.switches[switch_name].password,
Steve McIntyre3b655af2014-12-23 13:43:19 +0000682 config.switches[switch_name].enable_password)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000683 except:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100684 logging.debug('Failed to talk to switch %s!', switch_name)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000685 raise
686
687 # DON'T create the switch record in the DB first - we'll want
Steve McIntyrefc511242014-12-23 22:28:30 +0000688 # to create VLANs on *other* switches, and it's easier to do
689 # that before we've added our new switch
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000690
691 new_vlan_tags = []
692
693 # Grab the VLANs defined on this switch
694 vlan_tags = s.vlan_get_list()
Steve McIntyrefc511242014-12-23 22:28:30 +0000695
Steve McIntyre5fa22652015-04-01 18:01:45 +0100696 logging.debug(' found %d vlans on the switch', len(vlan_tags))
Steve McIntyrefc511242014-12-23 22:28:30 +0000697
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000698 for vlan_tag in vlan_tags:
699 vlan_name = s.vlan_get_name(vlan_tag)
700
701 # If a VLAN is already in the database, then that's easy -
702 # we can just ignore it. However, we have to check that
703 # there is not a different name for the existing VLAN tag
Steve McIntyreb1529072014-12-23 17:17:22 +0000704 # - bail out if so... UNLESS we're looking at the default
705 # VLAN
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000706 #
707 # If this VLAN tag is not already in the DB, we'll need to
708 # add it there and to all the other switches (and their
709 # trunk ports!) too.
Steve McIntyrefc511242014-12-23 22:28:30 +0000710 vlan_id = db.get_vlan_id_by_tag(vlan_tag)
Steve McIntyre6c09c0c2015-07-17 17:20:01 +0100711 if vlan_id != state.default_vlan_id:
Steve McIntyreb1529072014-12-23 17:17:22 +0000712 if vlan_id is not None:
713 vlan_db_name = db.get_vlan_name_by_id(vlan_id)
714 if vlan_name != vlan_db_name:
715 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))
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000716
Steve McIntyreb1529072014-12-23 17:17:22 +0000717 else:
718 # OK, we'll need to set up the new VLAN now. It can't
719 # be a base VLAN - switches don't have such a concept!
720 # Rather than create individually here, add to a
721 # list. *Only* once we've worked through all the
722 # switch's VLANs successfully (checking for existing
723 # records and possible clashes!) should we start
724 # committing changes
725 new_vlan_tags.append(vlan_tag)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000726
727 # Now create the VLAN DB entries
728 for vlan_tag in new_vlan_tags:
729 vlan_name = s.vlan_get_name(vlan_tag)
730 vlan_id = self.create_vlan(state, vlan_name, vlan_tag, False)
731
732 # *Now* add this switch itself to the database, after we've
733 # worked on all the other switches
734 switch_id = db.create_switch(switch_name)
735
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000736 # And now the ports
737 trunk_ports = []
738 ports = s.switch_get_port_names()
Steve McIntyre5fa22652015-04-01 18:01:45 +0100739 logging.debug(' found %d ports on the switch', len(ports))
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000740 for port_name in ports:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100741 logging.debug(' trying to import port %s', port_name)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000742 port_id = None
743 port_mode = s.port_get_mode(port_name)
Steve McIntyreea753972015-08-05 13:52:48 +0100744 port_number = s.port_map_name_to_number(port_name)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000745 if port_mode == 'access':
746 # Access ports are easy - just create the port, and
747 # set both the current and base VLANs to the current
748 # VLAN on the switch. We'll end up changing this after
749 # import if needed.
Steve McIntyrea8fe1de2015-02-11 17:13:12 +0000750 port_vlans[port_name] = (s.port_get_access_vlan(port_name),)
751 port_vlan_id = db.get_vlan_id_by_tag(port_vlans[port_name][0])
Steve McIntyreea753972015-08-05 13:52:48 +0100752 port_id = db.create_port(switch_id, port_name, port_number,
Steve McIntyre6f17b102014-12-24 02:18:08 +0000753 port_vlan_id, port_vlan_id)
Steve McIntyre5fa22652015-04-01 18:01:45 +0100754 logging.debug(' access port, VLAN %d', int(port_vlans[port_name][0]))
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000755 # Nothing further needed
756 elif port_mode == 'trunk':
Steve McIntyre7cf80982015-02-12 07:03:40 +0000757 logging.debug(' trunk port, VLANs:')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000758 # Trunk ports are a little more involved. First,
759 # create the port in the DB, setting the VLANs to the
760 # first VLAN found on the trunk port. This will *also*
761 # be in access mode by default, and unlocked.
Steve McIntyrea8fe1de2015-02-11 17:13:12 +0000762 port_vlans[port_name] = s.port_get_trunk_vlan_list(port_name)
Steve McIntyre7cf80982015-02-12 07:03:40 +0000763 logging.debug(port_vlans[port_name])
Steve McIntyrea8fe1de2015-02-11 17:13:12 +0000764 if port_vlans[port_name] == [] or port_vlans[port_name] is None or 'ALL' in port_vlans[port_name]:
765 port_vlans[port_name] = (state.config.vland.default_vlan_tag,)
766 port_vlan_id = db.get_vlan_id_by_tag(port_vlans[port_name][0])
Steve McIntyreea753972015-08-05 13:52:48 +0100767 port_id = db.create_port(switch_id, port_name, port_number,
Steve McIntyre6f17b102014-12-24 02:18:08 +0000768 port_vlan_id, port_vlan_id)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000769 # Append to a list of trunk ports that we will need to
770 # modify once we're done
771 trunk_ports.append(port_id)
Steve McIntyrefc511242014-12-23 22:28:30 +0000772 else:
Steve McIntyre857d89f2015-07-24 16:22:37 +0100773 # We've found a port mode we don't want, e.g. the
774 # "dynamic auto" on a Cisco Catalyst. Handle that here
775 # - tell the switch to set that port to access and
776 # handle accordingly.
777 s.port_set_mode(port_name, 'access')
778 port_vlans[port_name] = (s.port_get_access_vlan(port_name),)
779 port_vlan_id = db.get_vlan_id_by_tag(port_vlans[port_name][0])
Steve McIntyreea753972015-08-05 13:52:48 +0100780 port_id = db.create_port(switch_id, port_name, port_number,
Steve McIntyre857d89f2015-07-24 16:22:37 +0100781 port_vlan_id, port_vlan_id)
782 logging.debug(' Found port in %s mode', port_mode)
783 logging.debug(' Forcing to access mode, VLAN %d', int(port_vlans[port_name][0]))
784 port_mode = "access"
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000785
Steve McIntyre5fa22652015-04-01 18:01:45 +0100786 logging.debug(" Added port %s, got port ID %d", port_name, port_id)
Steve McIntyrefc511242014-12-23 22:28:30 +0000787
Steve McIntyre574e3342015-01-23 18:08:33 +0000788 db.set_port_mode(port_id, port_mode)
789
Steve McIntyre6feea572015-02-09 15:49:20 +0000790 # Make sure this switch has all the VLANs we need
791 for vlan in db.all_vlans():
Steve McIntyre49d3c082015-07-24 16:17:23 +0100792 if vlan.tag != state.config.vland.default_vlan_tag:
Steve McIntyred7fb4072015-02-11 17:13:50 +0000793 if not vlan.tag in vlan_tags:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100794 logging.debug("Adding VLAN tag %d to this switch", vlan.tag)
Steve McIntyre6feea572015-02-09 15:49:20 +0000795 s.vlan_create(vlan.tag)
796 s.vlan_set_name(vlan.tag, vlan.name)
797
Steve McIntyrefc511242014-12-23 22:28:30 +0000798 # Now, on each trunk port on the switch, we need to add all
799 # the VLANs already configured across our system
800 if not 'TrunkWildCardVlans' in s.switch_get_capabilities():
801 for port_id in trunk_ports:
802 port = db.get_port_by_id(port_id)
Steve McIntyreb826fc72015-07-27 17:57:40 +0100803
Steve McIntyrefc511242014-12-23 22:28:30 +0000804 for vlan in db.all_vlans():
Steve McIntyre49d3c082015-07-24 16:17:23 +0100805 if vlan.vlan_id != state.default_vlan_id:
Steve McIntyrea8fe1de2015-02-11 17:13:12 +0000806 if not vlan.tag in port_vlans[port.name]:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100807 logging.debug("Adding allowed VLAN tag %d to trunk port %s", vlan.tag, port.name)
Steve McIntyrea8fe1de2015-02-11 17:13:12 +0000808 s.port_add_trunk_to_vlan(port.name, vlan.tag)
Steve McIntyrefc511242014-12-23 22:28:30 +0000809
Steve McIntyrefc511242014-12-23 22:28:30 +0000810 # Done with this switch \o/
811 s.switch_save_running_config()
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000812 s.switch_disconnect()
813 del s
814
Steve McIntyre4b4ab652014-12-22 17:19:09 +0000815 ret = {}
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000816 ret['switch_id'] = switch_id
817 ret['num_ports_added'] = len(ports)
818 ret['num_vlans_added'] = len(new_vlan_tags)
819 return ret # If we're successful