Add support for updating the config file

This commit adds a new flag to the verify_tempest_config.py script,
'-u' that will update the config file from values found during the
verification process.

Partially implements bp config-verification

Change-Id: I20b8bca7d3368b56da0a0496ed7aa3c1a6eacc9e
diff --git a/tools/verify_tempest_config.py b/tools/verify_tempest_config.py
index 1726beb..14dd0c2 100755
--- a/tools/verify_tempest_config.py
+++ b/tools/verify_tempest_config.py
@@ -14,11 +14,14 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import argparse
 import json
+import os
 import sys
 import urlparse
 
 import httplib2
+from six.moves import configparser
 
 from tempest import clients
 from tempest import config
@@ -26,18 +29,50 @@
 
 CONF = config.CONF
 RAW_HTTP = httplib2.Http()
+CONF_FILE = None
+OUTFILE = sys.stdout
 
 
-def verify_glance_api_versions(os):
+def _get_config_file():
+    default_config_dir = os.path.join(os.path.abspath(
+        os.path.dirname(os.path.dirname(__file__))), "etc")
+    default_config_file = "tempest.conf"
+
+    conf_dir = os.environ.get('TEMPEST_CONFIG_DIR', default_config_dir)
+    conf_file = os.environ.get('TEMPEST_CONFIG', default_config_file)
+    path = os.path.join(conf_dir, conf_file)
+    fd = open(path, 'rw')
+    return fd
+
+
+def change_option(option, group, value):
+    config_parse = configparser.SafeConfigParser()
+    config_parse.optionxform = str
+    config_parse.readfp(CONF_FILE)
+    if not config_parse.has_section(group):
+        config_parse.add_section(group)
+    config_parse.set(group, option, str(value))
+    global OUTFILE
+    config_parse.write(OUTFILE)
+
+
+def print_and_or_update(option, group, value, update):
+    print('Config option %s in group %s should be changed to: %s'
+          % (option, group, value))
+    if update:
+        change_option(option, group, value)
+
+
+def verify_glance_api_versions(os, update):
     # Check glance api versions
     __, versions = os.image_client.get_versions()
     if CONF.image_feature_enabled.api_v1 != ('v1.1' in versions or 'v1.0' in
                                              versions):
-        print('Config option image api_v1 should be change to: %s' % (
-            not CONF.image_feature_enabled.api_v1))
+        print_and_or_update('api_v1', 'image_feature_enabled',
+                            not CONF.image_feature_enabled.api_v1, update)
     if CONF.image_feature_enabled.api_v2 != ('v2.0' in versions):
-        print('Config option image api_v2 should be change to: %s' % (
-            not CONF.image_feature_enabled.api_v2))
+        print_and_or_update('api_v2', 'image_feature_enabled',
+                            not CONF.image_feature_enabled.api_v2, update)
 
 
 def _get_api_versions(os, service):
@@ -59,33 +94,33 @@
     return versions
 
 
-def verify_keystone_api_versions(os):
+def verify_keystone_api_versions(os, update):
     # Check keystone api versions
     versions = _get_api_versions(os, 'keystone')
     if CONF.identity_feature_enabled.api_v2 != ('v2.0' in versions):
-        print('Config option identity api_v2 should be change to %s' % (
-            not CONF.identity_feature_enabled.api_v2))
+        print_and_or_update('api_v2', 'identity_feature_enabled',
+                            not CONF.identity_feature_enabled.api_v2, update)
     if CONF.identity_feature_enabled.api_v3 != ('v3.0' in versions):
-        print('Config option identity api_v3 should be change to %s' % (
-            not CONF.identity_feature_enabled.api_v3))
+        print_and_or_update('api_v3', 'identity_feature_enabled',
+                            not CONF.identity_feature_enabled.api_v3, update)
 
 
-def verify_nova_api_versions(os):
+def verify_nova_api_versions(os, update):
     versions = _get_api_versions(os, 'nova')
     if CONF.compute_feature_enabled.api_v3 != ('v3.0' in versions):
-        print('Config option compute api_v3 should be change to: %s' % (
-              not CONF.compute_feature_enabled.api_v3))
+        print_and_or_update('api_v3', 'compute_feature_enabled',
+                            not CONF.compute_feature_enabled.api_v3, update)
 
 
-def verify_cinder_api_versions(os):
+def verify_cinder_api_versions(os, update):
     # Check cinder api versions
     versions = _get_api_versions(os, 'cinder')
     if CONF.volume_feature_enabled.api_v1 != ('v1.0' in versions):
-        print('Config option volume api_v2 should be change to: %s' % (
-              not CONF.volume_feature_enabled.api_v1))
+        print_and_or_update('api_v1', 'volume_feature_enabled',
+                            not CONF.volume_feature_enabled.api_v1, update)
     if CONF.volume_feature_enabled.api_v2 != ('v2.0' in versions):
-        print('Config option volume api_v2 should be change to: %s' % (
-              not CONF.volume_feature_enabled.api_v2))
+        print_and_or_update('api_v2', 'volume_feature_enabled',
+                            not CONF.volume_feature_enabled.api_v2, update)
 
 
 def get_extension_client(os, service):
@@ -139,7 +174,7 @@
         results[service] = {}
     extensions_opt = get_enabled_extensions(service)
     if extensions_opt[0] == 'all':
-        results[service]['extensions'] = 'all'
+        results[service]['extensions'] = extensions
         return results
     # Verify that all configured extensions are actually enabled
     for extension in extensions_opt:
@@ -152,24 +187,54 @@
     return results
 
 
-def display_results(results):
+def display_results(results, update, replace):
+    update_dict = {
+        'swift': 'object-storage-feature-enabled',
+        'nova': 'compute-feature-enabled',
+        'nova_v3': 'compute-feature-enabled',
+        'cinder': 'volume-feature-enabled',
+        'neutron': 'network-feature-enabled',
+    }
     for service in results:
         # If all extensions are specified as being enabled there is no way to
         # verify this so we just assume this to be true
         if results[service].get('extensions'):
-            continue
-        extension_list = get_enabled_extensions(service)
-        for extension in results[service]:
-            if not results[service][extension]:
-                if extension in extension_list:
-                    print("%s extension: %s should not be included in the list"
-                          " of enabled extensions" % (service, extension))
+            if replace:
+                output_list = results[service].get('extensions')
+            else:
+                output_list = ['all']
+        else:
+            extension_list = get_enabled_extensions(service)
+            output_list = []
+            for extension in results[service]:
+                if not results[service][extension]:
+                    if extension in extension_list:
+                        print("%s extension: %s should not be included in the "
+                              "list of enabled extensions" % (service,
+                                                              extension))
+                    else:
+                        print("%s extension: %s should be included in the list"
+                              " of enabled extensions" % (service, extension))
+                        output_list.append(extension)
                 else:
-                    print("%s extension: %s should be included in the list of "
-                          "enabled extensions" % (service, extension))
+                    output_list.append(extension)
+        if update:
+            # Sort List
+            output_list.sort()
+            # Convert list to a string
+            output_string = ', '.join(output_list)
+            if service == 'swift':
+                change_option('discoverable_apis', update_dict[service],
+                              output_string)
+            elif service == 'nova_v3':
+                change_option('api_v3_extensions', update_dict[service],
+                              output_string)
+            else:
+                change_option('api_extensions', update_dict[service],
+                              output_string)
 
 
-def check_service_availability(os):
+def check_service_availability(os, update):
     services = []
     avail_services = []
     codename_match = {
@@ -207,21 +272,59 @@
                     print('Endpoint type %s not found either disable service '
                           '%s or fix the catalog_type in the config file' % (
                           catalog_type, codename_match[cfgname]))
+                    if update:
+                        change_option(codename_match[cfgname],
+                                      'service_available', False)
             else:
                 if not getattr(CONF.service_available,
                                codename_match[cfgname]):
                     print('Endpoint type %s is available, service %s should be'
                           ' set as available in the config file.' % (
                           catalog_type, codename_match[cfgname]))
+                    if update:
+                        change_option(codename_match[cfgname],
+                                      'service_available', True)
                 else:
                     avail_services.append(codename_match[cfgname])
     return avail_services
 
 
+def parse_args():
+    parser = argparse.ArgumentParser()
+    parser.add_argument('-u', '--update', action='store_true',
+                        help='Update the config file with results from api '
+                             'queries. This assumes whatever is set in the '
+                             'config file is incorrect. In the case of '
+                             'endpoint checks where it could either be the '
+                             'incorrect catalog type or the service available '
+                             'option the service available option is assumed '
+                             'to be incorrect and is thus changed')
+    parser.add_argument('-o', '--output',
+                        help="Output file to write an updated config file to. "
+                             "This has to be a separate file from the "
+                             "original config file. If one isn't specified "
+                             "with -u the new config file will be printed to "
+                             "STDOUT")
+    parser.add_argument('-r', '--replace-ext', action='store_true',
+                        help="If specified the all option will be replaced "
+                             "with a full list of extensions")
+    args = parser.parse_args()
+    return args
+
+
 def main(argv):
     print('Running config verification...')
+    opts = parse_args()
+    update = opts.update
+    replace = opts.replace_ext
+    global CONF_FILE
+    global OUTFILE
+    if update:
+        CONF_FILE = _get_config_file()
+        if opts.output:
+            OUTFILE = open(opts.output, 'w+')
     os = clients.ComputeAdminManager(interface='json')
-    services = check_service_availability(os)
+    services = check_service_availability(os, update)
     results = {}
     for service in ['nova', 'nova_v3', 'cinder', 'neutron', 'swift']:
         if service == 'nova_v3' and 'nova' not in services:
@@ -229,11 +332,14 @@
         elif service not in services:
             continue
         results = verify_extensions(os, service, results)
-    verify_keystone_api_versions(os)
-    verify_glance_api_versions(os)
-    verify_nova_api_versions(os)
-    verify_cinder_api_versions(os)
-    display_results(results)
+    verify_keystone_api_versions(os, update)
+    verify_glance_api_versions(os, update)
+    verify_nova_api_versions(os, update)
+    verify_cinder_api_versions(os, update)
+    display_results(results, update, replace)
+    if CONF_FILE:
+        CONF_FILE.close()
+    OUTFILE.close()
 
 
 if __name__ == "__main__":