blob: 1d0c1e92c693770c361473ed714611438ed852eb [file] [log] [blame]
Steve McIntyref1c04f92014-12-16 18:23:15 +00001import logging
2import os
Steve McIntyre53c7ad92014-12-16 19:21:13 +00003import time
Steve McIntyref1c04f92014-12-16 18:23:15 +00004from db.db import VlanDB
5from errors import CriticalError, InputError, ConfigError, SocketError
6
7class VlanUtil:
8 """VLANd utility functions"""
9
10 def get_switch_driver(self, switch, config):
11 logging.debug("Trying to find a driver for %s" % switch)
12 driver = config.switches[switch].driver
13 logging.debug("Driver: %s" % driver)
14 module = __import__("drivers.%s" % driver, fromlist=[driver])
15 class_ = getattr(module, driver)
16 return class_(switch)
17
Steve McIntyrec68a18e2014-12-17 16:29:28 +000018 def get_all_switches(self, config):
19 for switch in sorted(config.switches):
Steve McIntyrea2a8f792014-12-17 17:34:32 +000020 print "Found switch %s:" % (switch)
21 print " Probing:"
Steve McIntyrec68a18e2014-12-17 16:29:28 +000022
Steve McIntyrea2a8f792014-12-17 17:34:32 +000023 s = util.get_switch_driver(switch, config)
24 s.switch_connect(config.switches[switch].username, config.switches[switch].password)
25 print " Found details of switch:"
26 s._dump_list(s._systemdata)
27 print " Switch has %d ports:" % len(s.switch_get_port_names())
28 for port in s.switch_get_port_names():
29 print " %s" % port
30 if 0 == 1:
31 mode = s.port_get_mode(port)
32 if mode == "trunk":
33 print " port %s is in trunk mode, VLAN(s):" % port
34 vlans = s.port_get_trunk_vlan_list(port)
35 for vlan in vlans:
36 name = s.vlan_get_name(vlan)
37 print " %d (%s)" % (vlan, name)
38 else:
39 vlan = s.port_get_access_vlan(port)
40 name = s.vlan_get_name(vlan)
41 print " port %s is in access mode, VLAN %d (%s):" % (port, vlan, name)
42 s.switch_disconnect()
43 del(s)
Steve McIntyrec68a18e2014-12-17 16:29:28 +000044
Steve McIntyre091e2ac2014-12-16 19:20:07 +000045 # Simple helper wrapper for all the read-only database queries
Steve McIntyre2150bc22014-12-17 13:13:56 +000046 def perform_db_query(self, state, command, data):
Steve McIntyre091e2ac2014-12-16 19:20:07 +000047 print 'perform_db_query'
Steve McIntyref1c04f92014-12-16 18:23:15 +000048 print command
49 print data
50 ret = {}
Steve McIntyre2150bc22014-12-17 13:13:56 +000051 db = state.db
Steve McIntyref1c04f92014-12-16 18:23:15 +000052 try:
Steve McIntyre091e2ac2014-12-16 19:20:07 +000053 if command == 'db.all_switches':
Steve McIntyref1c04f92014-12-16 18:23:15 +000054 ret = db.all_switches()
55 elif command == 'db.all_ports':
56 ret = db.all_ports()
57 elif command == 'db.all_vlans':
58 ret = db.all_vlans()
59 elif command == 'db.get_switch_by_id':
60 ret = db.get_switch_by_id(data['switch_id'])
61 elif command == 'db.get_switch_id_by_name':
62 ret = db.get_switch_id_by_name(data['name'])
63 elif command == 'db.get_switch_name_by_id':
64 ret = db.get_switch_name_by_id(data['switch_id'])
65 elif command == 'db.get_port_by_id':
66 ret = db.get_port_by_id(data['port_id'])
67 elif command == 'db.get_ports_by_switch':
68 ret = db.get_ports_by_switch(data['switch_id'])
69 elif command == 'db.get_port_by_switch_and_name':
70 ret = db.get_port_by_switch_and_name(data['switch_id'], data['name'])
71 elif command == 'db.get_current_vlan_id_by_port':
72 ret = db.get_current_vlan_id_by_port(data['port_id'])
73 elif command == 'db.get_base_vlan_id_by_port':
74 ret = db.get_base_vlan_id_by_port(data['port_id'])
75 elif command == 'db.get_ports_by_current_vlan':
76 ret = db.get_ports_by_current_vlan(data['vlan_id'])
77 elif command == 'db.get_ports_by_base_vlan':
78 ret = db.get_ports_by_base_vlan(data['vlan_id'])
79 elif command == 'db.get_vlan_by_id':
80 ret = db.get_vlan_by_id(data['vlan_id'])
81 elif command == 'db.get_vlan_id_by_name':
82 ret = db.get_vlan_id_by_name(data['name'])
83 elif command == 'db.get_vlan_id_by_tag':
Steve McIntyre07946c22014-12-17 13:14:15 +000084 ret = db.get_vlan_id_by_tag(data['tag'])
Steve McIntyref1c04f92014-12-16 18:23:15 +000085 elif command == 'db.get_vlan_name_by_id':
86 ret = db.get_vlan_name_by_id(data['vlan_id'])
87 else:
Steve McIntyree749fef2014-12-17 16:35:45 +000088 raise InputError("Unknown db_query command \"%s\"" % command)
Steve McIntyref1c04f92014-12-16 18:23:15 +000089
Steve McIntyre5da37fa2014-12-17 13:14:44 +000090 except InputError:
91 raise
92
Steve McIntyref1c04f92014-12-16 18:23:15 +000093 except:
94 raise InputError("Invalid input in query")
95
96 return ret
97
Steve McIntyre53c7ad92014-12-16 19:21:13 +000098 # Simple helper wrapper for all the read-only daemon state queries
99 def perform_daemon_query(self, state, command, data):
100 print 'perform_daemon_query'
101 print command
102 print data
103 ret = {}
104 try:
105 if command == 'daemon.status':
106 # data ignored
107 ret['running'] = 'ok'
108 elif command == 'daemon.version':
109 # data ignored
110 ret['version'] = state.version
111 elif command == 'daemon.statistics':
112 ret['uptime'] = time.time() - state.starttime
113 else:
Steve McIntyree749fef2014-12-17 16:35:45 +0000114 raise InputError("Unknown daemon_query command \"%s\"" % command)
Steve McIntyre53c7ad92014-12-16 19:21:13 +0000115
Steve McIntyrea590b5b2014-12-17 13:15:14 +0000116 except InputError:
117 raise
118
119 except:
120 raise InputError("Invalid input in query")
121
122 return ret
123
Steve McIntyree749fef2014-12-17 16:35:45 +0000124 # Helper wrapper for API functions modifying database state only
Steve McIntyrea590b5b2014-12-17 13:15:14 +0000125 def perform_db_update(self, state, command, data):
126 print 'perform_db_update'
127 print command
128 print data
129 ret = {}
130 db = state.db
131 try:
132 if command == 'db.create_switch':
133 ret = db.create_switch(data['name'])
134 elif command == 'db.create_port':
135 ret = db.create_port(data['switch_id'], data['name'],
136 state.config.default_vlan_id,
137 state.config.default_vlan_id)
138 elif command == 'db.delete_switch':
139 ret = db.delete_switch(data['switch_id'])
140 elif command == 'db.delete_port':
141 ret = db.delete_port(data['port_id'])
142 elif command == 'db.set_port_is_locked':
143 ret = db.set_port_is_locked(data['port_id'], data['is_locked'])
144 elif command == 'db.set_base_vlan':
145 ret = db.set_base_vlan(data['port_id'], data['base_vlan_id'])
146 else:
Steve McIntyre0f9dea62014-12-17 16:37:01 +0000147 raise InputError("Unknown db_update command \"%s\"" % command)
148
149 except InputError:
150 raise
151
152 except:
153 raise InputError("Invalid input in query")
154
155 return ret
156
157 # Helper wrapper for API functions that modify both database state
158 # and on-switch VLAN state
159 def perform_vlan_update(self, state, command, data):
160 print 'perform_vlan_update'
161 print command
162 print data
163 ret = {}
164 db = state.db
165 try:
166 # All of these are complex commands, so call helpers
167 # rather than inline the code here
168 if command == 'api.create_vlan':
169 ret = self.create_vlan(state, command, data)
170 elif command == 'api.delete_vlan':
171 ret = self.delete_vlan(state, command, data)
172 elif command == 'api.set_port_mode':
173 ret = self.set_port_mode(state, command, data)
174 elif command == 'api.set_current_vlan':
175 ret = self.set_current_vlan(state, command, data)
176 elif command == 'api.restore_base_vlan':
177 ret = self.restore_base_vlan(state, command, data)
178 else:
Steve McIntyrea590b5b2014-12-17 13:15:14 +0000179 raise InputError("Unknown query command \"%s\"" % command)
180
Steve McIntyre3256b182014-12-19 15:38:15 +0000181 except InputError as e:
182 print 'got error %s' % e
Steve McIntyrea590b5b2014-12-17 13:15:14 +0000183 raise
184
Steve McIntyre53c7ad92014-12-16 19:21:13 +0000185 except:
186 raise InputError("Invalid input in query")
187
188 return ret
189
190
Steve McIntyre3256b182014-12-19 15:38:15 +0000191 # Complex call
192 # 1. create the VLAN in the DB
193 # 2. Iterate through all switches:
194 # a. Create the VLAN
195 # b. Add the VLAN to all trunk ports (if needed)
196 #
197 # The VLAN may already exist on some of the switches, that's
Steve McIntyre153157d2014-12-19 18:05:20 +0000198 # fine. If things fail, we attempt to roll back by rebooting
199 # switches then removing the VLAN in the DB.
Steve McIntyre3256b182014-12-19 15:38:15 +0000200 def create_vlan(self, state, command, data):
201
202 print 'create_vlan'
203 db = state.db
204 config = state.config
205
206 name = data['name']
207 tag = int(data['tag'])
208 is_base_vlan = data['is_base_vlan']
209
210 # 1. Database record first
211 try:
212 print 'Adding DB record first: name %s, tag %d, is_base_vlan %d' % (name, tag, is_base_vlan)
213 vlan_id = db.create_vlan(name, tag, is_base_vlan)
214 print 'Added VLAN tag %d, name %s to the database, created VLAN ID %d' % (tag, name, vlan_id)
215 except InputError:
216 print 'DB creation failed'
217 raise
218
Steve McIntyre153157d2014-12-19 18:05:20 +0000219 # Keep track of which switches we've configured, for later use
220 switches_done = []
221
Steve McIntyre3256b182014-12-19 15:38:15 +0000222 # 2. Now the switches
223 try:
224 for switch in sorted(config.switches):
225 trunk_ports = []
226 try:
227 print 'Adding new VLAN to switch %s' % switch
228 # Get the right driver
229 s = self.get_switch_driver(switch, config)
230 s.switch_connect(config.switches[switch].username, config.switches[switch].password)
231
Steve McIntyre153157d2014-12-19 18:05:20 +0000232 # Mark this switch as one we've touched, for
233 # either config saving or rollback below
234 switches_done.append(switch)
235
Steve McIntyre3256b182014-12-19 15:38:15 +0000236 # 2a. Create the VLAN on the switch
237 s.vlan_create(tag)
238 s.vlan_set_name(tag, name)
239 print 'Added VLAN tag %d, name %s to switch %s' % (tag, name, switch)
240
241 # 2b. Do we need to worry about trunk ports on this switch?
242 if 'TrunkWildCardVlans' in s.switch_get_capabilities():
243 print 'This switch does not need special trunk port handling'
244 else:
245 print 'This switch needs special trunk port handling'
246 switch_id = db.get_switch_id_by_name(switch)
247 trunk_ports = db.get_trunk_port_names_by_switch(switch_id)
248 if trunk_ports is None:
249 print "But it has no trunk ports defined"
250 trunk_ports = []
251 else:
252 print 'Found %d trunk_ports that need adjusting' % len(trunk_ports)
253
254 # Modify any trunk ports as needed
255 for port in trunk_ports:
256 print 'Added VLAN tag %d, name %s to switch %s' % (tag, name, switch)
257 s.port_add_trunk_to_vlan(port, tag)
258
Steve McIntyre3256b182014-12-19 15:38:15 +0000259 # And now we're done with this switch
260 s.switch_disconnect()
261 del s
262
263 except IOError:
264 raise
265
266 except IOError:
Steve McIntyre153157d2014-12-19 18:05:20 +0000267 # Bugger. Looks like one of the switch calls above
268 # failed. To undo the changes safely, we'll need to reset
269 # all the switches we managed to configure. This could
270 # take some time!
271 for switch in switches_done:
272 s = self.get_switch_driver(switch, config)
273 s.switch_connect(config.switches[switch].username, config.switches[switch].password)
274 s.switch_restart() # Will implicitly also close the connection
275 del s
276
Steve McIntyre3256b182014-12-19 15:38:15 +0000277 # Undo the database change
278 print 'Switch access failed. Deleting the new VLAN entry in the database'
279 db.delete_vlan(vlan_id)
280 raise
281
Steve McIntyre153157d2014-12-19 18:05:20 +0000282 # If we've got this far, things were successful. Save config
283 # on all the switches so it will persist across reboots
284 for switch in switches_done
285 s = self.get_switch_driver(switch, config)
286 s.switch_connect(config.switches[switch].username, config.switches[switch].password)
287 s.switch_save_running_config()
288 s.switch_disconnect()
289 del s
290
Steve McIntyre3256b182014-12-19 15:38:15 +0000291 return vlan_id # If we're successful
292