def modify_config(args, detected_config): changes = False # Compare existing and detected config for each check plugin and write out the plugin config if changes for key, value in detected_config.iteritems(): if args.overwrite: changes = True if args.dry_run: continue else: agent_config.save_plugin_config(args.config_dir, key, args.user, value) else: old_config = agent_config.read_plugin_config_from_disk( args.config_dir, key) # merge old and new config, new has precedence if old_config is not None: agent_config.merge_by_name(value['instances'], old_config['instances']) # Sort before compare, if instances have no name the sort will fail making order changes significant try: value['instances'].sort(key=lambda k: k['name']) old_config['instances'].sort(key=lambda k: k['name']) except Exception: pass if value == old_config: # Don't write config if no change continue changes = True if args.dry_run: log.info( "Changes would be made to the config file for the {0} check plugin" .format(key)) else: agent_config.save_plugin_config(args.config_dir, key, args.user, value) return changes
def modify_config(args, detected_config): changes = False # Compare existing and detected config for each check plugin and write out the plugin config if changes for key, value in detected_config.iteritems(): if args.overwrite: changes = True if args.dry_run: continue else: agent_config.save_plugin_config(args.config_dir, key, args.user, value) else: old_config = agent_config.read_plugin_config_from_disk(args.config_dir, key) # merge old and new config, new has precedence if old_config is not None: agent_config.merge_by_name(value['instances'], old_config['instances']) # Sort before compare, if instances have no name the sort will fail making order changes significant try: value['instances'].sort(key=lambda k: k['name']) old_config['instances'].sort(key=lambda k: k['name']) except Exception: pass if value == old_config: # Don't write config if no change continue changes = True if args.dry_run: log.info("Changes would be made to the config file for the {0} check plugin".format(key)) else: agent_config.save_plugin_config(args.config_dir, key, args.user, value) return changes
def modify_config(args, detected_config): """Compare existing and detected config for each check plugin and write out the plugin config if there are changes """ modified_config = False for detection_plugin_name, new_config in detected_config.items(): if args.overwrite: modified_config = True if args.dry_run: continue else: agent_config.save_plugin_config(args.config_dir, detection_plugin_name, args.user, new_config) else: config = agent_config.read_plugin_config_from_disk(args.config_dir, detection_plugin_name) # merge old and new config, new has precedence if config is not None: # For HttpCheck, if the new input url has the same host and # port but a different protocol comparing with one of the # existing instances in http_check.yaml, we want to keep the # existing http check instance and replace the url with the # new protocol. If name in this instance is the same as the # url, we replace name with new url too. # For more details please see: # monasca-agent/docs/DeveloperDocs/agent_internals.md if detection_plugin_name == "http_check": # Save old http_check urls from config for later comparison config_urls = [i['url'] for i in config['instances'] if 'url' in i] # Check endpoint change, use new protocol instead # Note: config is possibly changed after running # check_endpoint_changes function. config = agent_config.check_endpoint_changes(new_config, config) agent_config.merge_by_name(new_config['instances'], config['instances']) # Sort before compare, if instances have no name the sort will # fail making order changes significant try: new_config['instances'].sort(key=lambda k: k['name']) config['instances'].sort(key=lambda k: k['name']) except Exception: pass if detection_plugin_name == "http_check": new_config_urls = [i['url'] for i in new_config['instances'] if 'url' in i] # Don't write config if no change if new_config_urls == config_urls and new_config == config: continue else: if new_config == config: continue modified_config = True if args.dry_run: log.info("Changes would be made to the config file for the {0}" " check plugin".format(detection_plugin_name)) else: agent_config.save_plugin_config(args.config_dir, detection_plugin_name, args.user, new_config) return modified_config
def modify_config(args, detected_config): """Compare existing and detected config for each check plugin and write out the plugin config if there are changes """ modified_config = False for detection_plugin_name, new_config in detected_config.iteritems(): if args.overwrite: modified_config = True if args.dry_run: continue else: agent_config.save_plugin_config(args.config_dir, detection_plugin_name, args.user, new_config) else: config = agent_config.read_plugin_config_from_disk(args.config_dir, detection_plugin_name) # merge old and new config, new has precedence if config is not None: # For HttpCheck, if the new input url has the same host and # port but a different protocol comparing with one of the # existing instances in http_check.yaml, we want to keep the # existing http check instance and replace the url with the # new protocol. If name in this instance is the same as the # url, we replace name with new url too. # For more details please see: # monasca-agent/docs/DeveloperDocs/agent_internals.md if detection_plugin_name == "http_check": # Save old http_check urls from config for later comparison config_urls = [i['url'] for i in config['instances'] if 'url' in i] # Check endpoint change, use new protocol instead # Note: config is possibly changed after running # check_endpoint_changes function. config = agent_config.check_endpoint_changes(new_config, config) agent_config.merge_by_name(new_config['instances'], config['instances']) # Sort before compare, if instances have no name the sort will # fail making order changes significant try: new_config['instances'].sort(key=lambda k: k['name']) config['instances'].sort(key=lambda k: k['name']) except Exception: pass if detection_plugin_name == "http_check": new_config_urls = [i['url'] for i in new_config['instances'] if 'url' in i] # Don't write config if no change if new_config_urls == config_urls and new_config == config: continue else: if new_config == config: continue modified_config = True if args.dry_run: log.info("Changes would be made to the config file for the {0}" " check plugin".format(detection_plugin_name)) else: agent_config.save_plugin_config(args.config_dir, detection_plugin_name, args.user, new_config) return modified_config
def main(argv=None): parser = argparse.ArgumentParser(description='Configure and setup the agent. In a full run it will detect running' + ' daemons then configure and start the agent.', formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument( '-u', '--username', help="Username used for keystone authentication. Required for basic configuration.") parser.add_argument( '-p', '--password', help="Password used for keystone authentication. Required for basic configuration.") parser.add_argument('--keystone_url', help="Keystone url. Required for basic configuration.") parser.add_argument('--project_name', help="Project name for keystone authentication", default='') parser.add_argument('--project_domain_id', help="Project domain id for keystone authentication", default='') parser.add_argument('--project_domain_name', help="Project domain name for keystone authentication", default='') parser.add_argument('--project_id', help="Keystone project id for keystone authentication", default='') parser.add_argument('--monasca_url', help="Monasca API url, if not defined the url is pulled from keystone", default='') parser.add_argument('--system_only', help="Setup the service but only configure the base config and system " + "metrics (cpu, disk, load, memory, network).", action="store_true", default=False) parser.add_argument('-d', '--detection_plugins', nargs='*', help="Skip base config and service setup and only configure this space separated list. " + "This assumes the base config has already run.") parser.add_argument('-a', '--detection_args', help="A string of arguments that will be passed to detection " + "plugins. Only certain detection plugins use arguments.") parser.add_argument('--check_frequency', help="How often to run metric collection in seconds", type=int, default=30) parser.add_argument('--dimensions', help="Additional dimensions to set for all metrics. A comma seperated list " + "of name/value pairs, 'name:value,name2:value2'") parser.add_argument('--ca_file', help="Sets the path to the ca certs file if using certificates. " + "Required only if insecure is set to False", default='') parser.add_argument('--insecure', help="Set whether certificates are used for Keystone authentication", default=False) parser.add_argument('--config_dir', help="Configuration directory", default='/etc/monasca/agent') parser.add_argument('--log_dir', help="monasca-agent log directory", default='/var/log/monasca/agent') parser.add_argument('--log_level', help="monasca-agent logging level (ERROR, WARNING, INFO, DEBUG)", required=False, default='WARN') parser.add_argument( '--template_dir', help="Alternative template directory", default=os.path.join(PREFIX_DIR, 'share/monasca/agent')) parser.add_argument('--overwrite', help="Overwrite existing plugin configuration. " + "The default is to merge. agent.yaml is always overwritten.", action="store_true") parser.add_argument('--skip_enable', help="By default the service is enabled, " + "which requires the script run as root. Set this to skip that step.", action="store_true") parser.add_argument('--user', help="User name to run monasca-agent as", default='mon-agent') parser.add_argument('-s', '--service', help="Service this node is associated with, added as a dimension.") parser.add_argument('--amplifier', help="Integer for the number of additional measurements to create. " + "Additional measurements contain the 'amplifier' dimension. " + "Useful for load testing; not for production use.", default=0) parser.add_argument('-v', '--verbose', help="Verbose Output", action="store_true") args = parser.parse_args() if args.verbose: logging.basicConfig(level=logging.DEBUG, format="%(levelname)s: %(message)s") else: logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s") # Detect and if possibly enable the agent service agent_service = detect_init(PREFIX_DIR, args.config_dir, args.log_dir, args.template_dir, username=args.user) if args.detection_plugins is None: # Skip base setup if running specific detection plugins # Verify required options if args.username is None or args.password is None or args.keystone_url is None: log.error('Username, password and keystone_url are required when running full configuration.') parser.print_help() sys.exit(1) if not args.skip_enable: agent_service.enable() gid = pwd.getpwnam(args.user).pw_gid # Write the main agent.yaml - Note this is always overwritten log.info('Configuring base Agent settings.') dimensions = {} # Join service in with the dimensions if args.service: dimensions.update({'service': args.service}) if args.dimensions: dimensions.update(dict(item.strip().split(":") for item in args.dimensions.split(","))) args.dimensions = dict((name, value) for (name, value) in dimensions.iteritems()) write_template(os.path.join(args.template_dir, 'agent.yaml.template'), os.path.join(args.config_dir, 'agent.yaml'), {'args': args, 'hostname': socket.getfqdn()}, gid, is_yaml=True) # Write the supervisor.conf write_template(os.path.join(args.template_dir, 'supervisor.conf.template'), os.path.join(args.config_dir, 'supervisor.conf'), {'prefix': PREFIX_DIR, 'log_dir': args.log_dir, 'monasca_user': args.user}, gid) # Run through detection and config building for the plugins plugin_config = agent_config.Plugins() detected_plugins = find_plugins(CUSTOM_PLUGIN_PATH) if args.system_only: from detection.plugins.system import System plugins = [System] elif args.detection_plugins is not None: lower_plugins = [p.lower() for p in args.detection_plugins] plugins = [] for plugin in detected_plugins: if plugin.__name__.lower() in lower_plugins: plugins.append(plugin) if len(plugins) != len(args.detection_plugins): plugin_names = [p.__name__ for p in detected_plugins] log.warn("Not all plugins found, discovered plugins {0}\nAvailable plugins{1}".format(plugins, plugin_names)) else: plugins = detected_plugins for detect_class in plugins: detect = detect_class(args.template_dir, args.overwrite, args.detection_args) if detect.available: log.info('Configuring {0}'.format(detect.name)) new_config = detect.build_config() if new_config is not None: plugin_config.merge(new_config) elif args.detection_plugins is not None: # Give a warning on failed detection when a plugin is called out log.warn('Failed detection of plugin {0}.'.format(detect.name) + "\n\tPossible causes: Service not found or missing arguments.") # todo add option to install dependencies # Write out the plugin config changes = False # The gid is created on service activation which we assume has happened before this step or before running with -d gid = pwd.getpwnam(args.user).pw_gid for key, value in plugin_config.iteritems(): # todo if overwrite is set I should either warn or just delete any config files not in the new config config_path = os.path.join(args.config_dir, 'conf.d', key + '.yaml') # merge old and new config, new has precedence if (not args.overwrite) and os.path.exists(config_path): with open(config_path, 'r') as config_file: old_config = yaml.load(config_file.read()) if old_config is not None: agent_config.merge_by_name(value['instances'], old_config['instances']) # Sort before compare, if instances have no name the sort will fail making order changes significant try: value['instances'].sort(key=lambda k: k['name']) old_config['instances'].sort(key=lambda k: k['name']) except Exception: pass if value == old_config: # Don't write config if no change continue with open(config_path, 'w') as config_file: changes = True os.chmod(config_path, 0640) os.chown(config_path, 0, gid) config_file.write(yaml.safe_dump(value, encoding='utf-8', allow_unicode=True, default_flow_style=False)) # Don't restart if only doing detection plugins and no changes found if args.detection_plugins is not None and not changes: plugin_names = [p.__name__ for p in plugins] log.info('No changes found for plugins {0}, skipping restart of Monasca Agent'.format(plugin_names)) return 0 # Now that the config is built, start the service try: agent_service.start(restart=True) except subprocess.CalledProcessError: log.error('The service did not startup correctly see %s' % args.log_dir)