1e1f130901
Bandit flags yaml.load() as security risk so replace all occurrences with yaml.safe_load() Change-Id: I8d0b322b9083c63a75bc34caf2a67fc05d8a4390 Closes-Bug: #1634265
131 lines
4.7 KiB
Python
131 lines
4.7 KiB
Python
# (C) Copyright 2015-2016 Hewlett Packard Enterprise Development LP
|
|
|
|
"""Classes to aid in configuration of the agent."""
|
|
|
|
import collections
|
|
import logging
|
|
import os
|
|
import pwd
|
|
import yaml
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
class Plugins(collections.defaultdict):
|
|
|
|
"""A container for the plugin configurations used by the monasca-agent.
|
|
|
|
This is essentially a defaultdict(dict) but put into a class primarily to make the interface clear, also
|
|
to add a couple of helper methods.
|
|
Each plugin config is stored with the key being its config name (excluding .yaml).
|
|
The value a dict which will convert to yaml.
|
|
"""
|
|
|
|
def __init__(self):
|
|
super(Plugins, self).__init__(dict)
|
|
|
|
# todo Possibly enforce the key being a string without .yaml in it.
|
|
|
|
def diff(self, other_plugins):
|
|
raise NotImplementedError
|
|
|
|
def merge(self, other):
|
|
"""Do a deep merge with precedence going to other (as is the case with update).
|
|
|
|
"""
|
|
# Implemented as a function so it can be used for arbitrary dictionaries not just self, this is needed
|
|
# for the recursive nature of the merge.
|
|
deep_merge(self, other)
|
|
|
|
|
|
def deep_merge(adict, other):
|
|
"""A recursive merge of two dictionaries including combining of any lists within the data structure.
|
|
|
|
"""
|
|
for key, value in other.iteritems():
|
|
if key in adict:
|
|
if isinstance(adict[key], dict) and isinstance(value, dict):
|
|
deep_merge(adict[key], value)
|
|
elif isinstance(adict[key], list) and isinstance(value, list):
|
|
adict[key] += value
|
|
else:
|
|
adict[key] = value
|
|
|
|
|
|
def merge_by_name(first, second):
|
|
"""Merge a list of dictionaries replacing any dictionaries with the same 'name' value rather than merging.
|
|
The precedence goes to first.
|
|
"""
|
|
first_names = [i['name'] for i in first if 'name' in i]
|
|
for item in second:
|
|
if 'name' not in item or item['name'] not in first_names:
|
|
first.append(item)
|
|
|
|
|
|
def read_plugin_config_from_disk(config_dir, plugin_name):
|
|
"""Reads from the Agent on disk configuration the config for a specific plugin
|
|
:param config_dir: Monasca Agent configuration directory
|
|
:param plugin_name: The name of the check plugin
|
|
:return: Dictionary of parsed yaml content
|
|
"""
|
|
config_path = os.path.join(config_dir, 'conf.d', plugin_name + '.yaml')
|
|
config = None
|
|
if os.path.exists(config_path):
|
|
with open(config_path, 'r') as config_file:
|
|
config = yaml.safe_load(config_file.read())
|
|
return config
|
|
|
|
|
|
def save_plugin_config(config_dir, plugin_name, user, conf):
|
|
"""Writes configuration for plugin_name to disk in the config_dir
|
|
:param config_dir: Monasca Agent configuration directory
|
|
:param plugin_name: The name of the check plugin
|
|
:param user: The username Monasca-agent will run as
|
|
:param conf: The value of the configuration to write to disk
|
|
:return: None
|
|
"""
|
|
config_path = os.path.join(config_dir, 'conf.d', plugin_name + '.yaml')
|
|
|
|
with open(config_path, 'w') as config_file:
|
|
# The gid is created on service activation which we assume has happened
|
|
config_file.write(yaml.safe_dump(conf,
|
|
encoding='utf-8',
|
|
allow_unicode=True,
|
|
default_flow_style=False))
|
|
stat = pwd.getpwnam(user)
|
|
|
|
gid = stat.pw_gid
|
|
uid = stat.pw_uid
|
|
|
|
os.chmod(config_path, 0o640)
|
|
os.chown(config_path, uid, gid)
|
|
|
|
|
|
def check_endpoint_changes(value, config):
|
|
"""Change urls in config with same path but different protocols into new
|
|
endpoints.
|
|
"""
|
|
new_url = value['instances'][0]['url']
|
|
old_urls = [i['url'] for i in config['instances'] if 'url' in i]
|
|
new_path = new_url.split("://")[1]
|
|
old_paths = [url.split("://")[1] for url in old_urls]
|
|
for i, old_path in enumerate(old_paths):
|
|
if old_path == new_path:
|
|
if config['instances'][i]['url'] == config['instances'][i]['name']:
|
|
config['instances'][i]['name'] = new_url
|
|
config['instances'][i]['url'] = new_url
|
|
return config
|
|
|
|
|
|
def delete_from_config(args, config, file_path, plugin_name):
|
|
if args.dry_run:
|
|
info_msg = ("Changes would be made to the config file {0}".format(file_path))
|
|
else:
|
|
if len(config['instances']) == 0:
|
|
info_msg = ("Removing configuration file {0} it is no longer needed.".format(file_path))
|
|
os.remove(file_path)
|
|
else:
|
|
info_msg = ("Saving changes to configuration file {0}.".format(file_path))
|
|
save_plugin_config(args.config_dir, plugin_name, args.user, config)
|
|
log.info(info_msg)
|