blob: e71e907e5a48ef51bd88ddf40e585636176ea3fa [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 McIntyre26de4d92015-09-23 00:04:55 +01008 def set_logging_level(self, level):
9
10 # Set up logging and maybe go quiet...
11 if level is None:
12 level = "CRITICAL"
13
14 loglevel = logging.CRITICAL
15 if level == "ERROR":
16 loglevel = logging.ERROR
17 elif level == "WARNING":
18 loglevel = logging.WARNING
19 elif level == "INFO":
20 loglevel = logging.INFO
21 elif level == "DEBUG":
22 loglevel = logging.DEBUG
23 return loglevel
24
Steve McIntyre5f6f85e2014-12-22 16:42:28 +000025 def get_switch_driver(self, switch_name, config):
Steve McIntyre5fa22652015-04-01 18:01:45 +010026 logging.debug("Trying to find a driver for %s", switch_name)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +000027 driver = config.switches[switch_name].driver
Steve McIntyre5fa22652015-04-01 18:01:45 +010028 logging.debug("Driver: %s", driver)
Steve McIntyref1c04f92014-12-16 18:23:15 +000029 module = __import__("drivers.%s" % driver, fromlist=[driver])
30 class_ = getattr(module, driver)
Steve McIntyre0f38f0e2015-07-14 15:26:05 +010031 return class_(switch_name, debug = config.switches[switch_name].debug)
Steve McIntyref1c04f92014-12-16 18:23:15 +000032
Steve McIntyre519158e2014-12-23 13:44:44 +000033 def probe_switches(self, state):
34 config = state.config
Steve McIntyree8d80582014-12-23 16:53:39 +000035 ret = {}
Steve McIntyre5f6f85e2014-12-22 16:42:28 +000036 for switch_name in sorted(config.switches):
Steve McIntyre5fa22652015-04-01 18:01:45 +010037 logging.debug("Found switch %s:", switch_name)
Steve McIntyre7cf80982015-02-12 07:03:40 +000038 logging.debug(" Probing...")
Steve McIntyre519158e2014-12-23 13:44:44 +000039
Steve McIntyre4b4ab652014-12-22 17:19:09 +000040 s = self.get_switch_driver(switch_name, config)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +000041 s.switch_connect(config.switches[switch_name].username,
42 config.switches[switch_name].password,
Steve McIntyre3b655af2014-12-23 13:43:19 +000043 config.switches[switch_name].enable_password)
Steve McIntyre27d4b582014-12-23 22:51:00 +000044 ret[switch_name] = 'Found %d ports: ' % len(s.switch_get_port_names())
45 for name in s.switch_get_port_names():
46 ret[switch_name] += '%s ' % name
Steve McIntyrea2a8f792014-12-17 17:34:32 +000047 s.switch_disconnect()
Steve McIntyre2ed90f52015-07-21 17:59:52 +010048 del s
Steve McIntyrea2020cb2014-12-23 16:56:40 +000049 return ret
Steve McIntyrec68a18e2014-12-17 16:29:28 +000050
Steve McIntyre091e2ac2014-12-16 19:20:07 +000051 # Simple helper wrapper for all the read-only database queries
Steve McIntyre2150bc22014-12-17 13:13:56 +000052 def perform_db_query(self, state, command, data):
Steve McIntyre7cf80982015-02-12 07:03:40 +000053 logging.debug('perform_db_query')
54 logging.debug(command)
55 logging.debug(data)
Steve McIntyref1c04f92014-12-16 18:23:15 +000056 ret = {}
Steve McIntyre2150bc22014-12-17 13:13:56 +000057 db = state.db
Steve McIntyref1c04f92014-12-16 18:23:15 +000058 try:
Steve McIntyre091e2ac2014-12-16 19:20:07 +000059 if command == 'db.all_switches':
Steve McIntyref1c04f92014-12-16 18:23:15 +000060 ret = db.all_switches()
61 elif command == 'db.all_ports':
62 ret = db.all_ports()
63 elif command == 'db.all_vlans':
64 ret = db.all_vlans()
Steve McIntyrec4890132015-08-07 15:19:11 +010065 elif command == 'db.all_trunks':
66 ret = db.all_trunks()
Steve McIntyref1c04f92014-12-16 18:23:15 +000067 elif command == 'db.get_switch_by_id':
68 ret = db.get_switch_by_id(data['switch_id'])
69 elif command == 'db.get_switch_id_by_name':
70 ret = db.get_switch_id_by_name(data['name'])
71 elif command == 'db.get_switch_name_by_id':
72 ret = db.get_switch_name_by_id(data['switch_id'])
73 elif command == 'db.get_port_by_id':
74 ret = db.get_port_by_id(data['port_id'])
75 elif command == 'db.get_ports_by_switch':
76 ret = db.get_ports_by_switch(data['switch_id'])
77 elif command == 'db.get_port_by_switch_and_name':
78 ret = db.get_port_by_switch_and_name(data['switch_id'], data['name'])
Steve McIntyre45f55012015-08-05 13:55:15 +010079 elif command == 'db.get_port_by_switch_and_number':
80 ret = db.get_port_by_switch_and_number(data['switch_id'], int(data['number']))
Steve McIntyref1c04f92014-12-16 18:23:15 +000081 elif command == 'db.get_current_vlan_id_by_port':
82 ret = db.get_current_vlan_id_by_port(data['port_id'])
83 elif command == 'db.get_base_vlan_id_by_port':
84 ret = db.get_base_vlan_id_by_port(data['port_id'])
85 elif command == 'db.get_ports_by_current_vlan':
86 ret = db.get_ports_by_current_vlan(data['vlan_id'])
87 elif command == 'db.get_ports_by_base_vlan':
88 ret = db.get_ports_by_base_vlan(data['vlan_id'])
Steve McIntyrec4890132015-08-07 15:19:11 +010089 elif command == 'db.get_ports_by_trunk':
90 ret = db.get_ports_by_trunk(data['trunk_id'])
Steve McIntyref1c04f92014-12-16 18:23:15 +000091 elif command == 'db.get_vlan_by_id':
92 ret = db.get_vlan_by_id(data['vlan_id'])
Steve McIntyre65533d72015-01-23 18:01:17 +000093 elif command == 'db.get_vlan_tag_by_id':
94 ret = db.get_vlan_tag_by_id(data['vlan_id'])
Steve McIntyref1c04f92014-12-16 18:23:15 +000095 elif command == 'db.get_vlan_id_by_name':
96 ret = db.get_vlan_id_by_name(data['name'])
97 elif command == 'db.get_vlan_id_by_tag':
Steve McIntyre07946c22014-12-17 13:14:15 +000098 ret = db.get_vlan_id_by_tag(data['tag'])
Steve McIntyref1c04f92014-12-16 18:23:15 +000099 elif command == 'db.get_vlan_name_by_id':
100 ret = db.get_vlan_name_by_id(data['vlan_id'])
Steve McIntyrec4890132015-08-07 15:19:11 +0100101 elif command == 'db.get_trunk_by_id':
102 ret = db.get_trunk_by_id(data['trunk_id'])
Steve McIntyref1c04f92014-12-16 18:23:15 +0000103 else:
Steve McIntyree749fef2014-12-17 16:35:45 +0000104 raise InputError("Unknown db_query command \"%s\"" % command)
Steve McIntyref1c04f92014-12-16 18:23:15 +0000105
Steve McIntyre978cb9f2015-10-09 16:51:28 +0100106 except InputError as e:
107 logging.error('perform_db_query(%s) got error %s', command, e)
Steve McIntyre5da37fa2014-12-17 13:14:44 +0000108 raise
Steve McIntyre978cb9f2015-10-09 16:51:28 +0100109 except ValueError as e:
110 logging.error('perform_db_query(%s) got error %s', command, e)
111 raise InputError("Invalid value in API call argument: %s" % e)
Steve McIntyref1c04f92014-12-16 18:23:15 +0000112
113 return ret
114
Steve McIntyre53c7ad92014-12-16 19:21:13 +0000115 # Simple helper wrapper for all the read-only daemon state queries
116 def perform_daemon_query(self, state, command, data):
Steve McIntyre7cf80982015-02-12 07:03:40 +0000117 logging.debug('perform_daemon_query')
118 logging.debug(command)
119 logging.debug(data)
Steve McIntyre53c7ad92014-12-16 19:21:13 +0000120 ret = {}
121 try:
122 if command == 'daemon.status':
123 # data ignored
124 ret['running'] = 'ok'
125 elif command == 'daemon.version':
126 # data ignored
127 ret['version'] = state.version
128 elif command == 'daemon.statistics':
129 ret['uptime'] = time.time() - state.starttime
Steve McIntyre88b79df2014-12-23 13:45:08 +0000130 elif command == 'daemon.probe_switches':
131 ret = self.probe_switches(state)
Steve McIntyre06fe6422015-01-23 17:55:43 +0000132 elif command == 'daemon.shutdown':
133 # data ignored
134 ret['shutdown'] = 'Shutting down'
135 state.running = False
Steve McIntyre53c7ad92014-12-16 19:21:13 +0000136 else:
Steve McIntyree749fef2014-12-17 16:35:45 +0000137 raise InputError("Unknown daemon_query command \"%s\"" % command)
Steve McIntyre53c7ad92014-12-16 19:21:13 +0000138
Steve McIntyre978cb9f2015-10-09 16:51:28 +0100139 except InputError as e:
140 logging.error('perform_daemon_query(%s) got error %s', command, e)
Steve McIntyrea590b5b2014-12-17 13:15:14 +0000141 raise
Steve McIntyre978cb9f2015-10-09 16:51:28 +0100142 except ValueError as e:
143 logging.error('perform_daemon_query(%s) got error %s', command, e)
144 raise InputError("Invalid value in API call: %s" % e)
Steve McIntyrea590b5b2014-12-17 13:15:14 +0000145
146 return ret
147
Steve McIntyree749fef2014-12-17 16:35:45 +0000148 # Helper wrapper for API functions modifying database state only
Steve McIntyrea590b5b2014-12-17 13:15:14 +0000149 def perform_db_update(self, state, command, data):
Steve McIntyre7cf80982015-02-12 07:03:40 +0000150 logging.debug('perform_db_update')
151 logging.debug(command)
152 logging.debug(data)
Steve McIntyrea590b5b2014-12-17 13:15:14 +0000153 ret = {}
154 db = state.db
155 try:
156 if command == 'db.create_switch':
157 ret = db.create_switch(data['name'])
158 elif command == 'db.create_port':
Steve McIntyreca6adfc2015-08-06 15:08:58 +0100159 try:
160 number = int(data['number'])
161 except ValueError:
162 raise InputError("Invalid value for port number (%s) - must be numeric only!" % data['number'])
Steve McIntyrea590b5b2014-12-17 13:15:14 +0000163 ret = db.create_port(data['switch_id'], data['name'],
Steve McIntyreca6adfc2015-08-06 15:08:58 +0100164 number,
Steve McIntyrefefdbb42014-12-22 16:14:28 +0000165 state.default_vlan_id,
166 state.default_vlan_id)
Steve McIntyrec4890132015-08-07 15:19:11 +0100167 if command == 'db.create_trunk':
168 ret = db.create_trunk(data['port_id1'], data['port_id2'])
Steve McIntyrea590b5b2014-12-17 13:15:14 +0000169 elif command == 'db.delete_switch':
170 ret = db.delete_switch(data['switch_id'])
171 elif command == 'db.delete_port':
172 ret = db.delete_port(data['port_id'])
173 elif command == 'db.set_port_is_locked':
174 ret = db.set_port_is_locked(data['port_id'], data['is_locked'])
175 elif command == 'db.set_base_vlan':
176 ret = db.set_base_vlan(data['port_id'], data['base_vlan_id'])
Steve McIntyrec4890132015-08-07 15:19:11 +0100177 elif command == 'db.delete_trunk':
178 ret = db.delete_trunk(data['trunk_id'])
Steve McIntyrea590b5b2014-12-17 13:15:14 +0000179 else:
Steve McIntyre0f9dea62014-12-17 16:37:01 +0000180 raise InputError("Unknown db_update command \"%s\"" % command)
181
Steve McIntyre978cb9f2015-10-09 16:51:28 +0100182 except InputError as e:
183 logging.error('perform_db_update(%s) got error %s', command, e)
Steve McIntyre0f9dea62014-12-17 16:37:01 +0000184 raise
Steve McIntyre978cb9f2015-10-09 16:51:28 +0100185 except ValueError as e:
186 logging.error('perform_db_update(%s) got error %s', command, e)
187 raise InputError("Invalid value in API call: %s" % e)
Steve McIntyre0f9dea62014-12-17 16:37:01 +0000188
189 return ret
190
191 # Helper wrapper for API functions that modify both database state
192 # and on-switch VLAN state
193 def perform_vlan_update(self, state, command, data):
Steve McIntyre7cf80982015-02-12 07:03:40 +0000194 logging.debug('perform_vlan_update')
195 logging.debug(command)
196 logging.debug(data)
Steve McIntyre0f9dea62014-12-17 16:37:01 +0000197 ret = {}
Steve McIntyre4b4ab652014-12-22 17:19:09 +0000198
Steve McIntyre0f9dea62014-12-17 16:37:01 +0000199 try:
200 # All of these are complex commands, so call helpers
201 # rather than inline the code here
202 if command == 'api.create_vlan':
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000203 ret = self.create_vlan(state, data['name'], int(data['tag']), data['is_base_vlan'])
Steve McIntyre0f9dea62014-12-17 16:37:01 +0000204 elif command == 'api.delete_vlan':
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000205 ret = self.delete_vlan(state, int(data['vlan_id']))
Steve McIntyre0f9dea62014-12-17 16:37:01 +0000206 elif command == 'api.set_port_mode':
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000207 ret = self.set_port_mode(state, int(data['port_id']), data['mode'])
Steve McIntyre0f9dea62014-12-17 16:37:01 +0000208 elif command == 'api.set_current_vlan':
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000209 ret = self.set_current_vlan(state, int(data['port_id']), int(data['vlan_id']))
Steve McIntyre0f9dea62014-12-17 16:37:01 +0000210 elif command == 'api.restore_base_vlan':
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000211 ret = self.restore_base_vlan(state, int(data['port_id']))
212 elif command == 'api.auto_import_switch':
213 ret = self.auto_import_switch(state, data['switch'])
Steve McIntyre0f9dea62014-12-17 16:37:01 +0000214 else:
Steve McIntyrea590b5b2014-12-17 13:15:14 +0000215 raise InputError("Unknown query command \"%s\"" % command)
216
Steve McIntyre3256b182014-12-19 15:38:15 +0000217 except InputError as e:
Steve McIntyre978cb9f2015-10-09 16:51:28 +0100218 logging.error('perform_vlan_update(%s) got error %s', command, e)
Steve McIntyrea590b5b2014-12-17 13:15:14 +0000219 raise
Steve McIntyre978cb9f2015-10-09 16:51:28 +0100220 except ValueError as e:
221 logging.error('perform_vlan_update(%s) got error %s', command, e)
222 raise InputError("Invalid value in API call: %s" % e)
Steve McIntyre53c7ad92014-12-16 19:21:13 +0000223
224 return ret
225
226
Steve McIntyre3256b182014-12-19 15:38:15 +0000227 # Complex call
228 # 1. create the VLAN in the DB
229 # 2. Iterate through all switches:
Steve McIntyre1ab8b872014-12-19 18:37:00 +0000230 # a. Create the VLAN
231 # b. Add the VLAN to all trunk ports (if needed)
232 # 3. If all went OK, save config on all the switches
Steve McIntyre3256b182014-12-19 15:38:15 +0000233 #
234 # The VLAN may already exist on some of the switches, that's
Steve McIntyre153157d2014-12-19 18:05:20 +0000235 # fine. If things fail, we attempt to roll back by rebooting
236 # switches then removing the VLAN in the DB.
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000237 def create_vlan(self, state, name, tag, is_base_vlan):
Steve McIntyre3256b182014-12-19 15:38:15 +0000238
Steve McIntyre7cf80982015-02-12 07:03:40 +0000239 logging.debug('create_vlan')
Steve McIntyre3256b182014-12-19 15:38:15 +0000240 db = state.db
241 config = state.config
242
Steve McIntyre6b8d3862015-08-03 19:28:25 +0100243 # Check for tag == -1, i.e. use the next available tag
244 if tag == -1:
245 tag = db.find_lowest_unused_vlan_tag()
246 logging.debug('create_vlan called with a tag of -1, found first unused tag %d', tag)
247
Steve McIntyre3256b182014-12-19 15:38:15 +0000248 # 1. Database record first
249 try:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100250 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 +0000251 vlan_id = db.create_vlan(name, tag, is_base_vlan)
Steve McIntyre5fa22652015-04-01 18:01:45 +0100252 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 +0000253 except InputError:
Steve McIntyre7cf80982015-02-12 07:03:40 +0000254 logging.debug('DB creation failed')
Steve McIntyre3256b182014-12-19 15:38:15 +0000255 raise
256
Steve McIntyre153157d2014-12-19 18:05:20 +0000257 # Keep track of which switches we've configured, for later use
258 switches_done = []
259
Steve McIntyre3256b182014-12-19 15:38:15 +0000260 # 2. Now the switches
261 try:
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000262 for switch in db.all_switches():
Steve McIntyre3256b182014-12-19 15:38:15 +0000263 trunk_ports = []
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000264 switch_name = switch.name
Steve McIntyre3256b182014-12-19 15:38:15 +0000265 try:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100266 logging.debug('Adding new VLAN to switch %s', switch_name)
Steve McIntyre3256b182014-12-19 15:38:15 +0000267 # Get the right driver
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000268 s = self.get_switch_driver(switch_name, config)
269 s.switch_connect(config.switches[switch_name].username,
270 config.switches[switch_name].password,
Steve McIntyre3b655af2014-12-23 13:43:19 +0000271 config.switches[switch_name].enable_password)
Steve McIntyre3256b182014-12-19 15:38:15 +0000272
Steve McIntyre153157d2014-12-19 18:05:20 +0000273 # Mark this switch as one we've touched, for
274 # either config saving or rollback below
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000275 switches_done.append(switch_name)
Steve McIntyre153157d2014-12-19 18:05:20 +0000276
Steve McIntyre3256b182014-12-19 15:38:15 +0000277 # 2a. Create the VLAN on the switch
278 s.vlan_create(tag)
279 s.vlan_set_name(tag, name)
Steve McIntyre5fa22652015-04-01 18:01:45 +0100280 logging.debug('Added VLAN tag %d, name %s to switch %s', tag, name, switch_name)
Steve McIntyre3256b182014-12-19 15:38:15 +0000281
282 # 2b. Do we need to worry about trunk ports on this switch?
283 if 'TrunkWildCardVlans' in s.switch_get_capabilities():
Steve McIntyre7cf80982015-02-12 07:03:40 +0000284 logging.debug('This switch does not need special trunk port handling')
Steve McIntyre3256b182014-12-19 15:38:15 +0000285 else:
Steve McIntyre7cf80982015-02-12 07:03:40 +0000286 logging.debug('This switch needs special trunk port handling')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000287 trunk_ports = db.get_trunk_port_names_by_switch(switch.switch_id)
Steve McIntyre3256b182014-12-19 15:38:15 +0000288 if trunk_ports is None:
Steve McIntyre7cf80982015-02-12 07:03:40 +0000289 logging.debug("But it has no trunk ports defined")
Steve McIntyre3256b182014-12-19 15:38:15 +0000290 trunk_ports = []
291 else:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100292 logging.debug('Found %d trunk_ports that need adjusting', len(trunk_ports))
Steve McIntyre3256b182014-12-19 15:38:15 +0000293
294 # Modify any trunk ports as needed
295 for port in trunk_ports:
Steve McIntyre1ce09f92015-09-04 14:35:13 +0100296 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 +0000297 s.port_add_trunk_to_vlan(port, tag)
298
Steve McIntyre3256b182014-12-19 15:38:15 +0000299 # And now we're done with this switch
300 s.switch_disconnect()
301 del s
302
Steve McIntyre8f27ae12015-09-04 14:35:38 +0100303 except IOError as e:
Steve McIntyre9f7bac02015-09-18 14:10:34 +0100304 logging.error('Failed to add VLAN %d to switch ID %d (%s): %s', tag, switch.switch_id, switch.name, e)
Steve McIntyre3256b182014-12-19 15:38:15 +0000305 raise
Steve McIntyreb826fc72015-07-27 17:57:40 +0100306
Steve McIntyre3256b182014-12-19 15:38:15 +0000307 except IOError:
Steve McIntyre153157d2014-12-19 18:05:20 +0000308 # Bugger. Looks like one of the switch calls above
309 # failed. To undo the changes safely, we'll need to reset
310 # all the switches we managed to configure. This could
311 # take some time!
Steve McIntyre38a24e52015-07-21 17:54:58 +0100312 logging.error('create_vlan failed, resetting all switches to recover')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000313 for switch_name in switches_done:
314 s = self.get_switch_driver(switch_name, config)
315 s.switch_connect(config.switches[switch_name].username,
316 config.switches[switch_name].password,
Steve McIntyre3b655af2014-12-23 13:43:19 +0000317 config.switches[switch_name].enable_password)
Steve McIntyre153157d2014-12-19 18:05:20 +0000318 s.switch_restart() # Will implicitly also close the connection
319 del s
320
Steve McIntyre3256b182014-12-19 15:38:15 +0000321 # Undo the database change
Steve McIntyre7cf80982015-02-12 07:03:40 +0000322 logging.debug('Switch access failed. Deleting the new VLAN entry in the database')
Steve McIntyre3256b182014-12-19 15:38:15 +0000323 db.delete_vlan(vlan_id)
324 raise
325
Steve McIntyre153157d2014-12-19 18:05:20 +0000326 # If we've got this far, things were successful. Save config
327 # on all the switches so it will persist across reboots
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000328 for switch_name in switches_done:
329 s = self.get_switch_driver(switch_name, config)
330 s.switch_connect(config.switches[switch_name].username,
331 config.switches[switch_name].password,
Steve McIntyre3b655af2014-12-23 13:43:19 +0000332 config.switches[switch_name].enable_password)
Steve McIntyre153157d2014-12-19 18:05:20 +0000333 s.switch_save_running_config()
334 s.switch_disconnect()
335 del s
336
Steve McIntyre6b8d3862015-08-03 19:28:25 +0100337 return (vlan_id, tag) # If we're successful
Steve McIntyre3256b182014-12-19 15:38:15 +0000338
Steve McIntyrefeb64522014-12-19 18:53:02 +0000339 # Complex call
340 # 1. Check in the DB if there are any ports on the VLAN. Bail if so
341 # 2. Iterate through all switches:
342 # a. Remove the VLAN from all trunk ports (if needed)
343 # b. Remove the VLAN
344 # 3. If all went OK, save config on the switches
345 # 4. Remove the VLAN in the DB
346 #
347 # If things fail, we attempt to roll back by rebooting switches.
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000348 def delete_vlan(self, state, vlan_id):
Steve McIntyrefeb64522014-12-19 18:53:02 +0000349
Steve McIntyre7cf80982015-02-12 07:03:40 +0000350 logging.debug('delete_vlan')
Steve McIntyrefeb64522014-12-19 18:53:02 +0000351 db = state.db
352 config = state.config
353
Steve McIntyrefeb64522014-12-19 18:53:02 +0000354 # 1. Check for database records first
Steve McIntyre5fa22652015-04-01 18:01:45 +0100355 logging.debug('Checking for ports using VLAN ID %d', vlan_id)
Steve McIntyrefeb64522014-12-19 18:53:02 +0000356 vlan = db.get_vlan_by_id(vlan_id)
357 if vlan is None:
358 raise InputError("VLAN ID %d does not exist" % vlan_id)
359 vlan_tag = vlan.tag
360 ports = db.get_ports_by_current_vlan(vlan_id)
361 if ports is not None:
362 raise InputError("Cannot delete VLAN ID %d when it still has %d ports" %
363 (vlan_id, len(ports)))
364 ports = db.get_ports_by_base_vlan(vlan_id)
365 if ports is not None:
366 raise InputError("Cannot delete VLAN ID %d when it still has %d ports" %
367 (vlan_id, len(ports)))
368
369 # Keep track of which switches we've configured, for later use
370 switches_done = []
371
372 # 2. Now the switches
373 try:
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000374 for switch in db.all_switches():
375 switch_name = switch.name
Steve McIntyrefeb64522014-12-19 18:53:02 +0000376 trunk_ports = []
377 try:
378 # Get the right driver
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000379 s = self.get_switch_driver(switch_name, config)
380 s.switch_connect(config.switches[switch_name].username,
381 config.switches[switch_name].password,
Steve McIntyre3b655af2014-12-23 13:43:19 +0000382 config.switches[switch_name].enable_password)
Steve McIntyrefeb64522014-12-19 18:53:02 +0000383
384 # Mark this switch as one we've touched, for
385 # either config saving or rollback below
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000386 switches_done.append(switch_name)
Steve McIntyrefeb64522014-12-19 18:53:02 +0000387
388 # 2a. Do we need to worry about trunk ports on this switch?
389 if 'TrunkWildCardVlans' in s.switch_get_capabilities():
Steve McIntyre7cf80982015-02-12 07:03:40 +0000390 logging.debug('This switch does not need special trunk port handling')
Steve McIntyrefeb64522014-12-19 18:53:02 +0000391 else:
Steve McIntyre7cf80982015-02-12 07:03:40 +0000392 logging.debug('This switch needs special trunk port handling')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000393 trunk_ports = db.get_trunk_port_names_by_switch(switch.switch_id)
Steve McIntyrefeb64522014-12-19 18:53:02 +0000394 if trunk_ports is None:
Steve McIntyre7cf80982015-02-12 07:03:40 +0000395 logging.debug("But it has no trunk ports defined")
Steve McIntyrefeb64522014-12-19 18:53:02 +0000396 trunk_ports = []
397 else:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100398 logging.debug('Found %d trunk_ports that need adjusting', len(trunk_ports))
Steve McIntyrefeb64522014-12-19 18:53:02 +0000399
400 # Modify any trunk ports as needed
401 for port in trunk_ports:
Steve McIntyre4b4ab652014-12-22 17:19:09 +0000402 s.port_remove_trunk_from_vlan(port, vlan_tag)
Steve McIntyre5fa22652015-04-01 18:01:45 +0100403 logging.debug('Removed VLAN tag %d from switch %s port %s', vlan_tag, switch_name, port)
Steve McIntyrefeb64522014-12-19 18:53:02 +0000404
405 # 2b. Remove the VLAN from the switch
Steve McIntyre5fa22652015-04-01 18:01:45 +0100406 logging.debug('Removing VLAN tag %d from switch %s', vlan_tag, switch_name)
Steve McIntyrefeb64522014-12-19 18:53:02 +0000407 s.vlan_destroy(vlan_tag)
Steve McIntyre5fa22652015-04-01 18:01:45 +0100408 logging.debug('Removed VLAN tag %d from switch %s', vlan_tag, switch_name)
Steve McIntyrefeb64522014-12-19 18:53:02 +0000409
410 # And now we're done with this switch
411 s.switch_disconnect()
412 del s
413
414 except IOError:
415 raise
416
417 except IOError:
418 # Bugger. Looks like one of the switch calls above
419 # failed. To undo the changes safely, we'll need to reset
420 # all the switches we managed to configure. This could
421 # take some time!
Steve McIntyre38a24e52015-07-21 17:54:58 +0100422 logging.error('delete_vlan failed, resetting all switches to recover')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000423 for switch_name in switches_done:
424 s = self.get_switch_driver(switch_name, config)
425 s.switch_connect(config.switches[switch_name].username,
426 config.switches[switch_name].password,
Steve McIntyre3b655af2014-12-23 13:43:19 +0000427 config.switches[switch_name].enable_password)
Steve McIntyrefeb64522014-12-19 18:53:02 +0000428 s.switch_restart() # Will implicitly also close the connection
429 del s
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000430 raise
Steve McIntyrefeb64522014-12-19 18:53:02 +0000431
432 # 3. If we've got this far, things were successful. Save
433 # config on all the switches so it will persist across reboots
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000434 for switch_name in switches_done:
435 s = self.get_switch_driver(switch_name, config)
436 s.switch_connect(config.switches[switch_name].username,
437 config.switches[switch_name].password,
Steve McIntyre3b655af2014-12-23 13:43:19 +0000438 config.switches[switch_name].enable_password)
Steve McIntyrefeb64522014-12-19 18:53:02 +0000439 s.switch_save_running_config()
440 s.switch_disconnect()
441 del s
442
443 # 4. Finally, remove the VLAN in the DB
444 try:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100445 logging.debug('Removing DB record: VLAN ID %d', vlan_id)
Steve McIntyrefeb64522014-12-19 18:53:02 +0000446 vlan_id = db.delete_vlan(vlan_id)
Steve McIntyre5fa22652015-04-01 18:01:45 +0100447 logging.debug('Removed VLAN ID %d from the database OK', vlan_id)
Steve McIntyrefeb64522014-12-19 18:53:02 +0000448 except InputError:
Steve McIntyre7cf80982015-02-12 07:03:40 +0000449 logging.debug('DB deletion failed')
Steve McIntyrefeb64522014-12-19 18:53:02 +0000450 raise
451
452 return vlan_id # If we're successful
453
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000454 # Complex call, depends on existing state a lot
455 # 1. Check validity of inputs
456 # 2. Switch mode and other config on the port.
457 # a. If switching trunk->access, remove all trunk VLANs from it
458 # (if needed) and switch back to the base VLAN for the
459 # port. Next, switch to access mode.
460 # b. If switching access->trunk, switch back to the base VLAN
461 # for the port. Next, switch mode. Then add all trunk VLANs
462 # to it (if needed)
463 # 3. If all went OK, save config on the switch
464 # 4. Change details of the port in the DB
465 #
466 # If things fail, we attempt to roll back by rebooting the switch
Steve McIntyre4b4ab652014-12-22 17:19:09 +0000467 def set_port_mode(self, state, port_id, mode):
Steve McIntyrefeb64522014-12-19 18:53:02 +0000468
Steve McIntyre7cf80982015-02-12 07:03:40 +0000469 logging.debug('set_port_mode')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000470 db = state.db
471 config = state.config
472
473 # 1. Sanity-check inputs
Steve McIntyre5b0de002015-01-23 18:05:13 +0000474 if mode != 'access' and mode != 'trunk':
475 raise InputError("Port mode '%s' is not a valid option: try 'access' or 'trunk'" % mode)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000476 port = db.get_port_by_id(port_id)
477 if port is None:
478 raise InputError("Port ID %d does not exist" % port_id)
Steve McIntyre28114092015-02-13 03:04:40 +0000479 if port.is_locked:
480 raise InputError("Port ID %d is locked" % port_id)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000481 if mode == 'trunk' and port.is_trunk:
Steve McIntyre3aff7da2015-02-13 04:00:11 +0000482 raise InputError("Port ID %d is already in trunk mode" % port_id)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000483 if mode == 'access' and not port.is_trunk:
Steve McIntyre3aff7da2015-02-13 04:00:11 +0000484 raise InputError("Port ID %d is already in access mode" % port_id)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000485 base_vlan_tag = db.get_vlan_tag_by_id(port.base_vlan_id)
486
487 # Get the right driver
488 switch_name = db.get_switch_name_by_id(port.switch_id)
489 s = self.get_switch_driver(switch_name, config)
490
491 # 2. Now start configuring the switch
492 try:
493 s.switch_connect(config.switches[switch_name].username,
494 config.switches[switch_name].password,
Steve McIntyre3b655af2014-12-23 13:43:19 +0000495 config.switches[switch_name].enable_password)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000496 except:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100497 logging.debug('Failed to talk to switch %s!', switch_name)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000498 raise
499
500 try:
501 if port.is_trunk:
502 # 2a. We're going from a trunk port to an access port
503 if 'TrunkWildCardVlans' in s.switch_get_capabilities():
Steve McIntyre7cf80982015-02-12 07:03:40 +0000504 logging.debug('This switch does not need special trunk port handling')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000505 else:
Steve McIntyre7cf80982015-02-12 07:03:40 +0000506 logging.debug('This switch needs special trunk port handling')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000507 vlans = s.port_get_trunk_vlan_list(port.name)
508 if vlans is None:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100509 logging.debug("But it has no VLANs defined on port %s", port.name)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000510 vlans = []
511 else:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100512 logging.debug('Found %d vlans that may need dropping on port %s', len(vlans), port.name)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000513
514 for vlan in vlans:
Steve McIntyre97f5e872015-01-23 18:07:05 +0000515 if vlan != state.config.vland.default_vlan_tag:
516 s.port_remove_trunk_from_vlan(port.name, vlan)
Steve McIntyref903b692015-07-09 18:29:05 +0100517
518 s.port_set_mode(port.name, "access")
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000519
520 else:
521 # 2b. We're going from an access port to a trunk port
522 s.port_set_access_vlan(port.name, base_vlan_tag)
523 s.port_set_mode(port.name, "trunk")
524 if 'TrunkWildCardVlans' in s.switch_get_capabilities():
Steve McIntyre7cf80982015-02-12 07:03:40 +0000525 logging.debug('This switch does not need special trunk port handling')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000526 else:
527 vlans = db.all_vlans()
528 for vlan in vlans:
Steve McIntyreee742d92015-09-03 18:16:09 +0100529 if vlan.tag != state.config.vland.default_vlan_tag:
Steve McIntyre36ae6ac2015-09-03 18:10:33 +0100530 s.port_add_trunk_to_vlan(port.name, vlan.tag)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000531
532 except IOError:
Steve McIntyre38a24e52015-07-21 17:54:58 +0100533 logging.error('set_port_mode failed, resetting switch to recover')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000534 # Bugger. Looks like one of the switch calls above
535 # failed. To undo the changes safely, we'll need to reset
536 # all the config on this switch
537 s.switch_restart() # Will implicitly also close the connection
538 del s
539 raise
540
541 # 3. All seems to have worked so far!
542 s.switch_save_running_config()
543 s.switch_disconnect()
544 del s
545
546 # 4. And update the DB
547 db.set_port_mode(port_id, mode)
548
549 return port_id # If we're successful
550
551 # Complex call, updating both DB and switch state
552 # 1. Check validity of inputs
553 # 2. Update the port config on the switch
554 # 3. If all went OK, save config on the switch
555 # 4. Change details of the port in the DB
556 #
557 # If things fail, we attempt to roll back by rebooting the switch
558 def set_current_vlan(self, state, port_id, vlan_id):
559
Steve McIntyre7cf80982015-02-12 07:03:40 +0000560 logging.debug('set_current_vlan')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000561 db = state.db
562 config = state.config
563
564 # 1. Sanity checks!
565 port = db.get_port_by_id(port_id)
566 if port is None:
567 raise InputError("Port ID %d does not exist" % port_id)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000568 if port.is_locked:
569 raise InputError("Port ID %d is locked" % port_id)
Steve McIntyre3bb7bbd2015-02-13 03:06:01 +0000570 if port.is_trunk:
571 raise InputError("Port ID %d is not an access port" % port_id)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000572
573 vlan = db.get_vlan_by_id(vlan_id)
574 if vlan is None:
575 raise InputError("VLAN ID %d does not exist" % vlan_id)
576
577 # Get the right driver
578 switch_name = db.get_switch_name_by_id(port.switch_id)
579 s = self.get_switch_driver(switch_name, config)
580
581 # 2. Now start configuring the switch
582 try:
583 s.switch_connect(config.switches[switch_name].username,
584 config.switches[switch_name].password,
Steve McIntyre3b655af2014-12-23 13:43:19 +0000585 config.switches[switch_name].enable_password)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000586 except:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100587 logging.debug('Failed to talk to switch %s!', switch_name)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000588 raise
589
590 try:
591 s.port_set_access_vlan(port.name, vlan.tag)
592 except IOError:
Steve McIntyre38a24e52015-07-21 17:54:58 +0100593 logging.error('set_current_vlan failed, resetting switch to recover')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000594 # Bugger. Looks like one of the switch calls above
595 # failed. To undo the changes safely, we'll need to reset
596 # all the config on this switch
597 s.switch_restart() # Will implicitly also close the connection
598 del s
599 raise
600
601 # 3. All seems to have worked so far!
602 s.switch_save_running_config()
603 s.switch_disconnect()
604 del s
605
606 # 4. And update the DB
607 db.set_current_vlan(port_id, vlan_id)
608
609 return port_id # If we're successful
610
611 # Complex call, updating both DB and switch state
612 # 1. Check validity of input
613 # 2. Update the port config on the switch
614 # 3. If all went OK, save config on the switch
615 # 4. Change details of the port in the DB
616 #
617 # If things fail, we attempt to roll back by rebooting the switch
618 def restore_base_vlan(self, state, port_id):
619
Steve McIntyre7cf80982015-02-12 07:03:40 +0000620 logging.debug('restore_base_vlan')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000621 db = state.db
622 config = state.config
623
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000624 # 1. Sanity checks!
625 port = db.get_port_by_id(port_id)
626 if port is None:
627 raise InputError("Port ID %d does not exist" % port_id)
628 if port.is_trunk:
629 raise InputError("Port ID %d is not an access port" % port_id)
630 if port.is_locked:
631 raise InputError("Port ID %d is locked" % port_id)
632
633 # Bail out early if we're *already* on the base VLAN. This is
634 # not an error
635 if port.current_vlan_id == port.base_vlan_id:
636 return port_id
637
638 vlan = db.get_vlan_by_id(port.base_vlan_id)
639
640 # Get the right driver
641 switch_name = db.get_switch_name_by_id(port.switch_id)
642 s = self.get_switch_driver(switch_name, config)
643
644 # 2. Now start configuring the switch
645 try:
646 s.switch_connect(config.switches[switch_name].username,
647 config.switches[switch_name].password,
Steve McIntyre3b655af2014-12-23 13:43:19 +0000648 config.switches[switch_name].enable_password)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000649 except:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100650 logging.debug('Failed to talk to switch %s!', switch_name)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000651 raise
652
653 try:
654 s.port_set_access_vlan(port.name, vlan.tag)
655 except IOError:
Steve McIntyre38a24e52015-07-21 17:54:58 +0100656 logging.error('restore_base_vlan failed, resetting switch to recover')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000657 # Bugger. Looks like one of the switch calls above
658 # failed. To undo the changes safely, we'll need to reset
659 # all the config on this switch
660 s.switch_restart() # Will implicitly also close the connection
661 del s
662 raise
663
664 # 3. All seems to have worked so far!
665 s.switch_save_running_config()
666 s.switch_disconnect()
667 del s
668
669 # 4. And update the DB
670 db.set_current_vlan(port_id, port.base_vlan_id)
671
672 return port_id # If we're successful
673
674 # Complex call, updating both DB and switch state
675 # * Check validity of input
676 # * Read all the config from the switch (switch, ports, VLANs)
677 # * Create initial DB entries to match each of those
678 # * Merge VLANs across all switches
679 # * Set up ports appropriately
680 #
681 def auto_import_switch(self, state, switch_name):
682
Steve McIntyre7cf80982015-02-12 07:03:40 +0000683 logging.debug('auto_import_switch')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000684 db = state.db
685 config = state.config
686
Steve McIntyrea8fe1de2015-02-11 17:13:12 +0000687 port_vlans = {}
688
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000689 # 1. Sanity checks!
690 switch_id = db.get_switch_id_by_name(switch_name)
691 if switch_id is not None:
692 raise InputError("Switch name %s already exists in the DB (ID %d)" % (switch_name, switch_id))
693
Steve McIntyre4b4ab652014-12-22 17:19:09 +0000694 if not switch_name in config.switches:
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000695 raise InputError("Switch name %s not defined in config" % switch_name)
696
697 # 2. Now start reading config from the switch
698 try:
699 s = self.get_switch_driver(switch_name, config)
700 s.switch_connect(config.switches[switch_name].username,
701 config.switches[switch_name].password,
Steve McIntyre3b655af2014-12-23 13:43:19 +0000702 config.switches[switch_name].enable_password)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000703 except:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100704 logging.debug('Failed to talk to switch %s!', switch_name)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000705 raise
706
707 # DON'T create the switch record in the DB first - we'll want
Steve McIntyrefc511242014-12-23 22:28:30 +0000708 # to create VLANs on *other* switches, and it's easier to do
709 # that before we've added our new switch
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000710
711 new_vlan_tags = []
712
713 # Grab the VLANs defined on this switch
714 vlan_tags = s.vlan_get_list()
Steve McIntyrefc511242014-12-23 22:28:30 +0000715
Steve McIntyre5fa22652015-04-01 18:01:45 +0100716 logging.debug(' found %d vlans on the switch', len(vlan_tags))
Steve McIntyrefc511242014-12-23 22:28:30 +0000717
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000718 for vlan_tag in vlan_tags:
719 vlan_name = s.vlan_get_name(vlan_tag)
720
721 # If a VLAN is already in the database, then that's easy -
722 # we can just ignore it. However, we have to check that
723 # there is not a different name for the existing VLAN tag
Steve McIntyreb1529072014-12-23 17:17:22 +0000724 # - bail out if so... UNLESS we're looking at the default
725 # VLAN
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000726 #
727 # If this VLAN tag is not already in the DB, we'll need to
728 # add it there and to all the other switches (and their
729 # trunk ports!) too.
Steve McIntyrefc511242014-12-23 22:28:30 +0000730 vlan_id = db.get_vlan_id_by_tag(vlan_tag)
Steve McIntyre6c09c0c2015-07-17 17:20:01 +0100731 if vlan_id != state.default_vlan_id:
Steve McIntyreb1529072014-12-23 17:17:22 +0000732 if vlan_id is not None:
733 vlan_db_name = db.get_vlan_name_by_id(vlan_id)
734 if vlan_name != vlan_db_name:
735 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 +0000736
Steve McIntyreb1529072014-12-23 17:17:22 +0000737 else:
738 # OK, we'll need to set up the new VLAN now. It can't
739 # be a base VLAN - switches don't have such a concept!
740 # Rather than create individually here, add to a
741 # list. *Only* once we've worked through all the
742 # switch's VLANs successfully (checking for existing
743 # records and possible clashes!) should we start
744 # committing changes
745 new_vlan_tags.append(vlan_tag)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000746
747 # Now create the VLAN DB entries
748 for vlan_tag in new_vlan_tags:
749 vlan_name = s.vlan_get_name(vlan_tag)
750 vlan_id = self.create_vlan(state, vlan_name, vlan_tag, False)
751
752 # *Now* add this switch itself to the database, after we've
753 # worked on all the other switches
754 switch_id = db.create_switch(switch_name)
755
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000756 # And now the ports
757 trunk_ports = []
758 ports = s.switch_get_port_names()
Steve McIntyre5fa22652015-04-01 18:01:45 +0100759 logging.debug(' found %d ports on the switch', len(ports))
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000760 for port_name in ports:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100761 logging.debug(' trying to import port %s', port_name)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000762 port_id = None
763 port_mode = s.port_get_mode(port_name)
Steve McIntyreea753972015-08-05 13:52:48 +0100764 port_number = s.port_map_name_to_number(port_name)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000765 if port_mode == 'access':
766 # Access ports are easy - just create the port, and
767 # set both the current and base VLANs to the current
768 # VLAN on the switch. We'll end up changing this after
769 # import if needed.
Steve McIntyrea8fe1de2015-02-11 17:13:12 +0000770 port_vlans[port_name] = (s.port_get_access_vlan(port_name),)
771 port_vlan_id = db.get_vlan_id_by_tag(port_vlans[port_name][0])
Steve McIntyreea753972015-08-05 13:52:48 +0100772 port_id = db.create_port(switch_id, port_name, port_number,
Steve McIntyre6f17b102014-12-24 02:18:08 +0000773 port_vlan_id, port_vlan_id)
Steve McIntyre5fa22652015-04-01 18:01:45 +0100774 logging.debug(' access port, VLAN %d', int(port_vlans[port_name][0]))
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000775 # Nothing further needed
776 elif port_mode == 'trunk':
Steve McIntyre7cf80982015-02-12 07:03:40 +0000777 logging.debug(' trunk port, VLANs:')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000778 # Trunk ports are a little more involved. First,
779 # create the port in the DB, setting the VLANs to the
780 # first VLAN found on the trunk port. This will *also*
781 # be in access mode by default, and unlocked.
Steve McIntyrea8fe1de2015-02-11 17:13:12 +0000782 port_vlans[port_name] = s.port_get_trunk_vlan_list(port_name)
Steve McIntyre7cf80982015-02-12 07:03:40 +0000783 logging.debug(port_vlans[port_name])
Steve McIntyrea8fe1de2015-02-11 17:13:12 +0000784 if port_vlans[port_name] == [] or port_vlans[port_name] is None or 'ALL' in port_vlans[port_name]:
785 port_vlans[port_name] = (state.config.vland.default_vlan_tag,)
786 port_vlan_id = db.get_vlan_id_by_tag(port_vlans[port_name][0])
Steve McIntyreea753972015-08-05 13:52:48 +0100787 port_id = db.create_port(switch_id, port_name, port_number,
Steve McIntyre6f17b102014-12-24 02:18:08 +0000788 port_vlan_id, port_vlan_id)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000789 # Append to a list of trunk ports that we will need to
790 # modify once we're done
791 trunk_ports.append(port_id)
Steve McIntyrefc511242014-12-23 22:28:30 +0000792 else:
Steve McIntyre857d89f2015-07-24 16:22:37 +0100793 # We've found a port mode we don't want, e.g. the
794 # "dynamic auto" on a Cisco Catalyst. Handle that here
795 # - tell the switch to set that port to access and
796 # handle accordingly.
797 s.port_set_mode(port_name, 'access')
798 port_vlans[port_name] = (s.port_get_access_vlan(port_name),)
799 port_vlan_id = db.get_vlan_id_by_tag(port_vlans[port_name][0])
Steve McIntyreea753972015-08-05 13:52:48 +0100800 port_id = db.create_port(switch_id, port_name, port_number,
Steve McIntyre857d89f2015-07-24 16:22:37 +0100801 port_vlan_id, port_vlan_id)
802 logging.debug(' Found port in %s mode', port_mode)
803 logging.debug(' Forcing to access mode, VLAN %d', int(port_vlans[port_name][0]))
804 port_mode = "access"
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000805
Steve McIntyre5fa22652015-04-01 18:01:45 +0100806 logging.debug(" Added port %s, got port ID %d", port_name, port_id)
Steve McIntyrefc511242014-12-23 22:28:30 +0000807
Steve McIntyre574e3342015-01-23 18:08:33 +0000808 db.set_port_mode(port_id, port_mode)
809
Steve McIntyre6feea572015-02-09 15:49:20 +0000810 # Make sure this switch has all the VLANs we need
811 for vlan in db.all_vlans():
Steve McIntyre49d3c082015-07-24 16:17:23 +0100812 if vlan.tag != state.config.vland.default_vlan_tag:
Steve McIntyred7fb4072015-02-11 17:13:50 +0000813 if not vlan.tag in vlan_tags:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100814 logging.debug("Adding VLAN tag %d to this switch", vlan.tag)
Steve McIntyre6feea572015-02-09 15:49:20 +0000815 s.vlan_create(vlan.tag)
816 s.vlan_set_name(vlan.tag, vlan.name)
817
Steve McIntyrefc511242014-12-23 22:28:30 +0000818 # Now, on each trunk port on the switch, we need to add all
819 # the VLANs already configured across our system
820 if not 'TrunkWildCardVlans' in s.switch_get_capabilities():
821 for port_id in trunk_ports:
822 port = db.get_port_by_id(port_id)
Steve McIntyreb826fc72015-07-27 17:57:40 +0100823
Steve McIntyrefc511242014-12-23 22:28:30 +0000824 for vlan in db.all_vlans():
Steve McIntyre49d3c082015-07-24 16:17:23 +0100825 if vlan.vlan_id != state.default_vlan_id:
Steve McIntyrea8fe1de2015-02-11 17:13:12 +0000826 if not vlan.tag in port_vlans[port.name]:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100827 logging.debug("Adding allowed VLAN tag %d to trunk port %s", vlan.tag, port.name)
Steve McIntyrea8fe1de2015-02-11 17:13:12 +0000828 s.port_add_trunk_to_vlan(port.name, vlan.tag)
Steve McIntyrefc511242014-12-23 22:28:30 +0000829
Steve McIntyrefc511242014-12-23 22:28:30 +0000830 # Done with this switch \o/
831 s.switch_save_running_config()
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000832 s.switch_disconnect()
833 del s
834
Steve McIntyre4b4ab652014-12-22 17:19:09 +0000835 ret = {}
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000836 ret['switch_id'] = switch_id
837 ret['num_ports_added'] = len(ports)
838 ret['num_vlans_added'] = len(new_vlan_tags)
839 return ret # If we're successful