monasca-agent/monasca_setup/agent_config.py
Craig Bryant 1e1f130901 Replace yaml.load() with yaml.safe_load()
Bandit flags yaml.load() as security risk so replace all occurrences
with yaml.safe_load()

Change-Id: I8d0b322b9083c63a75bc34caf2a67fc05d8a4390
Closes-Bug: #1634265
2016-10-17 15:59:48 -06:00

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)