Exemplo n.º 1
0
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
Exemplo n.º 2
0
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
Exemplo n.º 3
0
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
Exemplo n.º 4
0
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
Exemplo n.º 5
0
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)