blob: 4c9816a5524aeffb823d37bfdd7e81b39a739c5e [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):
Steve McIntyre26de4d92015-09-23 00:04:55 +01009 loglevel = logging.CRITICAL
10 if level == "ERROR":
11 loglevel = logging.ERROR
12 elif level == "WARNING":
13 loglevel = logging.WARNING
14 elif level == "INFO":
15 loglevel = logging.INFO
16 elif level == "DEBUG":
17 loglevel = logging.DEBUG
18 return loglevel
19
Steve McIntyre5f6f85e2014-12-22 16:42:28 +000020 def get_switch_driver(self, switch_name, config):
Steve McIntyre5fa22652015-04-01 18:01:45 +010021 logging.debug("Trying to find a driver for %s", switch_name)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +000022 driver = config.switches[switch_name].driver
Steve McIntyre5fa22652015-04-01 18:01:45 +010023 logging.debug("Driver: %s", driver)
Steve McIntyref1c04f92014-12-16 18:23:15 +000024 module = __import__("drivers.%s" % driver, fromlist=[driver])
25 class_ = getattr(module, driver)
Steve McIntyre0f38f0e2015-07-14 15:26:05 +010026 return class_(switch_name, debug = config.switches[switch_name].debug)
Steve McIntyref1c04f92014-12-16 18:23:15 +000027
Steve McIntyre519158e2014-12-23 13:44:44 +000028 def probe_switches(self, state):
29 config = state.config
Steve McIntyree8d80582014-12-23 16:53:39 +000030 ret = {}
Steve McIntyre5f6f85e2014-12-22 16:42:28 +000031 for switch_name in sorted(config.switches):
Steve McIntyre5fa22652015-04-01 18:01:45 +010032 logging.debug("Found switch %s:", switch_name)
Steve McIntyre7cf80982015-02-12 07:03:40 +000033 logging.debug(" Probing...")
Steve McIntyre519158e2014-12-23 13:44:44 +000034
Steve McIntyre4b4ab652014-12-22 17:19:09 +000035 s = self.get_switch_driver(switch_name, config)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +000036 s.switch_connect(config.switches[switch_name].username,
37 config.switches[switch_name].password,
Steve McIntyre3b655af2014-12-23 13:43:19 +000038 config.switches[switch_name].enable_password)
Steve McIntyre27d4b582014-12-23 22:51:00 +000039 ret[switch_name] = 'Found %d ports: ' % len(s.switch_get_port_names())
40 for name in s.switch_get_port_names():
41 ret[switch_name] += '%s ' % name
Steve McIntyrea2a8f792014-12-17 17:34:32 +000042 s.switch_disconnect()
Steve McIntyre2ed90f52015-07-21 17:59:52 +010043 del s
Steve McIntyrea2020cb2014-12-23 16:56:40 +000044 return ret
Steve McIntyrec68a18e2014-12-17 16:29:28 +000045
Steve McIntyre091e2ac2014-12-16 19:20:07 +000046 # Simple helper wrapper for all the read-only database queries
Steve McIntyre2150bc22014-12-17 13:13:56 +000047 def perform_db_query(self, state, command, data):
Steve McIntyre7cf80982015-02-12 07:03:40 +000048 logging.debug('perform_db_query')
49 logging.debug(command)
50 logging.debug(data)
Steve McIntyref1c04f92014-12-16 18:23:15 +000051 ret = {}
Steve McIntyre2150bc22014-12-17 13:13:56 +000052 db = state.db
Steve McIntyref1c04f92014-12-16 18:23:15 +000053 try:
Steve McIntyre091e2ac2014-12-16 19:20:07 +000054 if command == 'db.all_switches':
Steve McIntyref1c04f92014-12-16 18:23:15 +000055 ret = db.all_switches()
56 elif command == 'db.all_ports':
57 ret = db.all_ports()
58 elif command == 'db.all_vlans':
59 ret = db.all_vlans()
Steve McIntyrec4890132015-08-07 15:19:11 +010060 elif command == 'db.all_trunks':
61 ret = db.all_trunks()
Steve McIntyref1c04f92014-12-16 18:23:15 +000062 elif command == 'db.get_switch_by_id':
63 ret = db.get_switch_by_id(data['switch_id'])
64 elif command == 'db.get_switch_id_by_name':
65 ret = db.get_switch_id_by_name(data['name'])
66 elif command == 'db.get_switch_name_by_id':
67 ret = db.get_switch_name_by_id(data['switch_id'])
68 elif command == 'db.get_port_by_id':
69 ret = db.get_port_by_id(data['port_id'])
70 elif command == 'db.get_ports_by_switch':
71 ret = db.get_ports_by_switch(data['switch_id'])
72 elif command == 'db.get_port_by_switch_and_name':
73 ret = db.get_port_by_switch_and_name(data['switch_id'], data['name'])
Steve McIntyre45f55012015-08-05 13:55:15 +010074 elif command == 'db.get_port_by_switch_and_number':
75 ret = db.get_port_by_switch_and_number(data['switch_id'], int(data['number']))
Steve McIntyref1c04f92014-12-16 18:23:15 +000076 elif command == 'db.get_current_vlan_id_by_port':
77 ret = db.get_current_vlan_id_by_port(data['port_id'])
78 elif command == 'db.get_base_vlan_id_by_port':
79 ret = db.get_base_vlan_id_by_port(data['port_id'])
80 elif command == 'db.get_ports_by_current_vlan':
81 ret = db.get_ports_by_current_vlan(data['vlan_id'])
82 elif command == 'db.get_ports_by_base_vlan':
83 ret = db.get_ports_by_base_vlan(data['vlan_id'])
Steve McIntyrec4890132015-08-07 15:19:11 +010084 elif command == 'db.get_ports_by_trunk':
85 ret = db.get_ports_by_trunk(data['trunk_id'])
Steve McIntyref1c04f92014-12-16 18:23:15 +000086 elif command == 'db.get_vlan_by_id':
87 ret = db.get_vlan_by_id(data['vlan_id'])
Steve McIntyre65533d72015-01-23 18:01:17 +000088 elif command == 'db.get_vlan_tag_by_id':
89 ret = db.get_vlan_tag_by_id(data['vlan_id'])
Steve McIntyref1c04f92014-12-16 18:23:15 +000090 elif command == 'db.get_vlan_id_by_name':
91 ret = db.get_vlan_id_by_name(data['name'])
92 elif command == 'db.get_vlan_id_by_tag':
Steve McIntyre07946c22014-12-17 13:14:15 +000093 ret = db.get_vlan_id_by_tag(data['tag'])
Steve McIntyref1c04f92014-12-16 18:23:15 +000094 elif command == 'db.get_vlan_name_by_id':
95 ret = db.get_vlan_name_by_id(data['vlan_id'])
Steve McIntyrec4890132015-08-07 15:19:11 +010096 elif command == 'db.get_trunk_by_id':
97 ret = db.get_trunk_by_id(data['trunk_id'])
Steve McIntyref1c04f92014-12-16 18:23:15 +000098 else:
Steve McIntyree749fef2014-12-17 16:35:45 +000099 raise InputError("Unknown db_query command \"%s\"" % command)
Steve McIntyref1c04f92014-12-16 18:23:15 +0000100
Steve McIntyre978cb9f2015-10-09 16:51:28 +0100101 except InputError as e:
102 logging.error('perform_db_query(%s) got error %s', command, e)
Steve McIntyre5da37fa2014-12-17 13:14:44 +0000103 raise
Steve McIntyre978cb9f2015-10-09 16:51:28 +0100104 except ValueError as e:
105 logging.error('perform_db_query(%s) got error %s', command, e)
106 raise InputError("Invalid value in API call argument: %s" % e)
Steve McIntyref1c04f92014-12-16 18:23:15 +0000107
108 return ret
109
Steve McIntyre53c7ad92014-12-16 19:21:13 +0000110 # Simple helper wrapper for all the read-only daemon state queries
111 def perform_daemon_query(self, state, command, data):
Steve McIntyre7cf80982015-02-12 07:03:40 +0000112 logging.debug('perform_daemon_query')
113 logging.debug(command)
114 logging.debug(data)
Steve McIntyre53c7ad92014-12-16 19:21:13 +0000115 ret = {}
116 try:
117 if command == 'daemon.status':
118 # data ignored
119 ret['running'] = 'ok'
Steve McIntyreea343aa2015-10-23 17:46:17 +0100120 ret['last_modified'] = state.db.get_last_modified_time()
Steve McIntyre53c7ad92014-12-16 19:21:13 +0000121 elif command == 'daemon.version':
122 # data ignored
123 ret['version'] = state.version
124 elif command == 'daemon.statistics':
125 ret['uptime'] = time.time() - state.starttime
Steve McIntyre88b79df2014-12-23 13:45:08 +0000126 elif command == 'daemon.probe_switches':
127 ret = self.probe_switches(state)
Steve McIntyre06fe6422015-01-23 17:55:43 +0000128 elif command == 'daemon.shutdown':
129 # data ignored
130 ret['shutdown'] = 'Shutting down'
131 state.running = False
Steve McIntyre53c7ad92014-12-16 19:21:13 +0000132 else:
Steve McIntyree749fef2014-12-17 16:35:45 +0000133 raise InputError("Unknown daemon_query command \"%s\"" % command)
Steve McIntyre53c7ad92014-12-16 19:21:13 +0000134
Steve McIntyre978cb9f2015-10-09 16:51:28 +0100135 except InputError as e:
136 logging.error('perform_daemon_query(%s) got error %s', command, e)
Steve McIntyrea590b5b2014-12-17 13:15:14 +0000137 raise
Steve McIntyre978cb9f2015-10-09 16:51:28 +0100138 except ValueError as e:
139 logging.error('perform_daemon_query(%s) got error %s', command, e)
140 raise InputError("Invalid value in API call: %s" % e)
Steve McIntyrea590b5b2014-12-17 13:15:14 +0000141
142 return ret
143
Steve McIntyree749fef2014-12-17 16:35:45 +0000144 # Helper wrapper for API functions modifying database state only
Steve McIntyrea590b5b2014-12-17 13:15:14 +0000145 def perform_db_update(self, state, command, data):
Steve McIntyre7cf80982015-02-12 07:03:40 +0000146 logging.debug('perform_db_update')
147 logging.debug(command)
148 logging.debug(data)
Steve McIntyrea590b5b2014-12-17 13:15:14 +0000149 ret = {}
150 db = state.db
151 try:
152 if command == 'db.create_switch':
153 ret = db.create_switch(data['name'])
154 elif command == 'db.create_port':
Steve McIntyreca6adfc2015-08-06 15:08:58 +0100155 try:
156 number = int(data['number'])
157 except ValueError:
158 raise InputError("Invalid value for port number (%s) - must be numeric only!" % data['number'])
Steve McIntyrea590b5b2014-12-17 13:15:14 +0000159 ret = db.create_port(data['switch_id'], data['name'],
Steve McIntyreca6adfc2015-08-06 15:08:58 +0100160 number,
Steve McIntyrefefdbb42014-12-22 16:14:28 +0000161 state.default_vlan_id,
162 state.default_vlan_id)
Steve McIntyrec4890132015-08-07 15:19:11 +0100163 if command == 'db.create_trunk':
164 ret = db.create_trunk(data['port_id1'], data['port_id2'])
Steve McIntyrea590b5b2014-12-17 13:15:14 +0000165 elif command == 'db.delete_switch':
166 ret = db.delete_switch(data['switch_id'])
167 elif command == 'db.delete_port':
168 ret = db.delete_port(data['port_id'])
169 elif command == 'db.set_port_is_locked':
170 ret = db.set_port_is_locked(data['port_id'], data['is_locked'])
171 elif command == 'db.set_base_vlan':
172 ret = db.set_base_vlan(data['port_id'], data['base_vlan_id'])
Steve McIntyrec4890132015-08-07 15:19:11 +0100173 elif command == 'db.delete_trunk':
174 ret = db.delete_trunk(data['trunk_id'])
Steve McIntyrea590b5b2014-12-17 13:15:14 +0000175 else:
Steve McIntyre0f9dea62014-12-17 16:37:01 +0000176 raise InputError("Unknown db_update command \"%s\"" % command)
177
Steve McIntyre978cb9f2015-10-09 16:51:28 +0100178 except InputError as e:
179 logging.error('perform_db_update(%s) got error %s', command, e)
Steve McIntyre0f9dea62014-12-17 16:37:01 +0000180 raise
Steve McIntyre978cb9f2015-10-09 16:51:28 +0100181 except ValueError as e:
182 logging.error('perform_db_update(%s) got error %s', command, e)
183 raise InputError("Invalid value in API call: %s" % e)
Steve McIntyre0f9dea62014-12-17 16:37:01 +0000184
185 return ret
186
187 # Helper wrapper for API functions that modify both database state
188 # and on-switch VLAN state
189 def perform_vlan_update(self, state, command, data):
Steve McIntyre7cf80982015-02-12 07:03:40 +0000190 logging.debug('perform_vlan_update')
191 logging.debug(command)
192 logging.debug(data)
Steve McIntyre0f9dea62014-12-17 16:37:01 +0000193 ret = {}
Steve McIntyre4b4ab652014-12-22 17:19:09 +0000194
Steve McIntyre0f9dea62014-12-17 16:37:01 +0000195 try:
196 # All of these are complex commands, so call helpers
197 # rather than inline the code here
198 if command == 'api.create_vlan':
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000199 ret = self.create_vlan(state, data['name'], int(data['tag']), data['is_base_vlan'])
Steve McIntyre0f9dea62014-12-17 16:37:01 +0000200 elif command == 'api.delete_vlan':
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000201 ret = self.delete_vlan(state, int(data['vlan_id']))
Steve McIntyre0f9dea62014-12-17 16:37:01 +0000202 elif command == 'api.set_port_mode':
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000203 ret = self.set_port_mode(state, int(data['port_id']), data['mode'])
Steve McIntyre0f9dea62014-12-17 16:37:01 +0000204 elif command == 'api.set_current_vlan':
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000205 ret = self.set_current_vlan(state, int(data['port_id']), int(data['vlan_id']))
Steve McIntyre0f9dea62014-12-17 16:37:01 +0000206 elif command == 'api.restore_base_vlan':
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000207 ret = self.restore_base_vlan(state, int(data['port_id']))
208 elif command == 'api.auto_import_switch':
209 ret = self.auto_import_switch(state, data['switch'])
Steve McIntyre0f9dea62014-12-17 16:37:01 +0000210 else:
Steve McIntyrea590b5b2014-12-17 13:15:14 +0000211 raise InputError("Unknown query command \"%s\"" % command)
212
Steve McIntyre3256b182014-12-19 15:38:15 +0000213 except InputError as e:
Steve McIntyre978cb9f2015-10-09 16:51:28 +0100214 logging.error('perform_vlan_update(%s) got error %s', command, e)
Steve McIntyrea590b5b2014-12-17 13:15:14 +0000215 raise
Steve McIntyre978cb9f2015-10-09 16:51:28 +0100216 except ValueError as e:
217 logging.error('perform_vlan_update(%s) got error %s', command, e)
218 raise InputError("Invalid value in API call: %s" % e)
Steve McIntyre53c7ad92014-12-16 19:21:13 +0000219
220 return ret
221
222
Steve McIntyre3256b182014-12-19 15:38:15 +0000223 # Complex call
224 # 1. create the VLAN in the DB
225 # 2. Iterate through all switches:
Steve McIntyre1ab8b872014-12-19 18:37:00 +0000226 # a. Create the VLAN
227 # b. Add the VLAN to all trunk ports (if needed)
228 # 3. If all went OK, save config on all the switches
Steve McIntyre3256b182014-12-19 15:38:15 +0000229 #
230 # The VLAN may already exist on some of the switches, that's
Steve McIntyre153157d2014-12-19 18:05:20 +0000231 # fine. If things fail, we attempt to roll back by rebooting
232 # switches then removing the VLAN in the DB.
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000233 def create_vlan(self, state, name, tag, is_base_vlan):
Steve McIntyre3256b182014-12-19 15:38:15 +0000234
Steve McIntyre7cf80982015-02-12 07:03:40 +0000235 logging.debug('create_vlan')
Steve McIntyre3256b182014-12-19 15:38:15 +0000236 db = state.db
237 config = state.config
238
Steve McIntyre6b8d3862015-08-03 19:28:25 +0100239 # Check for tag == -1, i.e. use the next available tag
240 if tag == -1:
241 tag = db.find_lowest_unused_vlan_tag()
242 logging.debug('create_vlan called with a tag of -1, found first unused tag %d', tag)
243
Steve McIntyre3256b182014-12-19 15:38:15 +0000244 # 1. Database record first
245 try:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100246 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 +0000247 vlan_id = db.create_vlan(name, tag, is_base_vlan)
Steve McIntyre5fa22652015-04-01 18:01:45 +0100248 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 +0000249 except InputError:
Steve McIntyre7cf80982015-02-12 07:03:40 +0000250 logging.debug('DB creation failed')
Steve McIntyre3256b182014-12-19 15:38:15 +0000251 raise
252
Steve McIntyre153157d2014-12-19 18:05:20 +0000253 # Keep track of which switches we've configured, for later use
254 switches_done = []
255
Steve McIntyre3256b182014-12-19 15:38:15 +0000256 # 2. Now the switches
257 try:
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000258 for switch in db.all_switches():
Steve McIntyre3256b182014-12-19 15:38:15 +0000259 trunk_ports = []
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000260 switch_name = switch.name
Steve McIntyre3256b182014-12-19 15:38:15 +0000261 try:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100262 logging.debug('Adding new VLAN to switch %s', switch_name)
Steve McIntyre3256b182014-12-19 15:38:15 +0000263 # Get the right driver
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000264 s = self.get_switch_driver(switch_name, config)
265 s.switch_connect(config.switches[switch_name].username,
266 config.switches[switch_name].password,
Steve McIntyre3b655af2014-12-23 13:43:19 +0000267 config.switches[switch_name].enable_password)
Steve McIntyre3256b182014-12-19 15:38:15 +0000268
Steve McIntyre153157d2014-12-19 18:05:20 +0000269 # Mark this switch as one we've touched, for
270 # either config saving or rollback below
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000271 switches_done.append(switch_name)
Steve McIntyre153157d2014-12-19 18:05:20 +0000272
Steve McIntyre3256b182014-12-19 15:38:15 +0000273 # 2a. Create the VLAN on the switch
274 s.vlan_create(tag)
275 s.vlan_set_name(tag, name)
Steve McIntyre5fa22652015-04-01 18:01:45 +0100276 logging.debug('Added VLAN tag %d, name %s to switch %s', tag, name, switch_name)
Steve McIntyre3256b182014-12-19 15:38:15 +0000277
278 # 2b. Do we need to worry about trunk ports on this switch?
279 if 'TrunkWildCardVlans' in s.switch_get_capabilities():
Steve McIntyre7cf80982015-02-12 07:03:40 +0000280 logging.debug('This switch does not need special trunk port handling')
Steve McIntyre3256b182014-12-19 15:38:15 +0000281 else:
Steve McIntyre7cf80982015-02-12 07:03:40 +0000282 logging.debug('This switch needs special trunk port handling')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000283 trunk_ports = db.get_trunk_port_names_by_switch(switch.switch_id)
Steve McIntyre3256b182014-12-19 15:38:15 +0000284 if trunk_ports is None:
Steve McIntyre7cf80982015-02-12 07:03:40 +0000285 logging.debug("But it has no trunk ports defined")
Steve McIntyre3256b182014-12-19 15:38:15 +0000286 trunk_ports = []
287 else:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100288 logging.debug('Found %d trunk_ports that need adjusting', len(trunk_ports))
Steve McIntyre3256b182014-12-19 15:38:15 +0000289
290 # Modify any trunk ports as needed
291 for port in trunk_ports:
Steve McIntyre1ce09f92015-09-04 14:35:13 +0100292 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 +0000293 s.port_add_trunk_to_vlan(port, tag)
294
Steve McIntyre3256b182014-12-19 15:38:15 +0000295 # And now we're done with this switch
296 s.switch_disconnect()
297 del s
298
Steve McIntyre8f27ae12015-09-04 14:35:38 +0100299 except IOError as e:
Steve McIntyre9f7bac02015-09-18 14:10:34 +0100300 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 +0000301 raise
Steve McIntyreb826fc72015-07-27 17:57:40 +0100302
Steve McIntyre3256b182014-12-19 15:38:15 +0000303 except IOError:
Steve McIntyre153157d2014-12-19 18:05:20 +0000304 # Bugger. Looks like one of the switch calls above
305 # failed. To undo the changes safely, we'll need to reset
306 # all the switches we managed to configure. This could
307 # take some time!
Steve McIntyre38a24e52015-07-21 17:54:58 +0100308 logging.error('create_vlan failed, resetting all switches to recover')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000309 for switch_name in switches_done:
310 s = self.get_switch_driver(switch_name, config)
311 s.switch_connect(config.switches[switch_name].username,
312 config.switches[switch_name].password,
Steve McIntyre3b655af2014-12-23 13:43:19 +0000313 config.switches[switch_name].enable_password)
Steve McIntyre153157d2014-12-19 18:05:20 +0000314 s.switch_restart() # Will implicitly also close the connection
315 del s
316
Steve McIntyre3256b182014-12-19 15:38:15 +0000317 # Undo the database change
Steve McIntyre7cf80982015-02-12 07:03:40 +0000318 logging.debug('Switch access failed. Deleting the new VLAN entry in the database')
Steve McIntyre3256b182014-12-19 15:38:15 +0000319 db.delete_vlan(vlan_id)
320 raise
321
Steve McIntyre153157d2014-12-19 18:05:20 +0000322 # If we've got this far, things were successful. Save config
323 # on all the switches so it will persist across reboots
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000324 for switch_name in switches_done:
325 s = self.get_switch_driver(switch_name, config)
326 s.switch_connect(config.switches[switch_name].username,
327 config.switches[switch_name].password,
Steve McIntyre3b655af2014-12-23 13:43:19 +0000328 config.switches[switch_name].enable_password)
Steve McIntyre153157d2014-12-19 18:05:20 +0000329 s.switch_save_running_config()
330 s.switch_disconnect()
331 del s
332
Steve McIntyre6b8d3862015-08-03 19:28:25 +0100333 return (vlan_id, tag) # If we're successful
Steve McIntyre3256b182014-12-19 15:38:15 +0000334
Steve McIntyrefeb64522014-12-19 18:53:02 +0000335 # Complex call
336 # 1. Check in the DB if there are any ports on the VLAN. Bail if so
337 # 2. Iterate through all switches:
338 # a. Remove the VLAN from all trunk ports (if needed)
339 # b. Remove the VLAN
340 # 3. If all went OK, save config on the switches
341 # 4. Remove the VLAN in the DB
342 #
343 # If things fail, we attempt to roll back by rebooting switches.
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000344 def delete_vlan(self, state, vlan_id):
Steve McIntyrefeb64522014-12-19 18:53:02 +0000345
Steve McIntyre7cf80982015-02-12 07:03:40 +0000346 logging.debug('delete_vlan')
Steve McIntyrefeb64522014-12-19 18:53:02 +0000347 db = state.db
348 config = state.config
349
Steve McIntyrefeb64522014-12-19 18:53:02 +0000350 # 1. Check for database records first
Steve McIntyre5fa22652015-04-01 18:01:45 +0100351 logging.debug('Checking for ports using VLAN ID %d', vlan_id)
Steve McIntyrefeb64522014-12-19 18:53:02 +0000352 vlan = db.get_vlan_by_id(vlan_id)
353 if vlan is None:
354 raise InputError("VLAN ID %d does not exist" % vlan_id)
355 vlan_tag = vlan.tag
356 ports = db.get_ports_by_current_vlan(vlan_id)
357 if ports is not None:
358 raise InputError("Cannot delete VLAN ID %d when it still has %d ports" %
359 (vlan_id, len(ports)))
360 ports = db.get_ports_by_base_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
365 # Keep track of which switches we've configured, for later use
366 switches_done = []
367
368 # 2. Now the switches
369 try:
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000370 for switch in db.all_switches():
371 switch_name = switch.name
Steve McIntyrefeb64522014-12-19 18:53:02 +0000372 trunk_ports = []
373 try:
374 # Get the right driver
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000375 s = self.get_switch_driver(switch_name, config)
376 s.switch_connect(config.switches[switch_name].username,
377 config.switches[switch_name].password,
Steve McIntyre3b655af2014-12-23 13:43:19 +0000378 config.switches[switch_name].enable_password)
Steve McIntyrefeb64522014-12-19 18:53:02 +0000379
380 # Mark this switch as one we've touched, for
381 # either config saving or rollback below
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000382 switches_done.append(switch_name)
Steve McIntyrefeb64522014-12-19 18:53:02 +0000383
384 # 2a. Do we need to worry about trunk ports on this switch?
385 if 'TrunkWildCardVlans' in s.switch_get_capabilities():
Steve McIntyre7cf80982015-02-12 07:03:40 +0000386 logging.debug('This switch does not need special trunk port handling')
Steve McIntyrefeb64522014-12-19 18:53:02 +0000387 else:
Steve McIntyre7cf80982015-02-12 07:03:40 +0000388 logging.debug('This switch needs special trunk port handling')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000389 trunk_ports = db.get_trunk_port_names_by_switch(switch.switch_id)
Steve McIntyrefeb64522014-12-19 18:53:02 +0000390 if trunk_ports is None:
Steve McIntyre7cf80982015-02-12 07:03:40 +0000391 logging.debug("But it has no trunk ports defined")
Steve McIntyrefeb64522014-12-19 18:53:02 +0000392 trunk_ports = []
393 else:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100394 logging.debug('Found %d trunk_ports that need adjusting', len(trunk_ports))
Steve McIntyrefeb64522014-12-19 18:53:02 +0000395
396 # Modify any trunk ports as needed
397 for port in trunk_ports:
Steve McIntyre4b4ab652014-12-22 17:19:09 +0000398 s.port_remove_trunk_from_vlan(port, vlan_tag)
Steve McIntyre5fa22652015-04-01 18:01:45 +0100399 logging.debug('Removed VLAN tag %d from switch %s port %s', vlan_tag, switch_name, port)
Steve McIntyrefeb64522014-12-19 18:53:02 +0000400
401 # 2b. Remove the VLAN from the switch
Steve McIntyre5fa22652015-04-01 18:01:45 +0100402 logging.debug('Removing VLAN tag %d from switch %s', vlan_tag, switch_name)
Steve McIntyrefeb64522014-12-19 18:53:02 +0000403 s.vlan_destroy(vlan_tag)
Steve McIntyre5fa22652015-04-01 18:01:45 +0100404 logging.debug('Removed VLAN tag %d from switch %s', vlan_tag, switch_name)
Steve McIntyrefeb64522014-12-19 18:53:02 +0000405
406 # And now we're done with this switch
407 s.switch_disconnect()
408 del s
409
410 except IOError:
411 raise
412
413 except IOError:
414 # Bugger. Looks like one of the switch calls above
415 # failed. To undo the changes safely, we'll need to reset
416 # all the switches we managed to configure. This could
417 # take some time!
Steve McIntyre38a24e52015-07-21 17:54:58 +0100418 logging.error('delete_vlan failed, resetting all switches to recover')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000419 for switch_name in switches_done:
420 s = self.get_switch_driver(switch_name, config)
421 s.switch_connect(config.switches[switch_name].username,
422 config.switches[switch_name].password,
Steve McIntyre3b655af2014-12-23 13:43:19 +0000423 config.switches[switch_name].enable_password)
Steve McIntyrefeb64522014-12-19 18:53:02 +0000424 s.switch_restart() # Will implicitly also close the connection
425 del s
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000426 raise
Steve McIntyrefeb64522014-12-19 18:53:02 +0000427
428 # 3. If we've got this far, things were successful. Save
429 # config on all the switches so it will persist across reboots
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000430 for switch_name in switches_done:
431 s = self.get_switch_driver(switch_name, config)
432 s.switch_connect(config.switches[switch_name].username,
433 config.switches[switch_name].password,
Steve McIntyre3b655af2014-12-23 13:43:19 +0000434 config.switches[switch_name].enable_password)
Steve McIntyrefeb64522014-12-19 18:53:02 +0000435 s.switch_save_running_config()
436 s.switch_disconnect()
437 del s
438
439 # 4. Finally, remove the VLAN in the DB
440 try:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100441 logging.debug('Removing DB record: VLAN ID %d', vlan_id)
Steve McIntyrefeb64522014-12-19 18:53:02 +0000442 vlan_id = db.delete_vlan(vlan_id)
Steve McIntyre5fa22652015-04-01 18:01:45 +0100443 logging.debug('Removed VLAN ID %d from the database OK', vlan_id)
Steve McIntyrefeb64522014-12-19 18:53:02 +0000444 except InputError:
Steve McIntyre7cf80982015-02-12 07:03:40 +0000445 logging.debug('DB deletion failed')
Steve McIntyrefeb64522014-12-19 18:53:02 +0000446 raise
447
448 return vlan_id # If we're successful
449
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000450 # Complex call, depends on existing state a lot
451 # 1. Check validity of inputs
452 # 2. Switch mode and other config on the port.
453 # a. If switching trunk->access, remove all trunk VLANs from it
454 # (if needed) and switch back to the base VLAN for the
455 # port. Next, switch to access mode.
456 # b. If switching access->trunk, switch back to the base VLAN
457 # for the port. Next, switch mode. Then add all trunk VLANs
458 # to it (if needed)
459 # 3. If all went OK, save config on the switch
460 # 4. Change details of the port in the DB
461 #
462 # If things fail, we attempt to roll back by rebooting the switch
Steve McIntyre4b4ab652014-12-22 17:19:09 +0000463 def set_port_mode(self, state, port_id, mode):
Steve McIntyrefeb64522014-12-19 18:53:02 +0000464
Steve McIntyre7cf80982015-02-12 07:03:40 +0000465 logging.debug('set_port_mode')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000466 db = state.db
467 config = state.config
468
469 # 1. Sanity-check inputs
Steve McIntyre5b0de002015-01-23 18:05:13 +0000470 if mode != 'access' and mode != 'trunk':
471 raise InputError("Port mode '%s' is not a valid option: try 'access' or 'trunk'" % mode)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000472 port = db.get_port_by_id(port_id)
473 if port is None:
474 raise InputError("Port ID %d does not exist" % port_id)
Steve McIntyre28114092015-02-13 03:04:40 +0000475 if port.is_locked:
476 raise InputError("Port ID %d is locked" % port_id)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000477 if mode == 'trunk' and port.is_trunk:
Steve McIntyre3aff7da2015-02-13 04:00:11 +0000478 raise InputError("Port ID %d is already in trunk mode" % port_id)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000479 if mode == 'access' and not port.is_trunk:
Steve McIntyre3aff7da2015-02-13 04:00:11 +0000480 raise InputError("Port ID %d is already in access mode" % port_id)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000481 base_vlan_tag = db.get_vlan_tag_by_id(port.base_vlan_id)
482
483 # Get the right driver
484 switch_name = db.get_switch_name_by_id(port.switch_id)
485 s = self.get_switch_driver(switch_name, config)
486
487 # 2. Now start configuring the switch
488 try:
489 s.switch_connect(config.switches[switch_name].username,
490 config.switches[switch_name].password,
Steve McIntyre3b655af2014-12-23 13:43:19 +0000491 config.switches[switch_name].enable_password)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000492 except:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100493 logging.debug('Failed to talk to switch %s!', switch_name)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000494 raise
495
496 try:
497 if port.is_trunk:
498 # 2a. We're going from a trunk port to an access port
499 if 'TrunkWildCardVlans' in s.switch_get_capabilities():
Steve McIntyre7cf80982015-02-12 07:03:40 +0000500 logging.debug('This switch does not need special trunk port handling')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000501 else:
Steve McIntyre7cf80982015-02-12 07:03:40 +0000502 logging.debug('This switch needs special trunk port handling')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000503 vlans = s.port_get_trunk_vlan_list(port.name)
504 if vlans is None:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100505 logging.debug("But it has no VLANs defined on port %s", port.name)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000506 vlans = []
507 else:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100508 logging.debug('Found %d vlans that may need dropping on port %s', len(vlans), port.name)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000509
510 for vlan in vlans:
Steve McIntyre97f5e872015-01-23 18:07:05 +0000511 if vlan != state.config.vland.default_vlan_tag:
512 s.port_remove_trunk_from_vlan(port.name, vlan)
Steve McIntyref903b692015-07-09 18:29:05 +0100513
514 s.port_set_mode(port.name, "access")
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000515
516 else:
517 # 2b. We're going from an access port to a trunk port
518 s.port_set_access_vlan(port.name, base_vlan_tag)
519 s.port_set_mode(port.name, "trunk")
520 if 'TrunkWildCardVlans' in s.switch_get_capabilities():
Steve McIntyre7cf80982015-02-12 07:03:40 +0000521 logging.debug('This switch does not need special trunk port handling')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000522 else:
523 vlans = db.all_vlans()
524 for vlan in vlans:
Steve McIntyreee742d92015-09-03 18:16:09 +0100525 if vlan.tag != state.config.vland.default_vlan_tag:
Steve McIntyre36ae6ac2015-09-03 18:10:33 +0100526 s.port_add_trunk_to_vlan(port.name, vlan.tag)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000527
528 except IOError:
Steve McIntyre38a24e52015-07-21 17:54:58 +0100529 logging.error('set_port_mode failed, resetting switch to recover')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000530 # Bugger. Looks like one of the switch calls above
531 # failed. To undo the changes safely, we'll need to reset
532 # all the config on this switch
533 s.switch_restart() # Will implicitly also close the connection
534 del s
535 raise
536
537 # 3. All seems to have worked so far!
538 s.switch_save_running_config()
539 s.switch_disconnect()
540 del s
541
542 # 4. And update the DB
543 db.set_port_mode(port_id, mode)
544
545 return port_id # If we're successful
546
547 # Complex call, updating both DB and switch state
548 # 1. Check validity of inputs
549 # 2. Update the port config on the switch
550 # 3. If all went OK, save config on the switch
551 # 4. Change details of the port in the DB
552 #
553 # If things fail, we attempt to roll back by rebooting the switch
554 def set_current_vlan(self, state, port_id, vlan_id):
555
Steve McIntyre7cf80982015-02-12 07:03:40 +0000556 logging.debug('set_current_vlan')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000557 db = state.db
558 config = state.config
559
560 # 1. Sanity checks!
561 port = db.get_port_by_id(port_id)
562 if port is None:
563 raise InputError("Port ID %d does not exist" % port_id)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000564 if port.is_locked:
565 raise InputError("Port ID %d is locked" % port_id)
Steve McIntyre3bb7bbd2015-02-13 03:06:01 +0000566 if port.is_trunk:
567 raise InputError("Port ID %d is not an access port" % port_id)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000568
569 vlan = db.get_vlan_by_id(vlan_id)
570 if vlan is None:
571 raise InputError("VLAN ID %d does not exist" % vlan_id)
572
573 # Get the right driver
574 switch_name = db.get_switch_name_by_id(port.switch_id)
575 s = self.get_switch_driver(switch_name, config)
576
577 # 2. Now start configuring the switch
578 try:
579 s.switch_connect(config.switches[switch_name].username,
580 config.switches[switch_name].password,
Steve McIntyre3b655af2014-12-23 13:43:19 +0000581 config.switches[switch_name].enable_password)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000582 except:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100583 logging.debug('Failed to talk to switch %s!', switch_name)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000584 raise
585
586 try:
587 s.port_set_access_vlan(port.name, vlan.tag)
588 except IOError:
Steve McIntyre38a24e52015-07-21 17:54:58 +0100589 logging.error('set_current_vlan failed, resetting switch to recover')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000590 # Bugger. Looks like one of the switch calls above
591 # failed. To undo the changes safely, we'll need to reset
592 # all the config on this switch
593 s.switch_restart() # Will implicitly also close the connection
594 del s
595 raise
596
597 # 3. All seems to have worked so far!
598 s.switch_save_running_config()
599 s.switch_disconnect()
600 del s
601
602 # 4. And update the DB
603 db.set_current_vlan(port_id, vlan_id)
604
605 return port_id # If we're successful
606
607 # Complex call, updating both DB and switch state
608 # 1. Check validity of input
609 # 2. Update the port config on the switch
610 # 3. If all went OK, save config on the switch
611 # 4. Change details of the port in the DB
612 #
613 # If things fail, we attempt to roll back by rebooting the switch
614 def restore_base_vlan(self, state, port_id):
615
Steve McIntyre7cf80982015-02-12 07:03:40 +0000616 logging.debug('restore_base_vlan')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000617 db = state.db
618 config = state.config
619
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000620 # 1. Sanity checks!
621 port = db.get_port_by_id(port_id)
622 if port is None:
623 raise InputError("Port ID %d does not exist" % port_id)
624 if port.is_trunk:
625 raise InputError("Port ID %d is not an access port" % port_id)
626 if port.is_locked:
627 raise InputError("Port ID %d is locked" % port_id)
628
629 # Bail out early if we're *already* on the base VLAN. This is
630 # not an error
631 if port.current_vlan_id == port.base_vlan_id:
632 return port_id
633
634 vlan = db.get_vlan_by_id(port.base_vlan_id)
635
636 # Get the right driver
637 switch_name = db.get_switch_name_by_id(port.switch_id)
638 s = self.get_switch_driver(switch_name, config)
639
640 # 2. Now start configuring the switch
641 try:
642 s.switch_connect(config.switches[switch_name].username,
643 config.switches[switch_name].password,
Steve McIntyre3b655af2014-12-23 13:43:19 +0000644 config.switches[switch_name].enable_password)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000645 except:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100646 logging.debug('Failed to talk to switch %s!', switch_name)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000647 raise
648
649 try:
650 s.port_set_access_vlan(port.name, vlan.tag)
651 except IOError:
Steve McIntyre38a24e52015-07-21 17:54:58 +0100652 logging.error('restore_base_vlan failed, resetting switch to recover')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000653 # Bugger. Looks like one of the switch calls above
654 # failed. To undo the changes safely, we'll need to reset
655 # all the config on this switch
656 s.switch_restart() # Will implicitly also close the connection
657 del s
658 raise
659
660 # 3. All seems to have worked so far!
661 s.switch_save_running_config()
662 s.switch_disconnect()
663 del s
664
665 # 4. And update the DB
666 db.set_current_vlan(port_id, port.base_vlan_id)
667
668 return port_id # If we're successful
669
670 # Complex call, updating both DB and switch state
671 # * Check validity of input
672 # * Read all the config from the switch (switch, ports, VLANs)
673 # * Create initial DB entries to match each of those
674 # * Merge VLANs across all switches
675 # * Set up ports appropriately
676 #
677 def auto_import_switch(self, state, switch_name):
678
Steve McIntyre7cf80982015-02-12 07:03:40 +0000679 logging.debug('auto_import_switch')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000680 db = state.db
681 config = state.config
682
Steve McIntyrea8fe1de2015-02-11 17:13:12 +0000683 port_vlans = {}
684
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000685 # 1. Sanity checks!
686 switch_id = db.get_switch_id_by_name(switch_name)
687 if switch_id is not None:
688 raise InputError("Switch name %s already exists in the DB (ID %d)" % (switch_name, switch_id))
689
Steve McIntyre4b4ab652014-12-22 17:19:09 +0000690 if not switch_name in config.switches:
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000691 raise InputError("Switch name %s not defined in config" % switch_name)
692
693 # 2. Now start reading config from the switch
694 try:
695 s = self.get_switch_driver(switch_name, config)
696 s.switch_connect(config.switches[switch_name].username,
697 config.switches[switch_name].password,
Steve McIntyre3b655af2014-12-23 13:43:19 +0000698 config.switches[switch_name].enable_password)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000699 except:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100700 logging.debug('Failed to talk to switch %s!', switch_name)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000701 raise
702
703 # DON'T create the switch record in the DB first - we'll want
Steve McIntyrefc511242014-12-23 22:28:30 +0000704 # to create VLANs on *other* switches, and it's easier to do
705 # that before we've added our new switch
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000706
707 new_vlan_tags = []
708
709 # Grab the VLANs defined on this switch
710 vlan_tags = s.vlan_get_list()
Steve McIntyrefc511242014-12-23 22:28:30 +0000711
Steve McIntyre5fa22652015-04-01 18:01:45 +0100712 logging.debug(' found %d vlans on the switch', len(vlan_tags))
Steve McIntyrefc511242014-12-23 22:28:30 +0000713
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000714 for vlan_tag in vlan_tags:
715 vlan_name = s.vlan_get_name(vlan_tag)
716
717 # If a VLAN is already in the database, then that's easy -
718 # we can just ignore it. However, we have to check that
719 # there is not a different name for the existing VLAN tag
Steve McIntyreb1529072014-12-23 17:17:22 +0000720 # - bail out if so... UNLESS we're looking at the default
721 # VLAN
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000722 #
723 # If this VLAN tag is not already in the DB, we'll need to
724 # add it there and to all the other switches (and their
725 # trunk ports!) too.
Steve McIntyrefc511242014-12-23 22:28:30 +0000726 vlan_id = db.get_vlan_id_by_tag(vlan_tag)
Steve McIntyre6c09c0c2015-07-17 17:20:01 +0100727 if vlan_id != state.default_vlan_id:
Steve McIntyreb1529072014-12-23 17:17:22 +0000728 if vlan_id is not None:
729 vlan_db_name = db.get_vlan_name_by_id(vlan_id)
730 if vlan_name != vlan_db_name:
731 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 +0000732
Steve McIntyreb1529072014-12-23 17:17:22 +0000733 else:
734 # OK, we'll need to set up the new VLAN now. It can't
735 # be a base VLAN - switches don't have such a concept!
736 # Rather than create individually here, add to a
737 # list. *Only* once we've worked through all the
738 # switch's VLANs successfully (checking for existing
739 # records and possible clashes!) should we start
740 # committing changes
741 new_vlan_tags.append(vlan_tag)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000742
743 # Now create the VLAN DB entries
744 for vlan_tag in new_vlan_tags:
745 vlan_name = s.vlan_get_name(vlan_tag)
746 vlan_id = self.create_vlan(state, vlan_name, vlan_tag, False)
747
748 # *Now* add this switch itself to the database, after we've
749 # worked on all the other switches
750 switch_id = db.create_switch(switch_name)
751
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000752 # And now the ports
753 trunk_ports = []
754 ports = s.switch_get_port_names()
Steve McIntyre5fa22652015-04-01 18:01:45 +0100755 logging.debug(' found %d ports on the switch', len(ports))
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000756 for port_name in ports:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100757 logging.debug(' trying to import port %s', port_name)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000758 port_id = None
759 port_mode = s.port_get_mode(port_name)
Steve McIntyreea753972015-08-05 13:52:48 +0100760 port_number = s.port_map_name_to_number(port_name)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000761 if port_mode == 'access':
762 # Access ports are easy - just create the port, and
763 # set both the current and base VLANs to the current
764 # VLAN on the switch. We'll end up changing this after
765 # import if needed.
Steve McIntyrea8fe1de2015-02-11 17:13:12 +0000766 port_vlans[port_name] = (s.port_get_access_vlan(port_name),)
767 port_vlan_id = db.get_vlan_id_by_tag(port_vlans[port_name][0])
Steve McIntyreea753972015-08-05 13:52:48 +0100768 port_id = db.create_port(switch_id, port_name, port_number,
Steve McIntyre6f17b102014-12-24 02:18:08 +0000769 port_vlan_id, port_vlan_id)
Steve McIntyre5fa22652015-04-01 18:01:45 +0100770 logging.debug(' access port, VLAN %d', int(port_vlans[port_name][0]))
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000771 # Nothing further needed
772 elif port_mode == 'trunk':
Steve McIntyre7cf80982015-02-12 07:03:40 +0000773 logging.debug(' trunk port, VLANs:')
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000774 # Trunk ports are a little more involved. First,
775 # create the port in the DB, setting the VLANs to the
776 # first VLAN found on the trunk port. This will *also*
777 # be in access mode by default, and unlocked.
Steve McIntyrea8fe1de2015-02-11 17:13:12 +0000778 port_vlans[port_name] = s.port_get_trunk_vlan_list(port_name)
Steve McIntyre7cf80982015-02-12 07:03:40 +0000779 logging.debug(port_vlans[port_name])
Steve McIntyrea8fe1de2015-02-11 17:13:12 +0000780 if port_vlans[port_name] == [] or port_vlans[port_name] is None or 'ALL' in port_vlans[port_name]:
781 port_vlans[port_name] = (state.config.vland.default_vlan_tag,)
782 port_vlan_id = db.get_vlan_id_by_tag(port_vlans[port_name][0])
Steve McIntyreea753972015-08-05 13:52:48 +0100783 port_id = db.create_port(switch_id, port_name, port_number,
Steve McIntyre6f17b102014-12-24 02:18:08 +0000784 port_vlan_id, port_vlan_id)
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000785 # Append to a list of trunk ports that we will need to
786 # modify once we're done
787 trunk_ports.append(port_id)
Steve McIntyrefc511242014-12-23 22:28:30 +0000788 else:
Steve McIntyre857d89f2015-07-24 16:22:37 +0100789 # We've found a port mode we don't want, e.g. the
790 # "dynamic auto" on a Cisco Catalyst. Handle that here
791 # - tell the switch to set that port to access and
792 # handle accordingly.
793 s.port_set_mode(port_name, 'access')
794 port_vlans[port_name] = (s.port_get_access_vlan(port_name),)
795 port_vlan_id = db.get_vlan_id_by_tag(port_vlans[port_name][0])
Steve McIntyreea753972015-08-05 13:52:48 +0100796 port_id = db.create_port(switch_id, port_name, port_number,
Steve McIntyre857d89f2015-07-24 16:22:37 +0100797 port_vlan_id, port_vlan_id)
798 logging.debug(' Found port in %s mode', port_mode)
799 logging.debug(' Forcing to access mode, VLAN %d', int(port_vlans[port_name][0]))
800 port_mode = "access"
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000801
Steve McIntyre5fa22652015-04-01 18:01:45 +0100802 logging.debug(" Added port %s, got port ID %d", port_name, port_id)
Steve McIntyrefc511242014-12-23 22:28:30 +0000803
Steve McIntyre574e3342015-01-23 18:08:33 +0000804 db.set_port_mode(port_id, port_mode)
805
Steve McIntyre6feea572015-02-09 15:49:20 +0000806 # Make sure this switch has all the VLANs we need
807 for vlan in db.all_vlans():
Steve McIntyre49d3c082015-07-24 16:17:23 +0100808 if vlan.tag != state.config.vland.default_vlan_tag:
Steve McIntyred7fb4072015-02-11 17:13:50 +0000809 if not vlan.tag in vlan_tags:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100810 logging.debug("Adding VLAN tag %d to this switch", vlan.tag)
Steve McIntyre6feea572015-02-09 15:49:20 +0000811 s.vlan_create(vlan.tag)
812 s.vlan_set_name(vlan.tag, vlan.name)
813
Steve McIntyrefc511242014-12-23 22:28:30 +0000814 # Now, on each trunk port on the switch, we need to add all
815 # the VLANs already configured across our system
816 if not 'TrunkWildCardVlans' in s.switch_get_capabilities():
817 for port_id in trunk_ports:
818 port = db.get_port_by_id(port_id)
Steve McIntyreb826fc72015-07-27 17:57:40 +0100819
Steve McIntyrefc511242014-12-23 22:28:30 +0000820 for vlan in db.all_vlans():
Steve McIntyre49d3c082015-07-24 16:17:23 +0100821 if vlan.vlan_id != state.default_vlan_id:
Steve McIntyrea8fe1de2015-02-11 17:13:12 +0000822 if not vlan.tag in port_vlans[port.name]:
Steve McIntyre5fa22652015-04-01 18:01:45 +0100823 logging.debug("Adding allowed VLAN tag %d to trunk port %s", vlan.tag, port.name)
Steve McIntyrea8fe1de2015-02-11 17:13:12 +0000824 s.port_add_trunk_to_vlan(port.name, vlan.tag)
Steve McIntyrefc511242014-12-23 22:28:30 +0000825
Steve McIntyrefc511242014-12-23 22:28:30 +0000826 # Done with this switch \o/
827 s.switch_save_running_config()
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000828 s.switch_disconnect()
829 del s
830
Steve McIntyre4b4ab652014-12-22 17:19:09 +0000831 ret = {}
Steve McIntyre5f6f85e2014-12-22 16:42:28 +0000832 ret['switch_id'] = switch_id
833 ret['num_ports_added'] = len(ports)
834 ret['num_vlans_added'] = len(new_vlan_tags)
835 return ret # If we're successful