diff --git a/monasca_setup/main.py b/monasca_setup/main.py index a0216bcf..9e148db3 100644 --- a/monasca_setup/main.py +++ b/monasca_setup/main.py @@ -48,29 +48,38 @@ def write_template(template_path, out_path, variables, group, is_yaml=False): def main(argv=None): - parser = argparse.ArgumentParser(description='Detect running daemons then configure and start the agent.', + 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=True) + '-u', '--username', help="Username used for keystone authentication. Required for basic configuration.") parser.add_argument( - '-p', '--password', help="Password used for keystone authentication", required=True) - parser.add_argument('--keystone_url', help="Keystone url", required=True) - parser.add_argument('--monasca_url', help="Monasca API url, if not defined the url is pulled from keystone", required=False, default='') - parser.add_argument('--insecure', help="Set whether certificates are used for Keystone authentication", required=False, default=False) - parser.add_argument('--project_name', help="Project name for keystone authentication", required=False, default='') - parser.add_argument('--project_domain_id', help="Project domain id for keystone authentication", required=False, default='') - parser.add_argument('--project_domain_name', help="Project domain name for keystone authentication", required=False, default='') - parser.add_argument('--project_id', help="Keystone project id for keystone authentication", required=False, default='') - 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", required=False, default='') + '-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 the this space separated list. " + + "This assumes the base config has already run.") parser.add_argument('--check_frequency', help="How often to run metric collection in seconds", type=int, default=60) - parser.add_argument('--config_dir', help="Configuration directory", default='/etc/monasca/agent') 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='INFO') - parser.add_argument( - '--template_dir', help="Alternative template directory", default=os.path.join(PREFIX_DIR, 'share/monasca/agent')) + parser.add_argument('--log_level', help="monasca-agent logging level (ERROR, WARNING, INFO, DEBUG)", default='INFO') + parser.add_argument('--template_dir', + help="Alternative template directory", default=os.path.join(PREFIX_DIR, 'share/monasca/agent')) parser.add_argument('--headless', help="Run in a non-interactive mode", action="store_true") parser.add_argument('--overwrite', help="Overwrite existing plugin configuration. " + @@ -81,12 +90,9 @@ def main(argv=None): action="store_true") parser.add_argument('--user', help="User name to run monasca-agent as", default='monasca-agent') parser.add_argument('-s', '--service', help="Service this node is associated with, added as a dimension.") - parser.add_argument('--system_only', - help="If set only system metrics (cpu, disk, load, memory, network) will be configured.", - action="store_true", default=False) 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, required=False) + "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() @@ -97,39 +103,58 @@ def main(argv=None): # 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 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) + 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() - # 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}, - 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}, + gid) # Run through detection and config building for the plugins plugin_config = agent_config.Plugins() 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 DETECTION_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 DETECTION_PLUGINS] + log.warn("Not all plugins found, discovered plugins {0}\nAvailable plugins{1}".format(plugins, + plugin_names)) else: plugins = DETECTION_PLUGINS + for detect_class in plugins: detect = detect_class(args.template_dir, args.overwrite) if detect.available: @@ -142,7 +167,6 @@ def main(argv=None): # Write out the plugin config 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 - # todo add the ability to show a diff before overwriting or merging 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): @@ -152,7 +176,7 @@ def main(argv=None): agent_config.deep_merge(old_config, value) value = old_config with open(config_path, 'w') as config_file: - os.chmod(config_path, 0o640) + os.chmod(config_path, 0640) os.chown(config_path, 0, gid) config_file.write(yaml.safe_dump(value, encoding='utf-8',