components/openstack/glance/files/glance-upgrade
changeset 4287 aba3ed31b37a
parent 4181 3ac4ce913bec
child 5405 66fd59fecd68
equal deleted inserted replaced
4286:a7f757b12343 4287:aba3ed31b37a
    12 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    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
    13 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
    14 #    License for the specific language governing permissions and limitations
    14 #    License for the specific language governing permissions and limitations
    15 #    under the License.
    15 #    under the License.
    16 
    16 
    17 from ConfigParser import NoOptionError
       
    18 from datetime import datetime
       
    19 import errno
       
    20 import glob
    17 import glob
    21 import os
    18 import os
    22 import shutil
       
    23 from subprocess import check_call, Popen, PIPE
    19 from subprocess import check_call, Popen, PIPE
    24 import sys
    20 import sys
    25 import time
       
    26 import traceback
    21 import traceback
    27 
    22 
    28 import iniparse
    23 import iniparse
    29 import smf_include
    24 import smf_include
    30 import sqlalchemy
    25 import sqlalchemy
       
    26 
       
    27 from openstack_common import alter_mysql_tables, create_backups, modify_conf
    31 
    28 
    32 
    29 
    33 GLANCE_API_MAPPINGS = {
    30 GLANCE_API_MAPPINGS = {
    34     # Deprecated group/name
    31     # Deprecated group/name
    35     ('DEFAULT', 'db_backend'): ('database', 'backend'),
    32     ('DEFAULT', 'db_backend'): ('database', 'backend'),
    50     ('DEFAULT', 'sql_max_overflow'): ('database', 'max_overflow'),
    47     ('DEFAULT', 'sql_max_overflow'): ('database', 'max_overflow'),
    51     ('DATABASE', 'sqlalchemy_max_overflow'): ('database', 'max_overflow'),
    48     ('DATABASE', 'sqlalchemy_max_overflow'): ('database', 'max_overflow'),
    52     ('DEFAULT', 'sql_connection_debug'): ('database', 'connection_debug'),
    49     ('DEFAULT', 'sql_connection_debug'): ('database', 'connection_debug'),
    53     ('DEFAULT', 'sql_connection_trace'): ('database', 'connection_trace'),
    50     ('DEFAULT', 'sql_connection_trace'): ('database', 'connection_trace'),
    54     ('DATABASE', 'sqlalchemy_pool_timeout'): ('database', 'pool_timeout'),
    51     ('DATABASE', 'sqlalchemy_pool_timeout'): ('database', 'pool_timeout'),
       
    52     ('DEFAULT', 'filesystem_store_datadir'):
       
    53         ('glance_store', 'filesystem_store_datadir'),
       
    54     ('DEFAULT', 'swift_store_auth_version'):
       
    55          ('glance_store', 'swift_store_auth_version'),
       
    56     ('DEFAULT', 'swift_store_auth_address'):
       
    57          ('glance_store', 'swift_store_auth_address'),
       
    58     ('DEFAULT', 'swift_store_user'): ('glance_store', 'swift_store_user'),
       
    59     ('DEFAULT', 'swift_store_key'): ('glance_store', 'swift_store_key'),
       
    60     ('DEFAULT', 'swift_store_container'):
       
    61          ('glance_store', 'swift_store_container'),
       
    62     ('DEFAULT', 'swift_store_create_container_on_put'):
       
    63          ('glance_store', 'swift_store_create_container_on_put'),
       
    64     ('DEFAULT', 'swift_store_large_object_size'):
       
    65          ('glance_store', 'swift_store_large_object_size'),
       
    66     ('DEFAULT', 'swift_store_large_object_chunk_size'):
       
    67          ('glance_store', 'swift_store_large_object_chunk_size'),
       
    68     ('DEFAULT', 'swift_enable_snet'): ('glance_store', 'swift_enable_snet'),
       
    69     ('DEFAULT', 's3_store_host'): ('glance_store', 's3_store_host'),
       
    70     ('DEFAULT', 's3_store_access_key'):
       
    71          ('glance_store', 's3_store_access_key'),
       
    72     ('DEFAULT', 's3_store_secret_key'):
       
    73          ('glance_store', 's3_store_secret_key'),
       
    74     ('DEFAULT', 's3_store_bucket'): ('glance_store', 's3_store_bucket'),
       
    75     ('DEFAULT', 's3_store_create_bucket_on_put'):
       
    76          ('glance_store', 's3_store_create_bucket_on_put'),
       
    77     ('DEFAULT', 'sheepdog_store_address'):
       
    78          ('glance_store', 'sheepdog_store_address'),
       
    79     ('DEFAULT', 'sheepdog_store_port'):
       
    80          ('glance_store', 'sheepdog_store_port'),
       
    81     ('DEFAULT', 'sheepdog_store_chunk_size'):
       
    82          ('glance_store', 'sheepdog_store_chunk_size'),
    55 }
    83 }
       
    84 
       
    85 GLANCE_API_EXCEPTIONS = [
       
    86     ('DEFAULT', 'bind_host'),
       
    87     ('DEFAULT', 'bind_port'),
       
    88     ('DEFAULT', 'log_file'),
       
    89     ('DEFAULT', 'backlog'),
       
    90     ('DEFAULT', 'workers'),
       
    91     ('DEFAULT', 'registry_host'),
       
    92     ('DEFAULT', 'registry_port'),
       
    93     ('DEFAULT', 'registry_client_protocol'),
       
    94     ('DEFAULT', 'rabbit_host'),
       
    95     ('DEFAULT', 'rabbit_port'),
       
    96     ('DEFAULT', 'rabbit_use_ssl'),
       
    97     ('DEFAULT', 'rabbit_userid'),
       
    98     ('DEFAULT', 'rabbit_password'),
       
    99     ('DEFAULT', 'rabbit_virtual_host'),
       
   100     ('DEFAULT', 'rabbit_notification_exchange'),
       
   101     ('DEFAULT', 'rabbit_notification_topic'),
       
   102     ('DEFAULT', 'rabbit_durable_queues'),
       
   103     ('DEFAULT', 'qpid_notification_exchange'),
       
   104     ('DEFAULT', 'qpid_notification_topic'),
       
   105     ('DEFAULT', 'qpid_hostname'),
       
   106     ('DEFAULT', 'qpid_port'),
       
   107     ('DEFAULT', 'qpid_usernamd'),
       
   108     ('DEFAULT', 'qpid_password'),
       
   109     ('DEFAULT', 'qpid_sasl_mechanisms'),
       
   110     ('DEFAULT', 'qpid_reconnect_timeout'),
       
   111     ('DEFAULT', 'qpid_reconnect_limit'),
       
   112     ('DEFAULT', 'qpid_reconnect_interval_min'),
       
   113     ('DEFAULT', 'qpid_reconnect_interval_max'),
       
   114     ('DEFAULT', 'qpid_reconnect_interval'),
       
   115     ('DEFAULT', 'qpid_heartbeat'),
       
   116     ('DEFAULT', 'qpid_protocol'),
       
   117     ('DEFAULT', 'qpid_tcp_nodelay'),
       
   118     ('DEFAULT', 'delayed_delete'),
       
   119     ('DEFAULT', 'scrub_time'),
       
   120     ('DEFAULT', 'scrubber_datadir'),
       
   121     ('DEFAULT', 'image_cache_dir'),
       
   122     ('database', 'connection'),
       
   123     ('keystone_authtoken', 'auth_uri'),
       
   124     ('keystone_authtoken', 'identity_uri'),
       
   125     ('keystone_authtoken', 'admin_tenant_name'),
       
   126     ('keystone_authtoken', 'admin_user'),
       
   127     ('keystone_authtoken', 'admin_password'),
       
   128     ('keystone_authtoken', 'revocation_cache_time'),
       
   129     ('keystone_authtoken', 'signing_dir'),
       
   130     ('paste_deploy', 'flavor'),
       
   131     ('glance_store', 'filesystem_store_datadir'),
       
   132     ('glance_store', 'swift_store_auth_version'),
       
   133     ('glance_store', 'swift_store_auth_address'),
       
   134     ('glance_store', 'swift_store_user'),
       
   135     ('glance_store', 'swift_store_key'),
       
   136     ('glance_store', 'swift_store_container'),
       
   137     ('glance_store', 'swift_store_create_container_on_put'),
       
   138     ('glance_store', 'swift_store_large_object_size'),
       
   139     ('glance_store', 'swift_store_large_object_chunk_size'),
       
   140     ('glance_store', 'swift_enable_snet'),
       
   141     ('glance_store', 's3_store_host'),
       
   142     ('glance_store', 's3_store_access_key'),
       
   143     ('glance_store', 's3_store_secret_key'),
       
   144     ('glance_store', 's3_store_bucket'),
       
   145     ('glance_store', 's3_store_create_bucket_on_put'),
       
   146     ('glance_store', 'sheepdog_store_address'),
       
   147     ('glance_store', 'sheepdog_store_port'),
       
   148     ('glance_store', 'sheepdog_store_chunk_size'),
       
   149 ]
       
   150 
       
   151 GLANCE_CACHE_EXCEPTIONS = [
       
   152     ('DEFAULT', 'log_file'),
       
   153     ('DEFAULT', 'image_cache_dir'),
       
   154     ('DEFAULT', 'image_cache_stall_time'),
       
   155     ('DEFAULT', 'image_cache_max_size'),
       
   156     ('DEFAULT', 'registry_host'),
       
   157     ('DEFAULT', 'registry_port'),
       
   158     ('DEFAULT', 'auth_url'),
       
   159     ('DEFAULT', 'admin_tenant_name'),
       
   160     ('DEFAULT', 'admin_user'),
       
   161     ('DEFAULT', 'admin_password'),
       
   162     ('DEFAULT', 'filesystem_store_datadir'),
       
   163     ('DEFAULT', 'swift_store_auth_version'),
       
   164     ('DEFAULT', 'swift_store_auth_address'),
       
   165     ('DEFAULT', 'swift_store_user'),
       
   166     ('DEFAULT', 'swift_store_key'),
       
   167     ('DEFAULT', 'swift_store_container'),
       
   168     ('DEFAULT', 'swift_store_create_container_on_put'),
       
   169     ('DEFAULT', 'swift_store_large_object_size'),
       
   170     ('DEFAULT', 'swift_store_large_object_chunk_size'),
       
   171     ('DEFAULT', 'swift_enable_snet'),
       
   172     ('DEFAULT', 's3_store_host'),
       
   173     ('DEFAULT', 's3_store_access_key'),
       
   174     ('DEFAULT', 's3_store_secret_key'),
       
   175     ('DEFAULT', 's3_store_bucket'),
       
   176     ('DEFAULT', 's3_store_create_bucket_on_put'),
       
   177 ]
    56 
   178 
    57 GLANCE_REGISTRY_MAPPINGS = {
   179 GLANCE_REGISTRY_MAPPINGS = {
    58     # Deprecated group/name
   180     # Deprecated group/name
    59     ('DEFAULT', 'db_backend'): ('database', 'backend'),
   181     ('DEFAULT', 'db_backend'): ('database', 'backend'),
    60     ('DEFAULT', 'sql_connection'): ('database', 'connection'),
   182     ('DEFAULT', 'sql_connection'): ('database', 'connection'),
    76     ('DEFAULT', 'sql_connection_debug'): ('database', 'connection_debug'),
   198     ('DEFAULT', 'sql_connection_debug'): ('database', 'connection_debug'),
    77     ('DEFAULT', 'sql_connection_trace'): ('database', 'connection_trace'),
   199     ('DEFAULT', 'sql_connection_trace'): ('database', 'connection_trace'),
    78     ('DATABASE', 'sqlalchemy_pool_timeout'): ('database', 'pool_timeout'),
   200     ('DATABASE', 'sqlalchemy_pool_timeout'): ('database', 'pool_timeout'),
    79 }
   201 }
    80 
   202 
    81 
   203 GLANCE_REGISTRY_EXCEPTIONS = [
    82 def update_mapping(section, key, mapping):
   204     ('DEFAULT', 'bind_host'),
    83     """ look for deprecated variables and, if found, convert it to the new
   205     ('DEFAULT', 'bind_host'),
    84     section/key.
   206     ('DEFAULT', 'log_file'),
    85     """
   207     ('DEFAULT', 'backlog'),
    86 
   208     ('DEFAULT', 'workers'),
    87     if (section, key) in mapping:
   209     ('DEFAULT', 'api_limit_max'),
    88         print "Deprecated value found: [%s] %s" % (section, key)
   210     ('DEFAULT', 'limit_param_default'),
    89         section, key = mapping[(section, key)]
   211     ('DEFAULT', 'rabbit_host'),
    90         if section is None and key is None:
   212     ('DEFAULT', 'rabbit_port'),
    91             print "Removing from configuration"
   213     ('DEFAULT', 'rabbit_use_ssl'),
    92         else:
   214     ('DEFAULT', 'rabbit_userid'),
    93             print "Updating to: [%s] %s" % (section, key)
   215     ('DEFAULT', 'rabbit_password'),
    94     return section, key
   216     ('DEFAULT', 'rabbit_virtual_host'),
    95 
   217     ('DEFAULT', 'rabbit_notification_exchange'),
    96 
   218     ('DEFAULT', 'rabbit_notification_topic'),
    97 def alter_mysql_tables(engine):
   219     ('DEFAULT', 'rabbit_durable_queues'),
    98     """ Convert MySQL tables to use utf8
   220     ('DEFAULT', 'qpid_notification_exchange'),
    99     """
   221     ('DEFAULT', 'qpid_notification_topic'),
   100 
   222     ('DEFAULT', 'qpid_hostname'),
   101     import MySQLdb
   223     ('DEFAULT', 'qpid_port'),
   102 
   224     ('DEFAULT', 'qpid_usernamd'),
   103     for _none in range(5):
   225     ('DEFAULT', 'qpid_password'),
   104         try:
   226     ('DEFAULT', 'qpid_sasl_mechanisms'),
   105             db = MySQLdb.connect(host=engine.url.host,
   227     ('DEFAULT', 'qpid_reconnect_timeout'),
   106                                  user=engine.url.username,
   228     ('DEFAULT', 'qpid_reconnect_limit'),
   107                                  passwd=engine.url.password,
   229     ('DEFAULT', 'qpid_reconnect_interval_min'),
   108                                  db=engine.url.database)
   230     ('DEFAULT', 'qpid_reconnect_interval_max'),
   109             break
   231     ('DEFAULT', 'qpid_reconnect_interval'),
   110         except MySQLdb.OperationalError as err:
   232     ('DEFAULT', 'qpid_heartbeat'),
   111             # mysql is not ready. sleep for 2 more seconds
   233     ('DEFAULT', 'qpid_protocol'),
   112             time.sleep(2)
   234     ('DEFAULT', 'qpid_tcp_nodelay'),
   113     else:
   235     ('database', 'connection'),
   114         print "Unable to connect to MySQL:  %s" % err
   236     ('keystone_authtoken', 'auth_uri'),
   115         print ("Please verify MySQL is properly configured and online "
   237     ('keystone_authtoken', 'identity_uri'),
   116                "before using svcadm(1M) to clear this service.")
   238     ('keystone_authtoken', 'admin_tenant_name'),
   117         sys.exit(smf_include.SMF_EXIT_ERR_FATAL)
   239     ('keystone_authtoken', 'admin_user'),
   118 
   240     ('keystone_authtoken', 'admin_password'),
   119     cursor = db.cursor()
   241     ('keystone_authtoken', 'signing_dir'),
   120     cursor.execute("ALTER DATABASE %s CHARACTER SET = 'utf8'" %
   242     ('paste_deploy', 'flavor'),
   121                    engine.url.database)
   243 ]
   122     cursor.execute("ALTER DATABASE %s COLLATE = 'utf8_general_ci'" %
   244 
   123                    engine.url.database)
   245 GLANCE_SCRUBBER_MAPPINGS = {
   124     cursor.execute("SHOW tables")
   246     # Deprecated group/name
   125     res = cursor.fetchall()
   247     ('DEFAULT', 'filesystem_store_datadir'):
   126     if res:
   248         ('glance_store', 'filesystem_store_datadir'),
   127         cursor.execute("SET foreign_key_checks = 0")
   249 }
   128         for item in res:
   250 
   129             cursor.execute("ALTER TABLE %s.%s CONVERT TO "
   251 GLANCE_SCRUBBER_EXCEPTIONS = [
   130                            "CHARACTER SET 'utf8', COLLATE 'utf8_general_ci'"
   252     ('DEFAULT', 'log_file'),
   131                            % (engine.url.database, item[0]))
   253     ('DEFAULT', 'wakeup_time'),
   132         cursor.execute("SET foreign_key_checks = 1")
   254     ('DEFAULT', 'scrubber_datadir'),
   133         db.commit()
   255     ('DEFAULT', 'cleanup_scrubber'),
   134         db.close()
   256     ('DEFAULT', 'cleanup_scrubber_time'),
   135 
   257     ('DEFAULT', 'registry_host'),
   136 
   258     ('DEFAULT', 'registry_port'),
   137 def modify_conf(old_file, mapping=None):
   259     ('DEFAULT', 'auth_url'),
   138     """ Copy over all uncommented options from the old configuration file.  In
   260     ('DEFAULT', 'admin_tenant_name'),
   139     addition, look for deprecated section/keys and convert them to the new
   261     ('DEFAULT', 'admin_user'),
   140     section/key.
   262     ('DEFAULT', 'admin_password'),
   141     """
   263     ('database', 'connection'),
   142 
   264     ('glance_store', 'filesystem_store_datadir'),
   143     new_file = old_file + '.new'
   265 ]
   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             oldsection = section
       
   176 
       
   177             if mapping is not None:
       
   178                 section, key = update_mapping(section, key, mapping)
       
   179 
       
   180                 if section is None and key is None:
       
   181                     # option is deprecated so continue
       
   182                     continue
       
   183 
       
   184             if not new.has_section(section):
       
   185                 if section != 'DEFAULT':
       
   186                     new.add_section(section)
       
   187 
       
   188             # print to the log when a value for the same section.key is
       
   189             # changing to a new value
       
   190             try:
       
   191                 new_value = new.get(section, key)
       
   192                 if new_value != value and '%SERVICE' not in new_value:
       
   193                     print "Changing [%s] %s:\n- %s\n+ %s" % \
       
   194                         (section, key, oldvalue, new_value)
       
   195                     print
       
   196             except NoOptionError:
       
   197                 # the new configuration file does not have this option set so
       
   198                 # just continue
       
   199                 pass
       
   200 
       
   201             # Only copy the old value to the new conf file if the entry doesn't
       
   202             # exist or if it contains '%SERVICE'
       
   203             if not new.has_option(section, key) or \
       
   204                '%SERVICE' in new.get(section, key):
       
   205                 new.set(section, key, value)
       
   206             section = oldsection
       
   207 
       
   208     # copy the old conf file to a backup
       
   209     today = datetime.now().strftime("%Y%m%d%H%M%S")
       
   210     shutil.copy2(old_file, old_file + '.' + today)
       
   211 
       
   212     # copy the new conf file in place
       
   213     with open(old_file, 'wb+') as fh:
       
   214         new.write(fh)
       
   215 
   266 
   216 
   267 
   217 def start():
   268 def start():
   218     # pull out the current version of config/upgrade-id
   269     # pull out the current version of config/upgrade-id
   219     p = Popen(['/usr/bin/svcprop', '-p', 'config/upgrade-id',
   270     p = Popen(['/usr/bin/svcprop', '-p', 'config/upgrade-id',
   234 
   285 
   235     # look for any .new files
   286     # look for any .new files
   236     if glob.glob('/etc/glance/*.new'):
   287     if glob.glob('/etc/glance/*.new'):
   237         # the versions are different, so perform an upgrade
   288         # the versions are different, so perform an upgrade
   238         # modify the configuration files
   289         # modify the configuration files
   239         modify_conf('/etc/glance/glance-api.conf', GLANCE_API_MAPPINGS)
   290 
       
   291         # backup all the old configuration files
       
   292         create_backups('/etc/glance')
       
   293 
       
   294         modify_conf('/etc/glance/glance-api.conf', GLANCE_API_MAPPINGS,
       
   295                     GLANCE_API_EXCEPTIONS)
   240         modify_conf('/etc/glance/glance-api-paste.ini')
   296         modify_conf('/etc/glance/glance-api-paste.ini')
   241         modify_conf('/etc/glance/glance-cache.conf')
   297         modify_conf('/etc/glance/glance-cache.conf',
       
   298                     exception_list=GLANCE_CACHE_EXCEPTIONS)
   242         modify_conf('/etc/glance/glance-registry.conf',
   299         modify_conf('/etc/glance/glance-registry.conf',
   243                     GLANCE_REGISTRY_MAPPINGS)
   300                     GLANCE_REGISTRY_MAPPINGS,
       
   301                     GLANCE_REGISTRY_EXCEPTIONS)
   244         modify_conf('/etc/glance/glance-registry-paste.ini')
   302         modify_conf('/etc/glance/glance-registry-paste.ini')
   245         modify_conf('/etc/glance/glance-scrubber.conf')
   303         modify_conf('/etc/glance/glance-scrubber.conf',
       
   304                     GLANCE_SCRUBBER_MAPPINGS,
       
   305                     GLANCE_SCRUBBER_EXCEPTIONS)
   246         modify_conf('/etc/glance/logging.conf')
   306         modify_conf('/etc/glance/logging.conf')
   247 
   307 
   248     config = iniparse.RawConfigParser()
   308     config = iniparse.RawConfigParser()
   249     config.read('/etc/glance/glance-api.conf')
   309     config.read('/etc/glance/glance-api.conf')
   250     # In certain cases the database section does not exist and the
   310     # In certain cases the database section does not exist and the
   260 
   320 
   261     # update the current version
   321     # update the current version
   262     check_call(['/usr/sbin/svccfg', '-s', os.environ['SMF_FMRI'], 'setprop',
   322     check_call(['/usr/sbin/svccfg', '-s', os.environ['SMF_FMRI'], 'setprop',
   263                'config/upgrade-id', '=', pkg_ver])
   323                'config/upgrade-id', '=', pkg_ver])
   264     check_call(['/usr/sbin/svccfg', '-s', os.environ['SMF_FMRI'], 'refresh'])
   324     check_call(['/usr/sbin/svccfg', '-s', os.environ['SMF_FMRI'], 'refresh'])
       
   325 
   265     sys.exit(smf_include.SMF_EXIT_OK)
   326     sys.exit(smf_include.SMF_EXIT_OK)
   266 
   327 
   267 
   328 
   268 if __name__ == '__main__':
   329 if __name__ == '__main__':
   269     os.putenv('LC_ALL', 'C')
   330     os.putenv('LC_ALL', 'C')
   270     try:
   331     try:
   271         smf_include.smf_main()
   332         smf_include.smf_main()
       
   333     except RuntimeError:
       
   334         sys.exit(smf_include.SMF_EXIT_ERR_FATAL)
   272     except Exception as err:
   335     except Exception as err:
   273         print 'Unknown error:  %s' % err
   336         print 'Unknown error:  %s' % err
   274         print
   337         print
   275         traceback.print_exc(file=sys.stdout)
   338         traceback.print_exc(file=sys.stdout)
   276         sys.exit(smf_include.SMF_EXIT_ERR_FATAL)
   339         sys.exit(smf_include.SMF_EXIT_ERR_FATAL)