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