components/openstack/neutron/files/neutron-upgrade
branchs11u2-sru
changeset 4156 4b1def16fe9b
child 4049 150852e281c4
child 4207 787ed839f409
equal deleted inserted replaced
4146:097063f324c0 4156:4b1def16fe9b
       
     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 from ConfigParser import NoOptionError
       
    18 from datetime import datetime
       
    19 import errno
       
    20 import glob
       
    21 import os
       
    22 import shutil
       
    23 from subprocess import check_call, Popen, PIPE
       
    24 import sys
       
    25 import time
       
    26 import traceback
       
    27 
       
    28 import iniparse
       
    29 import smf_include
       
    30 import sqlalchemy
       
    31 
       
    32 
       
    33 NEUTRON_CONF_MAPPINGS = {
       
    34     # Deprecated group/name
       
    35     ('DEFAULT', 'rabbit_durable_queues'): ('DEFAULT', 'amqp_durable_queues'),
       
    36     ('rpc_notifier2', 'topics'): ('DEFAULT', 'notification_topics'),
       
    37     ('DEFAULT', 'matchmaker_ringfile'): ('matchmaker_ring', 'ringfile'),
       
    38     # As of Juno, EVS now uses the standard quota driver
       
    39     ('quotas', 'quota_driver'): (None, None)
       
    40 }
       
    41 
       
    42 DHCP_AGENT_MAPPINGS = {
       
    43     # Deprecated group/name
       
    44     ('DEFAULT', 'dnsmasq_dns_server'): ('DEFAULT', 'dnsmasq_dns_servers'),
       
    45 }
       
    46 
       
    47 EVS_PLUGIN_MAPPINGS = {
       
    48     # No longer referenced by the service
       
    49     ('DATABASE', 'sql_connection'): (None, None),
       
    50 }
       
    51 
       
    52 
       
    53 def update_mapping(section, key, mapping):
       
    54     """ look for deprecated variables and, if found, convert it to the new
       
    55     section/key.
       
    56     """
       
    57 
       
    58     if (section, key) in mapping:
       
    59         print "Deprecated value found: [%s] %s" % (section, key)
       
    60         section, key = mapping[(section, key)]
       
    61         if section is None and key is None:
       
    62             print "Removing from configuration"
       
    63         else:
       
    64             print "Updating to: [%s] %s" % (section, key)
       
    65     return section, key
       
    66 
       
    67 
       
    68 def alter_mysql_tables(engine):
       
    69     """ Convert MySQL tables to use utf8
       
    70     """
       
    71 
       
    72     import MySQLdb
       
    73 
       
    74     for _none in range(5):
       
    75         try:
       
    76             db = MySQLdb.connect(host=engine.url.host,
       
    77                                  user=engine.url.username,
       
    78                                  passwd=engine.url.password,
       
    79                                  db=engine.url.database)
       
    80             break
       
    81         except MySQLdb.OperationalError as err:
       
    82             # mysql is not ready. sleep for 2 more seconds
       
    83             time.sleep(2)
       
    84     else:
       
    85         print "Unable to connect to MySQL:  %s" % err
       
    86         print ("Please verify MySQL is properly configured and online "
       
    87                "before using svcadm(1M) to clear this service.")
       
    88         sys.exit(smf_include.SMF_EXIT_ERR_FATAL)
       
    89 
       
    90     cursor = db.cursor()
       
    91     cursor.execute("ALTER DATABASE %s CHARACTER SET = 'utf8'" %
       
    92                    engine.url.database)
       
    93     cursor.execute("ALTER DATABASE %s COLLATE = 'utf8_general_ci'" %
       
    94                    engine.url.database)
       
    95     cursor.execute("SHOW tables")
       
    96     res = cursor.fetchall()
       
    97     if res:
       
    98         cursor.execute("SET foreign_key_checks = 0")
       
    99         for item in res:
       
   100             cursor.execute("ALTER TABLE %s.%s CONVERT TO "
       
   101                            "CHARACTER SET 'utf8', COLLATE 'utf8_general_ci'"
       
   102                            % (engine.url.database, item[0]))
       
   103         cursor.execute("SET foreign_key_checks = 1")
       
   104         db.commit()
       
   105         db.close()
       
   106 
       
   107 
       
   108 def modify_conf(old_file, mapping=None):
       
   109     """ Copy over all uncommented options from the old configuration file.  In
       
   110     addition, look for deprecated section/keys and convert them to the new
       
   111     section/key.
       
   112     """
       
   113 
       
   114     new_file = old_file + '.new'
       
   115 
       
   116     # open the previous version
       
   117     old = iniparse.ConfigParser()
       
   118     old.readfp(open(old_file))
       
   119 
       
   120     # open the new version
       
   121     new = iniparse.ConfigParser()
       
   122     try:
       
   123         new.readfp(open(new_file))
       
   124     except IOError as err:
       
   125         if err.errno == errno.ENOENT:
       
   126             # The upgrade did not deliver a .new file so, return
       
   127             print "%s not found - continuing with %s" % (new_file, old_file)
       
   128             return
       
   129         else:
       
   130             raise
       
   131     print "\nupdating %s" % old_file
       
   132 
       
   133     # walk every single section for uncommented options
       
   134     default_items = set(old.items('DEFAULT'))
       
   135     for section in old.sections() + ['DEFAULT']:
       
   136 
       
   137         # DEFAULT items show up in every section so remove them
       
   138         if section != 'DEFAULT':
       
   139             section_items = set(old.items(section)) - default_items
       
   140         else:
       
   141             section_items = default_items
       
   142 
       
   143         for key, value in section_items:
       
   144             # keep a copy of the old value
       
   145             oldvalue = value
       
   146 
       
   147             if mapping is not None:
       
   148                 section, key = update_mapping(section, key, mapping)
       
   149 
       
   150                 if section is None and key is None:
       
   151                     # option is deprecated so continue
       
   152                     continue
       
   153 
       
   154             if not new.has_section(section):
       
   155                 if section != 'DEFAULT':
       
   156                     new.add_section(section)
       
   157 
       
   158             # print to the log when a value for the same section.key is
       
   159             # changing to a new value
       
   160             try:
       
   161                 new_value = new.get(section, key)
       
   162                 if new_value != value and '%SERVICE' not in new_value:
       
   163                     print "Changing [%s] %s:\n- %s\n+ %s" % \
       
   164                         (section, key, oldvalue, new_value)
       
   165                     print
       
   166             except NoOptionError:
       
   167                 # the new configuration file does not have this option set so
       
   168                 # just continue
       
   169                 pass
       
   170 
       
   171             # Only copy the old value to the new conf file if the entry doesn't
       
   172             # exist or if it contains '%SERVICE'
       
   173             if not new.has_option(section, key) or \
       
   174                '%SERVICE' in new.get(section, key):
       
   175                 new.set(section, key, value)
       
   176 
       
   177     # copy the old conf file to a backup
       
   178     today = datetime.now().strftime("%Y%m%d%H%M%S")
       
   179     shutil.copy2(old_file, old_file + '.' + today)
       
   180 
       
   181     # copy the new conf file in place
       
   182     with open(old_file, 'wb+') as fh:
       
   183         new.write(fh)
       
   184 
       
   185 
       
   186 def start():
       
   187     # pull out the current version of config/upgrade-id
       
   188     p = Popen(['/usr/bin/svcprop', '-p', 'config/upgrade-id',
       
   189                os.environ['SMF_FMRI']], stdout=PIPE, stderr=PIPE)
       
   190     curr_ver, _err = p.communicate()
       
   191     curr_ver = curr_ver.strip()
       
   192 
       
   193     # extract the openstack-upgrade-id from the pkg
       
   194     p = Popen(['/usr/bin/pkg', 'contents', '-H', '-t', 'set', '-o', 'value',
       
   195                '-a', 'name=openstack.upgrade-id',
       
   196                'pkg:/cloud/openstack/neutron'], stdout=PIPE, stderr=PIPE)
       
   197     pkg_ver, _err = p.communicate()
       
   198     pkg_ver = pkg_ver.strip()
       
   199 
       
   200     if curr_ver == pkg_ver:
       
   201         # No need to upgrade
       
   202         sys.exit(smf_include.SMF_EXIT_OK)
       
   203 
       
   204     # look for any .new files
       
   205     if glob.glob('/etc/neutron/*.new'):
       
   206         # the versions are different, so perform an upgrade
       
   207         # modify the configuration files
       
   208         modify_conf('/etc/neutron/api-paste.ini')
       
   209         modify_conf('/etc/neutron/dhcp_agent.ini', DHCP_AGENT_MAPPINGS)
       
   210         modify_conf('/etc/neutron/l3_agent.ini')
       
   211         modify_conf('/etc/neutron/neutron.conf', NEUTRON_CONF_MAPPINGS)
       
   212         modify_conf('/etc/neutron/plugins/evs/evs_plugin.ini',
       
   213                     EVS_PLUGIN_MAPPINGS)
       
   214 
       
   215     config = iniparse.RawConfigParser()
       
   216     config.read('/etc/neutron/neutron.conf')
       
   217     # In certain cases the database section does not exist and the
       
   218     # default database chosen is sqlite.
       
   219     if config.has_section('database'):
       
   220         db_connection = config.get('database', 'connection')
       
   221         engine = sqlalchemy.create_engine(db_connection)
       
   222 
       
   223         # migrate EVS to Neutron db
       
   224         if engine.url.username != '%SERVICE_USER%':
       
   225             check_call(['/usr/lib/neutron/evs-neutron-migration'])
       
   226             print "EVS to Neutron migration complete"
       
   227 
       
   228             if engine.url.drivername == 'mysql':
       
   229                 alter_mysql_tables(engine)
       
   230                 print "altered character set to utf8 in neutron tables"
       
   231 
       
   232             check_call(['/usr/bin/neutron-db-manage', '--config-file',
       
   233                         '/etc/neutron/neutron.conf', '--config-file',
       
   234                         '/etc/neutron/plugins/evs/evs_plugin.ini', 'stamp',
       
   235                         'juno'])
       
   236 
       
   237     # update the current version
       
   238     check_call(['/usr/sbin/svccfg', '-s', os.environ['SMF_FMRI'], 'setprop',
       
   239                'config/upgrade-id', '=', pkg_ver])
       
   240     check_call(['/usr/sbin/svccfg', '-s', os.environ['SMF_FMRI'], 'refresh'])
       
   241 
       
   242     sys.exit(smf_include.SMF_EXIT_OK)
       
   243 
       
   244 
       
   245 if __name__ == '__main__':
       
   246     os.putenv('LC_ALL', 'C')
       
   247     try:
       
   248         smf_include.smf_main()
       
   249     except Exception as err:
       
   250         print 'Unknown error:  %s' % err
       
   251         print
       
   252         traceback.print_exc(file=sys.stdout)
       
   253         sys.exit(smf_include.SMF_EXIT_ERR_FATAL)