|
1 #!/usr/bin/python2.6 |
|
2 # |
|
3 # Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. |
|
4 # |
|
5 # Licensed under the Apache License, Version 2.0 (the "License"); you may |
|
6 # not use this file except in compliance with the License. You may obtain |
|
7 # a copy of the License at |
|
8 # |
|
9 # http://www.apache.org/licenses/LICENSE-2.0 |
|
10 # |
|
11 # Unless required by applicable law or agreed to in writing, software |
|
12 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
|
13 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
|
14 # License for the specific language governing permissions and limitations |
|
15 # under the License. |
|
16 |
|
17 # |
|
18 # This script migrates the network, subnet and port information from EVS DB to |
|
19 # neutron-server DB. It also re-creates routers and floatingips tables with |
|
20 # Neutron's l3 schema. This script needs to be run for the proper upgrade of |
|
21 # Neutron from Havana to Juno release. |
|
22 # |
|
23 |
|
24 import ConfigParser |
|
25 import rad.connect as radcon |
|
26 import rad.bindings.com.oracle.solaris.rad.evscntl as evsc |
|
27 |
|
28 from neutron import context as ctx |
|
29 from neutron.db import common_db_mixin |
|
30 from neutron.db import api as db |
|
31 from neutron.db import model_base |
|
32 from neutron.plugins.evs.migrate import havana_api |
|
33 |
|
34 import sqlalchemy as sa |
|
35 from sqlalchemy import MetaData |
|
36 from sqlalchemy.orm import sessionmaker |
|
37 from sqlalchemy.schema import DropConstraint |
|
38 from sqlalchemy import sql |
|
39 |
|
40 from oslo.config import cfg |
|
41 from oslo.db import options as db_options |
|
42 from oslo.db import exception as excp |
|
43 |
|
44 |
|
45 def create_db_network(nw, engine, ext_ro): |
|
46 ''' Method for creating networks table in the neutron-server DB |
|
47 Input params: |
|
48 @nw - Dictionary with values from EVS DB |
|
49 @engine - SQL engine |
|
50 @ext_ro - External router |
|
51 ''' |
|
52 # Importing locally because these modules end up importing neutron.wsgi |
|
53 # which causes RAD to hang |
|
54 from neutron.db import db_base_plugin_v2 |
|
55 from neutron.db import external_net_db as ext_net |
|
56 model_base.BASEV2.metadata.bind = engine |
|
57 model_base.BASEV2.metadata.create_all(engine) |
|
58 ctxt = ctx.get_admin_context() |
|
59 inst = db_base_plugin_v2.NeutronDbPluginV2() |
|
60 dup = False |
|
61 try: |
|
62 db_base_plugin_v2.NeutronDbPluginV2.create_network(inst, ctxt, nw) |
|
63 print "\nnetwork=%s added" % nw['network']['name'] |
|
64 if ext_ro: |
|
65 ext_nw = ext_net.ExternalNetwork(network_id=nw['network']['id']) |
|
66 session = sessionmaker() |
|
67 session.configure(bind=engine) |
|
68 s = session() |
|
69 s.add(ext_nw) |
|
70 s.commit() |
|
71 except excp.DBDuplicateEntry: |
|
72 print "\nnetwork '%s' already exists" % nw['network']['name'] |
|
73 dup = True |
|
74 return dup |
|
75 |
|
76 |
|
77 def create_db_subnet(sub): |
|
78 ''' Method for creating subnets table in the neutron-server DB |
|
79 Input params: |
|
80 @sub - Dictionary with values from EVS DB |
|
81 ''' |
|
82 # Importing locally because this module ends up importing neutron.wsgi |
|
83 # which causes RAD to hang |
|
84 from neutron.db import db_base_plugin_v2 |
|
85 ctxt = ctx.get_admin_context() |
|
86 inst = db_base_plugin_v2.NeutronDbPluginV2() |
|
87 try: |
|
88 db_base_plugin_v2.NeutronDbPluginV2.create_subnet(inst, ctxt, sub) |
|
89 print "\nsubnet=%s added" % sub['subnet']['id'] |
|
90 except excp.DBDuplicateEntry: |
|
91 print "\nsubnet '%s' already exists" % sub['subnet']['id'] |
|
92 |
|
93 |
|
94 def create_db_port(port): |
|
95 ''' Method for creating ports table in the neutron-server DB |
|
96 Input params: |
|
97 @port - Dictionary with values from EVS DB |
|
98 ''' |
|
99 # Importing locally because this module ends up importing neutron.wsgi |
|
100 # which causes RAD to hang |
|
101 from neutron.db import db_base_plugin_v2 |
|
102 ctxt = ctx.get_admin_context() |
|
103 inst = db_base_plugin_v2.NeutronDbPluginV2() |
|
104 try: |
|
105 db_base_plugin_v2.NeutronDbPluginV2.create_port(inst, ctxt, port) |
|
106 print "\nport=%s added" % port['port']['id'] |
|
107 except excp.DBDuplicateEntry: |
|
108 print "\nport '%s' already exists" % port['port']['id'] |
|
109 |
|
110 |
|
111 def main(): |
|
112 print "Start Migration." |
|
113 |
|
114 # Connect to EVS controller |
|
115 config = ConfigParser.RawConfigParser() |
|
116 config.readfp(open("/etc/neutron/plugins/evs/evs_plugin.ini")) |
|
117 if config.has_option("EVS", 'evs_controller'): |
|
118 config_suh = config.get("EVS", 'evs_controller') |
|
119 else: |
|
120 config_suh = 'ssh://evsuser@localhost' |
|
121 suh = config_suh.split('://') |
|
122 if len(suh) != 2 or suh[0] != 'ssh' or not suh[1].strip(): |
|
123 raise SystemExit(_("Specified evs_controller is invalid")) |
|
124 uh = suh[1].split('@') |
|
125 if len(uh) != 2 or not uh[0].strip() or not uh[1].strip(): |
|
126 raise SystemExit(_("'user' and 'hostname' need to be specified " |
|
127 "for evs_controller")) |
|
128 try: |
|
129 rc = radcon.connect_ssh(uh[1], user=uh[0]) |
|
130 except: |
|
131 raise SystemExit(_("Cannot connect to EVS Controller")) |
|
132 try: |
|
133 evs_contr = rc.get_object(evsc.EVSController()) |
|
134 except: |
|
135 raise SystemExit(_("Could not retrieve EVS info from EVS Controller")) |
|
136 evsinfo = evs_contr.getEVSInfo() |
|
137 if not evsinfo: |
|
138 raise SystemExit(_("No data to migrate")) |
|
139 |
|
140 config.readfp(open("/etc/neutron/neutron.conf")) |
|
141 if config.has_option("database", 'connection'): |
|
142 SQL_CONNECTION = config.get("database", 'connection') |
|
143 else: |
|
144 SQL_CONNECTION = 'sqlite:////var/lib/neutron/neutron.sqlite' |
|
145 |
|
146 conf = cfg.CONF |
|
147 db_options.set_defaults(cfg.CONF, |
|
148 connection=SQL_CONNECTION, |
|
149 sqlite_db='', max_pool_size=10, |
|
150 max_overflow=20, pool_timeout=10) |
|
151 |
|
152 neutron_engine = sa.create_engine(SQL_CONNECTION) |
|
153 router_port_ids = {} |
|
154 |
|
155 for e in evsinfo: |
|
156 ext_ro = False |
|
157 for p in e.props: |
|
158 if p.name == 'OpenStack:router:external' and p.value == 'True': |
|
159 ext_ro = True |
|
160 # Populate networks table |
|
161 n = { |
|
162 'tenant_id': e.tenantname, |
|
163 'id': e.uuid, |
|
164 'name': e.name, |
|
165 'status': 'ACTIVE', |
|
166 'admin_state_up': True, |
|
167 'shared': False |
|
168 } |
|
169 nw = {'network': n} |
|
170 dup = create_db_network(nw, neutron_engine, ext_ro) |
|
171 if dup: |
|
172 continue # No need to iterate over subnets and ports |
|
173 |
|
174 # Populate subnets table |
|
175 for i in e.ipnets: |
|
176 cidr = None |
|
177 gateway_ip = None |
|
178 enable_dhcp = None |
|
179 dns = [] |
|
180 host = [] |
|
181 start = [] |
|
182 for p in i.props: |
|
183 if p.name == 'subnet': |
|
184 cidr = p.value |
|
185 elif p.name == 'defrouter': |
|
186 gateway_ip = p.value |
|
187 elif p.name == 'OpenStack:enable_dhcp': |
|
188 enable_dhcp = p.value == 'True' |
|
189 elif p.name == 'OpenStack:dns_nameservers': |
|
190 dns = p.value.split(',') |
|
191 elif p.name == 'OpenStack:host_routes': |
|
192 hh = p.value.split(',') |
|
193 for h in range(0, len(hh), 2): |
|
194 d = {hh[h]: hh[h+1]} |
|
195 host.append(d) |
|
196 elif p.name == 'pool': |
|
197 ss = p.value.split(',') |
|
198 for s in ss: |
|
199 if '-' in s: |
|
200 d = {'start': s.split('-')[0], |
|
201 'end': s.split('-')[1]} |
|
202 start.append(d) |
|
203 else: |
|
204 d = {'start': s, 'end': s} |
|
205 start.append(d) |
|
206 ip_version = 4 if i.ipvers == evsc.IPVersion.IPV4 else 6 |
|
207 |
|
208 if i.name.startswith(i.uuid[:8]): |
|
209 # Skip autogenerated names |
|
210 name = None |
|
211 else: |
|
212 name = i.name |
|
213 s = { |
|
214 'tenant_id': i.tenantname, |
|
215 'id': i.uuid, |
|
216 'name': name, |
|
217 'network_id': e.uuid, |
|
218 'ip_version': ip_version, |
|
219 'cidr': cidr, |
|
220 'gateway_ip': gateway_ip, |
|
221 'enable_dhcp': enable_dhcp, |
|
222 'shared': False, |
|
223 'allocation_pools': start, |
|
224 'dns_nameservers': dns, |
|
225 'host_routes': host |
|
226 } |
|
227 |
|
228 sub = {'subnet': s} |
|
229 create_db_subnet(sub) |
|
230 |
|
231 # Populate ports table |
|
232 for j in e.vports: |
|
233 device_owner = '' |
|
234 device_id = '' |
|
235 mac_address = None |
|
236 ipaddr = None |
|
237 for v in j.props: |
|
238 if v.name == 'OpenStack:device_owner': |
|
239 device_owner = v.value |
|
240 if v.value in ('network:router_interface', |
|
241 'network:router_gateway'): |
|
242 router_port_ids[j.uuid] = v.value |
|
243 elif v.name == 'OpenStack:device_id': |
|
244 device_id = v.value |
|
245 elif v.name == 'macaddr': |
|
246 mac_address = v.value |
|
247 elif v.name == 'ipaddr': |
|
248 ipaddr = v.value.split('/')[0] |
|
249 if j.name.startswith(j.uuid[:8]): |
|
250 # Skip autogenerated names |
|
251 name = None |
|
252 else: |
|
253 name = j.name |
|
254 |
|
255 p = { |
|
256 'tenant_id': j.tenantname, |
|
257 'id': j.uuid, |
|
258 'name': name, |
|
259 'network_id': e.uuid, |
|
260 'mac_address': mac_address, |
|
261 'admin_state_up': True, |
|
262 'status': 'ACTIVE', |
|
263 'device_id': device_id, |
|
264 'device_owner': device_owner, |
|
265 'fixed_ips': [{'subnet_id': e.ipnets[0].uuid, |
|
266 'ip_address': ipaddr}] |
|
267 } |
|
268 port = {'port': p} |
|
269 create_db_port(port) |
|
270 |
|
271 # Change the schema of the floatingips and routers tables by doing |
|
272 # the following: |
|
273 # Fetch the floatingip, router entry using EVS API, |
|
274 # Temporarily store the information, |
|
275 # Delete floatingip, router entry, |
|
276 # Remove floatingip, router as a constraint from existing tables, |
|
277 # Drop the routers, floatingips table, |
|
278 # Add router, floatingip entry using Neutron API |
|
279 |
|
280 # Importing locally because this module ends up importing neutron.wsgi |
|
281 # which causes RAD to hang |
|
282 from neutron.db import l3_db |
|
283 havana_api.configure_db() |
|
284 session = havana_api.get_session() |
|
285 |
|
286 # Fetch the floatingip entry using EVS API |
|
287 query = session.query(havana_api.FloatingIP) |
|
288 floatingips = query.all() |
|
289 fl = [] |
|
290 if floatingips: |
|
291 for f in floatingips: |
|
292 fi = { |
|
293 'id': f['id'], |
|
294 'floating_ip_address': f['floating_ip_address'], |
|
295 'floating_network_id': f['floating_network_id'], |
|
296 'floating_port_id': f['floating_port_id'], |
|
297 'fixed_port_id': f['fixed_port_id'], |
|
298 'fixed_ip_address': f['fixed_ip_address'], |
|
299 'tenant_id': f['tenant_id'], |
|
300 'router_id': f['router_id'], |
|
301 } |
|
302 fl.append(fi) |
|
303 |
|
304 # Delete floatingip entry |
|
305 ctxt = ctx.get_admin_context() |
|
306 ctxt = havana_api.get_evs_context(ctxt) |
|
307 with ctxt.session.begin(subtransactions=True): |
|
308 cm_db_inst = common_db_mixin.CommonDbMixin() |
|
309 query = common_db_mixin.CommonDbMixin._model_query(cm_db_inst, |
|
310 ctxt, |
|
311 havana_api. |
|
312 FloatingIP) |
|
313 for fip in query: |
|
314 ctxt.session.delete(fip) |
|
315 |
|
316 # Fetch the router entry using EVS API |
|
317 query = session.query(havana_api.Router) |
|
318 routers = [] |
|
319 try: |
|
320 routers = query.all() |
|
321 except sa.exc.OperationalError: |
|
322 pass |
|
323 if routers: |
|
324 for r in routers: |
|
325 router_id = r['id'] |
|
326 rt = { |
|
327 'tenant_id': r['tenant_id'], |
|
328 'id': r['id'], |
|
329 'name': r['name'], |
|
330 'admin_state_up': r['admin_state_up'], |
|
331 'gw_port_id': r['gw_port_id'], |
|
332 'status': 'ACTIVE' |
|
333 } |
|
334 |
|
335 # Delete router entry |
|
336 ctxt = ctx.get_admin_context() |
|
337 ctxt = havana_api.get_evs_context(ctxt) |
|
338 with ctxt.session.begin(subtransactions=True): |
|
339 cm_db_inst = common_db_mixin.CommonDbMixin() |
|
340 query = common_db_mixin.CommonDbMixin._model_query(cm_db_inst, |
|
341 ctxt, |
|
342 havana_api. |
|
343 Router) |
|
344 router = query.filter(havana_api.Router.id == router_id).one() |
|
345 ctxt.session.delete(router) |
|
346 |
|
347 engine = sa.create_engine(SQL_CONNECTION) |
|
348 meta = MetaData() |
|
349 conn = engine.connect() |
|
350 trans = conn.begin() |
|
351 meta.reflect(engine) |
|
352 |
|
353 # Remove router as a constraint from existing tables, |
|
354 # Drop the routers table to remove old schema |
|
355 for t in meta.tables.values(): |
|
356 for fk in t.foreign_keys: |
|
357 if fk.column.table.name == "routers": |
|
358 engine.execute(DropConstraint(fk.constraint)) |
|
359 for t in meta.tables.values(): |
|
360 if t.name == "routers": |
|
361 t.drop(bind=conn) |
|
362 |
|
363 # Remove floatingip as a constraint from existing tables, |
|
364 # Drop the floatingip table to remove old schema |
|
365 for t in meta.tables.values(): |
|
366 for fk in t.foreign_keys: |
|
367 if fk.column.table.name == "floatingips": |
|
368 engine.execute(DropConstraint(fk.constraint)) |
|
369 for t in meta.tables.values(): |
|
370 if t.name == "floatingips": |
|
371 t.drop(bind=conn) |
|
372 conn.close() |
|
373 |
|
374 # Add the routers and floatingips using the schema in l3_db.py |
|
375 |
|
376 setattr(l3_db.Router, 'enable_snat', sa.Column(sa.Boolean, |
|
377 default=True, server_default=sql.true(), nullable=False)) |
|
378 neutron_engine = sa.create_engine(SQL_CONNECTION) |
|
379 model_base.BASEV2.metadata.bind = neutron_engine |
|
380 model_base.BASEV2.metadata.create_all(neutron_engine) |
|
381 if routers: |
|
382 ctxt = ctx.get_admin_context() |
|
383 with ctxt.session.begin(subtransactions=True): |
|
384 router_db = l3_db.Router(id=router_id, |
|
385 tenant_id=r['tenant_id'], |
|
386 name=rt['name'], |
|
387 admin_state_up=rt['admin_state_up'], |
|
388 gw_port_id=rt['gw_port_id'], |
|
389 status="ACTIVE") |
|
390 ctxt.session.add(router_db) |
|
391 print "\nrouter=%s updated" % rt['name'] |
|
392 with ctxt.session.begin(subtransactions=True): |
|
393 for i, j in router_port_ids.iteritems(): |
|
394 router_port = l3_db.RouterPort( |
|
395 port_id=i, |
|
396 router_id=router_id, |
|
397 port_type=j) |
|
398 ctxt.session.add(router_port) |
|
399 |
|
400 if floatingips: |
|
401 ctxt = ctx.get_admin_context() |
|
402 with ctxt.session.begin(subtransactions=True): |
|
403 for i in fl: |
|
404 fl_db = l3_db.FloatingIP( |
|
405 id=i['id'], |
|
406 floating_ip_address=i['floating_ip_address'], |
|
407 floating_network_id=i['floating_network_id'], |
|
408 floating_port_id=i['floating_port_id'], |
|
409 fixed_port_id=i['fixed_port_id'], |
|
410 fixed_ip_address=i['fixed_ip_address'], |
|
411 router_id=i['router_id'], |
|
412 tenant_id=i['tenant_id']) |
|
413 ctxt.session.add(fl_db) |
|
414 print "\nfloatingip=%s updated" % i['floating_ip_address'] |
|
415 |
|
416 print "\nEnd Migration." |
|
417 |
|
418 |
|
419 if __name__ == '__main__': |
|
420 main() |