components/openstack/neutron/files/evs/migrate/evs-neutron-migration.py
changeset 3998 5bd484384122
child 4047 9ab66cc72745
equal deleted inserted replaced
3997:0ca3f3d6c919 3998:5bd484384122
       
     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()