import collections import logging import pkgutil import yaml import abc import sys import os from yaml.representer import Representer from bridgy import inventory logger = logging.getLogger() class ConfigBase(object): __metaclass__ = abc.ABCMeta path = "~/.bridgy/config.yml" inventory = "~/.bridgy/inventory" mount = "~/.bridgy/mounts" conf = None def __init__(self, initial_data=None): # this is usefule for testing purposes self.conf = initial_data @abc.abstractproperty def version(self): pass @abc.abstractproperty def config_template_path(self): pass @abc.abstractproperty def sources(self): pass def verify(self): if self.dig('inventory', 'source') == None: logger.error("No inventory source specified (%s):" % self.path) sys.exit(1) if self.dig('inventory', 'include_pattern') != None and self.dig('inventory', 'exclude_pattern') != None: logger.error("'exclude_pattern' and 'include_pattern' are mutually exclusive") sys.exit(1) names = [] dupNames = [] for source, srcCfg in self.sources(): if 'name' in srcCfg and srcCfg['name'] in names: dupNames.append(srcCfg['name']) # verify each source here if source == 'newrelic': if 'insights_query_api_key' in srcCfg and srcCfg['insights_query_api_key'] == "API_KEY": logger.error("New Relic inventory selected but no API key was specified: %s" % self.path) sys.exit(1) if len(dupNames) > 0: logger.error("Duplicate inventory source names detected: %s" % ', '.join(dupNames)) sys.exit(1) @property def config_template_contents(self): return pkgutil.get_data('bridgy', 'config/samples/' + self.config_template_path) def read(self): # ensure yaml uses a defaultdict(str) yaml.add_representer(collections.defaultdict, Representer.represent_str) try: with open(os.path.expanduser(self.path), 'r') as fh: self.conf = yaml.load(fh) except Exception as ex: logger.error("Unable to read config (%s): %s" % (self.path, ex)) sys.exit(1) def exists(self): config_file = os.path.expanduser(self.path) return os.path.exists(config_file) def create(self): created = False if not self.exists(): config_file = os.path.expanduser(self.path) parent_dir = os.path.dirname(config_file) if not os.path.exists(parent_dir): os.mkdir(parent_dir) with open(config_file, 'wb') as fh: fh.write(self.config_template_contents) created = True inventory_cache = os.path.expanduser(self.inventory) if not os.path.exists(inventory_cache): parent_dir = os.path.dirname(inventory_cache) if not os.path.exists(parent_dir): os.mkdir(parent_dir) os.mkdir(inventory_cache) for source in list(inventory.SOURCES.keys()): source_path = os.path.join(inventory_cache, source) if not os.path.exists(source_path): os.mkdir(source_path) mount_path = os.path.expanduser(self.mount) if not os.path.exists(mount_path): os.mkdir(mount_path) return created def inventoryDir(self, source, name=''): if source not in list(inventory.SOURCES.keys()): raise RuntimeError("Unexpected inventory source: %s" % repr(source)) return os.path.join(os.path.expanduser(self.inventory), source, name) @property def mount_root_dir(self): return os.path.expanduser(self.mount) def __iter__(self): return iter(self.conf) def dig(self, *original_keys): def __dig(d, *keys): try: if len(keys) == 1: try: return d[keys[0]] except TypeError: return None return __dig(d[keys[0]], *keys[1:]) except KeyError: return None return __dig(self.conf, *original_keys) def __getitem__(self, key): return self.conf[key] def __setitem__(self, key, value): self.conf[key] = value def __repr__(self): return repr(self.conf)