components/openstack/common/files/openstack_common.py
changeset 4287 aba3ed31b37a
child 4351 c3f50d5f75d2
equal deleted inserted replaced
4286:a7f757b12343 4287:aba3ed31b37a
       
     1 # Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
       
     2 #
       
     3 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
       
     4 #    not use this file except in compliance with the License. You may obtain
       
     5 #    a copy of the License at
       
     6 #
       
     7 #         http://www.apache.org/licenses/LICENSE-2.0
       
     8 #
       
     9 #    Unless required by applicable law or agreed to in writing, software
       
    10 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
       
    11 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
       
    12 #    License for the specific language governing permissions and limitations
       
    13 #    under the License.
       
    14 
       
    15 
       
    16 """ openstack_upgrade - common functions used by the various OpenStack
       
    17 components to facilitate upgrading of configuration files and MySQL
       
    18 databases/tables (if in use)
       
    19 """
       
    20 
       
    21 from ConfigParser import NoOptionError
       
    22 from datetime import datetime
       
    23 import errno
       
    24 import glob
       
    25 import os
       
    26 import shutil
       
    27 import time
       
    28 
       
    29 import iniparse
       
    30 
       
    31 
       
    32 def create_backups(directory):
       
    33     """ create backups of each configuration file which also has a .new file
       
    34     from the upgrade.
       
    35     """
       
    36 
       
    37     today = datetime.now().strftime("%Y%m%d%H%M%S")
       
    38     cwd = os.getcwd()
       
    39     os.chdir(directory)
       
    40     for new_file in glob.glob('*.new'):
       
    41         # copy the old conf file to a backup
       
    42         old_file = new_file.replace('.new', '')
       
    43         try:
       
    44             shutil.copy2(old_file, old_file + '.' + today)
       
    45         except (IOError, OSError):
       
    46             print 'unable to create a backup of %s' % old_file
       
    47 
       
    48     os.chdir(cwd)
       
    49 
       
    50 
       
    51 def update_mapping(section, key, mapping):
       
    52     """ look for deprecated variables and, if found, convert it to the new
       
    53     section/key.
       
    54     """
       
    55 
       
    56     if (section, key) in mapping:
       
    57         print "Deprecated value found: [%s] %s" % (section, key)
       
    58         section, key = mapping[(section, key)]
       
    59         if section is None and key is None:
       
    60             print "Removing from configuration"
       
    61         else:
       
    62             print "Updating to: [%s] %s" % (section, key)
       
    63     return section, key
       
    64 
       
    65 
       
    66 def alter_mysql_tables(engine):
       
    67     """ Convert MySQL tables to use utf8
       
    68     """
       
    69 
       
    70     import MySQLdb
       
    71 
       
    72     for _none in range(5):
       
    73         try:
       
    74             db = MySQLdb.connect(host=engine.url.host,
       
    75                                  user=engine.url.username,
       
    76                                  passwd=engine.url.password,
       
    77                                  db=engine.url.database)
       
    78             break
       
    79         except MySQLdb.OperationalError as err:
       
    80             # mysql is not ready. sleep for 2 more seconds
       
    81             time.sleep(2)
       
    82     else:
       
    83         print "Unable to connect to MySQL:  %s" % err
       
    84         print ("Please verify MySQL is properly configured and online "
       
    85                "before using svcadm(1M) to clear this service.")
       
    86         raise RuntimeError
       
    87 
       
    88     cursor = db.cursor()
       
    89     cursor.execute("SHOW table status")
       
    90     cursor.execute("ALTER DATABASE %s CHARACTER SET = 'utf8'" %
       
    91                    engine.url.database)
       
    92     cursor.execute("ALTER DATABASE %s COLLATE = 'utf8_general_ci'" %
       
    93                    engine.url.database)
       
    94     cursor.execute("SHOW tables")
       
    95     res = cursor.fetchall()
       
    96     if res:
       
    97         cursor.execute("SET foreign_key_checks = 0")
       
    98         for item in res:
       
    99             cursor.execute("ALTER TABLE %s.%s CONVERT TO "
       
   100                            "CHARACTER SET 'utf8', COLLATE 'utf8_general_ci'"
       
   101                            % (engine.url.database, item[0]))
       
   102         cursor.execute("SET foreign_key_checks = 1")
       
   103         db.commit()
       
   104         db.close()
       
   105 
       
   106 
       
   107 def modify_conf(old_file, mapping=None, exception_list=None):
       
   108     """ Copy over all uncommented options from the old configuration file.  In
       
   109     addition, look for deprecated section/keys and convert them to the new
       
   110     section/key.
       
   111     """
       
   112 
       
   113     new_file = old_file + '.new'
       
   114 
       
   115     # open the previous version
       
   116     old = iniparse.ConfigParser()
       
   117     old.readfp(open(old_file))
       
   118 
       
   119     # open the new version
       
   120     new = iniparse.ConfigParser()
       
   121     try:
       
   122         new.readfp(open(new_file))
       
   123     except IOError as err:
       
   124         if err.errno == errno.ENOENT:
       
   125             # The upgrade did not deliver a .new file so, return
       
   126             print "%s not found - continuing with %s" % (new_file, old_file)
       
   127             return
       
   128         else:
       
   129             raise
       
   130     print "\nupdating %s" % old_file
       
   131 
       
   132     # walk every single section for uncommented options
       
   133     default_items = set(old.items('DEFAULT'))
       
   134     for old_section in old.sections() + ['DEFAULT']:
       
   135 
       
   136         # DEFAULT items show up in every section so remove them
       
   137         if old_section != 'DEFAULT':
       
   138             section_items = set(old.items(old_section)) - default_items
       
   139         else:
       
   140             section_items = default_items
       
   141 
       
   142         for old_key, value in section_items:
       
   143             # Look for deprecated section/keys
       
   144             if mapping is not None:
       
   145                 new_section, new_key = update_mapping(old_section, old_key,
       
   146                                                       mapping)
       
   147                 if new_section is None and new_key is None:
       
   148                     # option is deprecated so continue
       
   149                     continue
       
   150             else:
       
   151                 # no deprecated values for this file so just copy the values
       
   152                 # over
       
   153                 new_section, new_key = old_section, old_key
       
   154 
       
   155             # Look for exceptions
       
   156             if exception_list is not None:
       
   157                 if (new_section, new_key) in exception_list:
       
   158                     if (new_section != 'DEFAULT' and
       
   159                         not new.has_section(new_section)):
       
   160                         new.add_section(new_section)
       
   161                     print "Preserving [%s] %s = %s" % \
       
   162                         (new_section, new_key, value)
       
   163                     new.set(new_section, new_key, value)
       
   164                     continue
       
   165 
       
   166             if new_section != 'DEFAULT' and not new.has_section(new_section):
       
   167                 new.add_section(new_section)
       
   168 
       
   169             # print to the log when a value for old_section.old_key is changing
       
   170             # to a new value
       
   171             try:
       
   172                 new_value = new.get(new_section, new_key)
       
   173                 if new_value != value and '%SERVICE' not in new_value:
       
   174                     print "Changing [%s] %s:\n- %s\n+ %s" % \
       
   175                         (old_section, old_key, value, new_value)
       
   176                     print
       
   177             except NoOptionError:
       
   178                 # the new configuration file does not have this option set so
       
   179                 # just continue
       
   180                 pass
       
   181 
       
   182             # Only copy the old value to the new conf file if the entry doesn't
       
   183             # exist in the new file or if it contains '%SERVICE'
       
   184             if not new.has_option(new_section, new_key) or \
       
   185                '%SERVICE' in new.get(new_section, new_key):
       
   186                 new.set(new_section, new_key, value)
       
   187 
       
   188     # copy the new conf file in place
       
   189     with open(old_file, 'wb+') as fh:
       
   190         new.write(fh)
       
   191 
       
   192 
       
   193 def move_conf(original_file, new_file, mapping):
       
   194     """ move each entry in mapping from the original file to the new file.
       
   195     """
       
   196     # open the original file
       
   197     original = iniparse.ConfigParser()
       
   198     original.readfp(open(original_file))
       
   199 
       
   200     # open the new file
       
   201     new = iniparse.ConfigParser()
       
   202     new.readfp(open(new_file))
       
   203 
       
   204     # The mappings dictionary look similar to the deprecation mappings:
       
   205     # (original_section, original_key): (new_section, new_key)
       
   206     for (original_section, original_key) in mapping:
       
   207         try:
       
   208             original_value = original.get(original_section, original_key)
       
   209         except NoOptionError:
       
   210             # the original file does not contain this mapping so continue
       
   211             continue
       
   212 
       
   213         new_section, new_key = mapping.get((original_section, original_key))
       
   214 
       
   215         if new_section != 'DEFAULT' and not new.has_section(new_section):
       
   216             new.add_section(new_section)
       
   217 
       
   218         print 'Moving [%s] %s from %s to [%s] %s in %s' % \
       
   219             (original_section, original_key, original_file,
       
   220              new_section, new_key, new_file)
       
   221 
       
   222         # set the option in the new file
       
   223         new.set(new_section, new_key, original_value)
       
   224 
       
   225         # remove the option from the old file
       
   226         original.remove_option(original_section, original_key)
       
   227 
       
   228     with open(original_file, 'wb+') as fh:
       
   229         original.write(fh)
       
   230 
       
   231     with open(new_file, 'wb+') as fh:
       
   232         new.write(fh)