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