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