""" Copyright (c) 2015-2017 Wind River Systems, Inc. SPDX-License-Identifier: Apache-2.0 """ import ConfigParser import os import readline import sys import textwrap from common import constants from common import log from common.exceptions import (BackupFail, RestoreFail, UserQuit, CloneFail) from configutilities import lag_mode_to_str, Network, validate from configutilities import ConfigFail from configutilities import DEFAULT_CONFIG, REGION_CONFIG, SUBCLOUD_CONFIG from configutilities import MGMT_TYPE, HP_NAMES, DEFAULT_NAMES from configassistant import ConfigAssistant, check_for_ssh_parent import backup_restore import utils import clone # Temporary file for building cgcs_config TEMP_CGCS_CONFIG_FILE = "/tmp/cgcs_config" LOG = log.get_logger(__name__) def parse_system_config(config_file): """Parse system config file""" system_config = ConfigParser.RawConfigParser() try: system_config.read(config_file) except Exception as e: LOG.exception(e) raise ConfigFail("Error parsing system config file") # Dump configuration for debugging # for section in config.sections(): # print "Section: %s" % section # for (name, value) in config.items(section): # print "name: %s, value: %s" % (name, value) return system_config def configure_management_interface(region_config, config_type=REGION_CONFIG): """Bring up management interface """ mgmt_network = Network() if region_config.has_section('CLM_NETWORK'): naming_type = HP_NAMES else: naming_type = DEFAULT_NAMES try: mgmt_network.parse_config(region_config, config_type, MGMT_TYPE, min_addresses=8, naming_type=naming_type) except ConfigFail: raise except Exception as e: LOG.exception("Error parsing configuration file") raise ConfigFail("Error parsing configuration file: %s" % e) try: # Remove interface config files currently installed utils.remove_interface_config_files() # Create the management interface configuration files. # Code based on ConfigAssistant._write_interface_config_management parameters = utils.get_interface_config_static( mgmt_network.start_address, mgmt_network.cidr, mgmt_network.gateway_address) if mgmt_network.logical_interface.lag_interface: management_interface = 'bond0' else: management_interface = mgmt_network.logical_interface.ports[0] if mgmt_network.vlan: management_interface_name = "%s.%s" % (management_interface, mgmt_network.vlan) utils.write_interface_config_vlan( management_interface_name, mgmt_network.logical_interface.mtu, parameters) # underlying interface has no additional parameters parameters = None else: management_interface_name = management_interface if mgmt_network.logical_interface.lag_interface: utils.write_interface_config_bond( management_interface, mgmt_network.logical_interface.mtu, lag_mode_to_str(mgmt_network.logical_interface.lag_mode), None, constants.LAG_MIIMON_FREQUENCY, mgmt_network.logical_interface.ports[0], mgmt_network.logical_interface.ports[1], parameters) else: utils.write_interface_config_ethernet( management_interface, mgmt_network.logical_interface.mtu, parameters) # Restart networking with the new management interface configuration utils.restart_networking() # Send a GARP for floating address. Doing this to help in # cases where we are re-installing in a lab and another node # previously held the floating address. if mgmt_network.cidr.version == 4: utils.send_interface_garp(management_interface_name, mgmt_network.start_address) except Exception: LOG.exception("Failed to configure management interface") raise ConfigFail("Failed to configure management interface") def create_cgcs_config_file(output_file, system_config, services, endpoints, domains, config_type=REGION_CONFIG, validate_only=False): """ Create cgcs_config file or just perform validation of the system_config if validate_only=True. :param output_file: filename of output cgcs_config file :param system_config: system configuration :param services: keystone services (not used if validate_only) :param endpoints: keystone endpoints (not used if validate_only) :param domains: keystone domains (not used if validate_only) :param config_type: specify region, subcloud or standard config :param validate_only: used to validate the input system_config :return: """ cgcs_config = None if not validate_only: cgcs_config = ConfigParser.RawConfigParser() cgcs_config.optionxform = str # general error checking, if not validate_only cgcs config data is returned validate(system_config, config_type, cgcs_config) # Region configuration: services, endpoints and domain if config_type in [REGION_CONFIG, SUBCLOUD_CONFIG] and not validate_only: # The services and endpoints are not available in the validation phase region_1_name = system_config.get('SHARED_SERVICES', 'REGION_NAME') keystone_service_name = system_config.get('SHARED_SERVICES', 'KEYSTONE_SERVICE_NAME') keystone_service_type = system_config.get('SHARED_SERVICES', 'KEYSTONE_SERVICE_TYPE') keystone_service_id = services.get_service_id(keystone_service_name, keystone_service_type) keystone_admin_url = endpoints.get_service_url(region_1_name, keystone_service_id, "admin") keystone_internal_url = endpoints.get_service_url(region_1_name, keystone_service_id, "internal") keystone_public_url = endpoints.get_service_url(region_1_name, keystone_service_id, "public") cgcs_config.set('cREGION', 'KEYSTONE_AUTH_URI', keystone_internal_url) cgcs_config.set('cREGION', 'KEYSTONE_IDENTITY_URI', keystone_admin_url) cgcs_config.set('cREGION', 'KEYSTONE_ADMIN_URI', keystone_admin_url) cgcs_config.set('cREGION', 'KEYSTONE_INTERNAL_URI', keystone_internal_url) cgcs_config.set('cREGION', 'KEYSTONE_PUBLIC_URI', keystone_public_url) is_glance_cached = False if system_config.has_option('SHARED_SERVICES', 'GLANCE_CACHED'): if (system_config.get('SHARED_SERVICES', 'GLANCE_CACHED').upper() == 'TRUE'): is_glance_cached = True cgcs_config.set('cREGION', 'GLANCE_CACHED', is_glance_cached) if (system_config.has_option('SHARED_SERVICES', 'GLANCE_SERVICE_NAME') and not is_glance_cached): glance_service_name = system_config.get('SHARED_SERVICES', 'GLANCE_SERVICE_NAME') glance_service_type = system_config.get('SHARED_SERVICES', 'GLANCE_SERVICE_TYPE') glance_region_name = region_1_name glance_service_id = services.get_service_id(glance_service_name, glance_service_type) glance_internal_url = endpoints.get_service_url(glance_region_name, glance_service_id, "internal") glance_public_url = endpoints.get_service_url(glance_region_name, glance_service_id, "public") cgcs_config.set('cREGION', 'GLANCE_ADMIN_URI', glance_internal_url) cgcs_config.set('cREGION', 'GLANCE_PUBLIC_URI', glance_public_url) cgcs_config.set('cREGION', 'GLANCE_INTERNAL_URI', glance_internal_url) # The domains are not available in the validation phase heat_admin_domain = system_config.get('REGION_2_SERVICES', 'HEAT_ADMIN_DOMAIN') cgcs_config.set('cREGION', 'HEAT_ADMIN_DOMAIN_NAME', heat_admin_domain) # If primary region is non-TiC and keystone entries already created, # the flag will tell puppet not to create them. if (system_config.has_option('REGION_2_SERVICES', 'CREATE') and system_config.get('REGION_2_SERVICES', 'CREATE') == 'Y'): cgcs_config.set('cREGION', 'REGION_SERVICES_CREATE', 'True') # System Timezone configuration if system_config.has_option('SYSTEM', 'TIMEZONE'): timezone = system_config.get('SYSTEM', 'TIMEZONE') if not os.path.isfile("/usr/share/zoneinfo/%s" % timezone): raise ConfigFail( "Timezone file %s does not exist" % timezone) # Dump results for debugging # for section in cgcs_config.sections(): # print "[%s]" % section # for (name, value) in cgcs_config.items(section): # print "%s=%s" % (name, value) if not validate_only: # Write config file with open(output_file, 'w') as config_file: cgcs_config.write(config_file) def configure_system(config_file): """Configure the system""" # Parse the system config file print "Parsing system configuration file... ", system_config = parse_system_config(config_file) print "DONE" # Validate the system config file print "Validating system configuration file... ", try: create_cgcs_config_file(None, system_config, None, None, None, DEFAULT_CONFIG, validate_only=True) except ConfigParser.Error as e: raise ConfigFail("Error parsing configuration file %s: %s" % (config_file, e)) print "DONE" # Create cgcs_config file print "Creating config apply file... ", try: create_cgcs_config_file(TEMP_CGCS_CONFIG_FILE, system_config, None, None, None, DEFAULT_CONFIG) except ConfigParser.Error as e: raise ConfigFail("Error parsing configuration file %s: %s" % (config_file, e)) print "DONE" def show_help(): print ("Usage: %s\n" "Perform system configuration\n" "\nThe default action is to perform the initial configuration for " "the system. The following options are also available:\n" "--config-file Perform configuration using INI file\n" "--backup Backup configuration using the given " "name\n" "--clone-iso Clone and create an image with " "the given file name\n" "--clone-status Status of the last installation of " "cloned image\n" "--restore-system Restore system configuration from backup " "file with\n" " the given name, full path required\n" "--restore-images Restore images from backup file with the " "given name,\n" " full path required\n" "--restore-compute Restore controller-0 compute function " "for All-In-One system,\n" " controller-0 will reboot\n" % sys.argv[0]) def show_help_lab_only(): print ("Usage: %s\n" "Perform initial configuration\n" "\nThe following options are for lab use only:\n" "--answerfile Apply the configuration from the specified " "file without\n" " any validation or user interaction\n" "--default Apply default configuration with no NTP or " "DNS server\n" " configuration (suitable for testing in a " "virtual\n" " environment)\n" "--archive-dir Directory to store the archive in\n" "--provision Provision initial system data only\n" % sys.argv[0]) def no_complete(text, state): return def main(): options = {} answerfile = None backup_name = None archive_dir = constants.BACKUPS_PATH do_default_config = False do_backup = False do_system_restore = False do_images_restore = False do_compute_restore = False do_clone = False do_non_interactive = False do_provision = False system_config_file = "/home/wrsroot/system_config" # Disable completion as the default completer shows python commands readline.set_completer(no_complete) # remove any previous config fail flag file if os.path.exists(constants.CONFIG_FAIL_FILE) is True: os.remove(constants.CONFIG_FAIL_FILE) if os.environ.get('CGCS_LABMODE'): options['labmode'] = True arg = 1 while arg < len(sys.argv): if sys.argv[arg] == "--answerfile": arg += 1 if arg < len(sys.argv): answerfile = sys.argv[arg] else: print "--answerfile option requires a file to be specified" exit(1) elif sys.argv[arg] == "--backup": arg += 1 if arg < len(sys.argv): backup_name = sys.argv[arg] else: print "--backup requires the name of the backup" exit(1) do_backup = True elif sys.argv[arg] == "--restore-system": arg += 1 if arg < len(sys.argv): backup_name = sys.argv[arg] else: print "--restore-system requires the filename of the backup" exit(1) do_system_restore = True elif sys.argv[arg] == "--restore-images": arg += 1 if arg < len(sys.argv): backup_name = sys.argv[arg] else: print "--restore-images requires the filename of the backup" exit(1) do_images_restore = True elif sys.argv[arg] == "--restore-compute": do_compute_restore = True elif sys.argv[arg] == "--archive-dir": arg += 1 if arg < len(sys.argv): archive_dir = sys.argv[arg] else: print "--archive-dir requires a directory" exit(1) elif sys.argv[arg] == "--clone-iso": arg += 1 if arg < len(sys.argv): backup_name = sys.argv[arg] else: print "--clone-iso requires the name of the image" exit(1) do_clone = True elif sys.argv[arg] == "--clone-status": clone.clone_status() exit(0) elif sys.argv[arg] == "--default": do_default_config = True elif sys.argv[arg] == "--config-file": arg += 1 if arg < len(sys.argv): system_config_file = sys.argv[arg] else: print "--config-file requires the filename of the config file" exit(1) do_non_interactive = True elif sys.argv[arg] in ["--help", "-h", "-?"]: show_help() exit(1) elif sys.argv[arg] == "--labhelp": show_help_lab_only() exit(1) elif sys.argv[arg] == "--provision": do_provision = True else: print "Invalid option. Use --help for more information." exit(1) arg += 1 if [do_backup, do_system_restore, do_images_restore, do_compute_restore, do_clone, do_default_config, do_non_interactive].count(True) > 1: print "Invalid combination of options selected" exit(1) if answerfile and [do_backup, do_system_restore, do_images_restore, do_compute_restore, do_clone, do_default_config, do_non_interactive].count(True) > 0: print "The --answerfile option cannot be used with the selected option" exit(1) log.configure() # Reduce the printk console log level to avoid noise during configuration printk_levels = '' with open('/proc/sys/kernel/printk', 'r') as f: printk_levels = f.readline() temp_printk_levels = '3' + printk_levels[1:] with open('/proc/sys/kernel/printk', 'w') as f: f.write(temp_printk_levels) if not do_backup and not do_clone: check_for_ssh_parent() try: if do_backup: backup_restore.backup(backup_name, archive_dir) print "\nBackup complete" elif do_system_restore: backup_restore.restore_system(backup_name) print "\nSystem restore complete" elif do_images_restore: backup_restore.restore_images(backup_name) print "\nImages restore complete" elif do_compute_restore: backup_restore.restore_compute() elif do_clone: clone.clone(backup_name, archive_dir) print "\nCloning complete" elif do_provision: assistant = ConfigAssistant(**options) assistant.provision(answerfile) else: if do_non_interactive: if not os.path.isfile(system_config_file): raise ConfigFail("Config file %s does not exist." % system_config_file) if (os.path.exists(constants.CGCS_CONFIG_FILE) or os.path.exists(constants.CONFIG_PERMDIR) or os.path.exists( constants.INITIAL_CONFIG_COMPLETE_FILE)): raise ConfigFail("Configuration has already been done " "and cannot be repeated.") configure_system(system_config_file) answerfile = TEMP_CGCS_CONFIG_FILE assistant = ConfigAssistant(**options) assistant.configure(answerfile, do_default_config) print "\nConfiguration was applied\n" print textwrap.fill( "Please complete any out of service commissioning steps " "with system commands and unlock controller to proceed.", 80) assistant.check_required_interfaces_status() except KeyboardInterrupt: print "\nAborting configuration" except BackupFail as e: print "\nBackup failed: {}".format(e) except RestoreFail as e: print "\nRestore failed: {}".format(e) except ConfigFail as e: print "\nConfiguration failed: {}".format(e) except CloneFail as e: print "\nCloning failed: {}".format(e) except UserQuit: print "\nAborted configuration" finally: if os.path.isfile(TEMP_CGCS_CONFIG_FILE): os.remove(TEMP_CGCS_CONFIG_FILE) # Restore the printk console log level with open('/proc/sys/kernel/printk', 'w') as f: f.write(printk_levels)