def configure_chrony(ntp_servers, ntp_pool=None, fstore=None, sysstore=None, debug=False): """ This method only configures chrony client with ntp_servers or ntp_pool """ module = "chrony" if sysstore: sysstore.backup_state(module, "enabled", services.knownservices.chronyd.is_enabled()) aug = Augeas(flags=Augeas.NO_LOAD | Augeas.NO_MODL_AUTOLOAD, loadpath=paths.USR_SHARE_IPA_DIR) try: logger.debug("Configuring chrony") chrony_conf = os.path.abspath(paths.CHRONY_CONF) aug.transform(module, chrony_conf) # loads chrony lens file aug.load() # loads augeas tree # augeas needs to prepend path with '/files' path = '/files{path}'.format(path=chrony_conf) # remove possible conflicting configuration of servers aug.remove('{}/server'.format(path)) aug.remove('{}/pool'.format(path)) aug.remove('{}/peer'.format(path)) if ntp_pool: logger.debug("Setting server pool:") logger.debug("'%s'", ntp_pool) aug.set('{}/pool[last()+1]'.format(path), ntp_pool) aug.set('{}/pool[last()]/iburst'.format(path), None) if ntp_servers: logger.debug("Setting time servers:") for server in ntp_servers: aug.set('{}/server[last()+1]'.format(path), server) aug.set('{}/server[last()]/iburst'.format(path), None) logger.debug("'%s'", server) # backup oginal conf file logger.debug("Backing up '%s'", chrony_conf) __backup_config(chrony_conf, fstore) logger.debug("Writing configuration to '%s'", chrony_conf) aug.save() logger.info('Configuration of chrony was changed by installer.') configured = True except IOError: logger.error("Augeas failed to configure file %s", chrony_conf) configured = False except RuntimeError as e: logger.error("Configuration failed with: %s", e) configured = False finally: aug.close() tasks.restore_context(chrony_conf) return configured
def get_configured_ifaces(): aug = Augeas(flags=Augeas.NO_MODL_AUTOLOAD) aug.add_transform('interfaces', '/etc/network/interfaces') aug.load() base = '/files/etc/network/interfaces' for m in aug.match('%s/iface' % base): yield aug.get(m) aug.close()
def _augeas_init(): if not _directories_inited: init_directories() global augeas augeas = Augeas(loadpath=get_config_path(), flags=Augeas.SAVE_BACKUP | Augeas.NO_MODL_AUTOLOAD) augeas.set("/augeas/load/Agocontrol/lens", "agocontrol.lns"); augeas.set("/augeas/load/Agocontrol/incl[1]", get_config_path("conf.d") + "/*.conf"); augeas.load()
def get_augeas(self, entry): """ Get an augeas object for the given entry. """ if entry.get("name") not in self._augeas: aug = Augeas() if entry.get("lens"): self.logger.debug("Augeas: Adding %s to include path for %s" % (entry.get("name"), entry.get("lens"))) incl = "/augeas/load/%s/incl" % entry.get("lens") ilen = len(aug.match(incl)) if ilen == 0: self.logger.error("Augeas: Lens %s does not exist" % entry.get("lens")) else: aug.set("%s[%s]" % (incl, ilen + 1), entry.get("name")) aug.load() self._augeas[entry.get("name")] = aug return self._augeas[entry.get("name")]
def __disable_mod_ssl_ocsp(self): aug = Augeas(flags=Augeas.NO_LOAD | Augeas.NO_MODL_AUTOLOAD) aug.set('/augeas/load/Httpd/lens', 'Httpd.lns') aug.set('/augeas/load/Httpd/incl', paths.HTTPD_SSL_CONF) aug.load() path = '/files{}/VirtualHost'.format(paths.HTTPD_SSL_CONF) ocsp_path = '{}/directive[.="{}"]'.format(path, OCSP_DIRECTIVE) ocsp_arg = '{}/arg'.format(ocsp_path) ocsp_comment = '{}/#comment[.="{}"]'.format(path, OCSP_DIRECTIVE) ocsp_dir = aug.get(ocsp_path) # there is SSLOCSPEnable directive in nss.conf file, comment it # otherwise just do nothing if ocsp_dir is not None: ocsp_state = aug.get(ocsp_arg) aug.remove(ocsp_arg) aug.rename(ocsp_path, '#comment') aug.set(ocsp_comment, '{} {}'.format(OCSP_DIRECTIVE, ocsp_state)) aug.save()
class AugeasWrapper(object): """python-augeas higher-level wrapper. Load single augeas lens and configuration file. Exposes configuration file as AugeasNode object with dict-like interface. AugeasWrapper can be used in with statement in the same way as file does. """ def __init__(self, confpath, lens, root=None, loadpath=None, flags=Augeas.NO_MODL_AUTOLOAD | Augeas.NO_LOAD | Augeas.ENABLE_SPAN): """Parse configuration file using given lens. Params: confpath (str): Absolute path to the configuration file lens (str): Name of module containing Augeas lens root: passed down to original Augeas flags: passed down to original Augeas loadpath: passed down to original Augeas flags: passed down to original Augeas """ log.debug('loadpath: %s', loadpath) log.debug('confpath: %s', confpath) self._aug = Augeas(root=root, loadpath=loadpath, flags=flags) # /augeas/load/{lens} aug_load_path = join(AUGEAS_LOAD_PATH, lens) # /augeas/load/{lens}/lens = {lens}.lns self._aug.set(join(aug_load_path, 'lens'), '%s.lns' % lens) # /augeas/load/{lens}/incl[0] = {confpath} self._aug.set(join(aug_load_path, 'incl[0]'), confpath) self._aug.load() errors = self._aug.match(AUGEAS_ERROR_PATH) if errors: err_msg = '\n'.join( ["{}: {}".format(e, self._aug.get(e)) for e in errors]) raise RuntimeError(err_msg) path = join(AUGEAS_FILES_PATH, confpath) paths = self._aug.match(path) if len(paths) != 1: raise ValueError('path %s did not match exactly once' % path) self.tree = AugeasNode(self._aug, path) self._loaded = True def __enter__(self): return self def __exit__(self, exc_type, exc_value, traceback): self.save() self.close() def save(self): """Save Augeas tree to its original file.""" assert self._loaded try: self._aug.save() except IOError as exc: log.exception(exc) for err_path in self._aug.match('//error'): log.error('%s: %s', err_path, self._aug.get(os.path.join(err_path, 'message'))) raise def close(self): """ close Augeas library After calling close() the object must not be used anymore. """ assert self._loaded self._aug.close() del self._aug self._loaded = False def match(self, path): """Yield AugeasNodes matching given expression.""" assert self._loaded assert path log.debug('tree match %s', path) for matched_path in self._aug.match(path): yield AugeasNode(self._aug, matched_path)
class LVMConfig(object): def __init__(self, path="/etc/lvm/lvm.conf"): self.path = path # Augeas loads by default tons of unneeded lenses and configuration # files. On my test host, it fails to load, trying to read my 500 MiB # /etc/lvm/archive/. # # These are the standard LVM lens includes: # /augeas/load/LVM/incl[1] /etc/lvm/lvm.conf # /augeas/load/LVM/incl[2] /etc/lvm/backup/* # /augeas/load/LVM/incl[3] /etc/lvm/archive/*.vg # # We need only the first entry to work with lvm.conf. Using customized # load setup, as explained in # https://github.com/hercules-team/augeas/wiki/Loading-specific-files # # Removing the archive and backup entries, we can load augeas in 0.7 # seconds on my test vm. Removing all other lenses shorten the time to # 0.04 seconds. log.debug("Loading LVM configuration from %r", path) self.aug = Augeas(flags=Augeas.NO_MODL_AUTOLOAD | Augeas.SAVE_BACKUP) self.aug.add_transform("lvm.lns", [path]) self.aug.load() # Context manager interface def __enter__(self): return self def __exit__(self, t, v, tb): try: self.close() except Exception as e: # Caller succeeded, raise the close error. if t is None: raise # Caller has failed, do not hide the original error. log.exception("Error closing %s: %s" % (self, e)) # Accessing list of strings def getlist(self, section, option): pat = "/files%s/%s/dict/%s/list/*/str" % (self.path, section, option) matches = self.aug.match(pat) if not matches: return None # Cannot store/read empty list return [self.aug.get(m) for m in matches] def setlist(self, section, option, value): log.debug("Setting %s/%s to %s", section, option, value) opt_path = "/files%s/%s/dict/%s" % (self.path, section, option) self.aug.remove(opt_path) item_path = opt_path + "/list/%d/str" for i, item in enumerate(value, 1): self.aug.set(item_path % i, item) # Accessing flat values (int, string) def getint(self, section, option): val = self._get_flat(section, option, "int") return int(val) if val is not None else None def setint(self, section, option, value): self._set_flat(section, option, "int", str(value)) def getstr(self, section, option): return self._get_flat(section, option, "str") def setstr(self, section, option, value): self._set_flat(section, option, "str", value) def _get_flat(self, section, option, opt_type): path = self._flat_path(section, option, opt_type) return self.aug.get(path) def _set_flat(self, section, option, opt_type, value): log.debug("Setting %s/%s to %r", section, option, value) path = self._flat_path(section, option, opt_type) return self.aug.set(path, value) def _flat_path(self, section, option, opt_type): return "/files%s/%s/dict/%s/%s" % ( self.path, section, option, opt_type) # Removing options def remove(self, section, option): log.debug("Removing %s/%s", section, option) path = "/files%s/%s/dict/%s" % (self.path, section, option) self.aug.remove(path) # File operations def save(self): log.info("Saving new LVM configuration to %r, previous configuration " "saved to %r", self.path, self.path + ".augsave") self.aug.save() def close(self): log.debug("Closing LVM configuration %s", self.path) self.aug.close()
class AugeasWrap(object): _transform = 'interfaces' _file = None _attrs = [] _map = {} _match = None __aug = None @property def _aug(self): if not self.__aug: self.__aug = Augeas(flags=Augeas.NO_MODL_AUTOLOAD) self.__aug.add_transform(self._transform, self._file) self.__aug.load() return self.__aug _debug = False def exists(self): return bool(self.get()) def _abspath(self, path): if not path or not (path.startswith('/augeas') or path.startswith('/files') or path.startswith('$')): path = '%s%s' % (self._match, path or '') return path or '' def get(self, path=None): ret = self._aug.get(self._abspath(path)) if self._debug: logger.debug('get path=%s value=%s', self._abspath(path), ret) return ret def set(self, value, path=None): value = str(value) if self._debug: logger.debug('set path=%s value=%s', self._abspath(path), value) return self._aug.set(self._abspath(path), value) def match(self, path=None): if self._debug: logger.debug('match path=%s', self._abspath(path)) return self._aug.match(self._abspath(path)) def remove(self, path=None): if self._debug: logger.debug('remove path=%s', self._abspath(path)) return self._aug.remove(self._abspath(path)) def insert(self, value, path=None, before=True): value = str(value) if self._debug: logger.debug('insert path=%s value=%s', self._abspath(path), value) return self._aug.insert(self._abspath(path), value, before=before) def _print(self, path=None): path = self._abspath(path) get = self.get(path) logger.info("[%s] = '%s'", path, get) try: for match in self.match('%s//*' % path): logger.info("[%s] = '%s'", match, self._aug.get(match)) except RuntimeError: pass def _all_attrs(self): return self._attrs + self._map.keys()
class TestNode(unittest.TestCase): """Tests the Node class""" def setUp(self): self.tmp = tempfile.mkdtemp() self.root = "%s/root" % self.tmp shutil.copytree("%s/fakeroot" % os.path.dirname(__file__), self.root) self.aug = Augeas(root=self.root, flags=Augeas.NO_MODL_AUTOLOAD) self.aug.add_transform("Nsswitch.lns", "/etc/nsswitch.conf") self.aug.load() class FakeParent: def setpath(self): return "/files/etc/nsswitch.conf" self.parent = FakeParent() def tearDown(self): shutil.rmtree(self.tmp) self.aug.close() def test_setpath_self(self): """Test setpath generates path for '.' (itself)""" n = aug2cmds.Node(self.aug, self.parent, uniqpaths=["."], path="/files/etc/nsswitch.conf/database[1]") self.assertEqual(n.setpath(), "/files/etc/nsswitch.conf/database[.='passwd']") def test_setpath_subnode(self): """Test setpath generates path for a subnode""" n = aug2cmds.Node(self.aug, self.parent, uniqpaths=["service"], path="/files/etc/nsswitch.conf/database[1]") self.assertEqual(n.setpath(), "/files/etc/nsswitch.conf/database[service='files']") def test_setpath_and(self): """Test setpath generates path for multiple subpaths""" n = aug2cmds.Node(self.aug, self.parent, uniqpaths=[".", "service"], path="/files/etc/nsswitch.conf/database[1]") self.assertEqual(n.setpath(), "/files/etc/nsswitch.conf/database[.='passwd' and service='files']") def test_basename(self): """Test basename understands Augeas paths""" self.assertEqual(aug2cmds.Node.basename("/files"), "files") self.assertEqual(aug2cmds.Node.basename("/files/test"), "test") self.assertEqual(aug2cmds.Node.basename("/files/test[foo]"), "test") self.assertEqual(aug2cmds.Node.basename( "/files/test[foo/bar]"), "test") def test_dirname(self): """Test dirname understands Augeas paths""" self.assertEqual(aug2cmds.Node.dirname("/files"), "/") self.assertEqual(aug2cmds.Node.dirname("/files/test"), "/files") self.assertEqual(aug2cmds.Node.dirname("/files/test[foo]"), "/files") self.assertEqual(aug2cmds.Node.dirname( "/files/test[foo/bar]"), "/files") def test_children_none(self): """Test when there are no children""" n = aug2cmds.Node(self.aug, self.parent, "/files/etc/nsswitch.conf/#comment[1]", ["."]) self.assertRaises(StopIteration, next, n.children()) def test_children(self): """Test new Node objects are created for children""" n = aug2cmds.Node(self.aug, self.parent, "/files/etc/nsswitch.conf/database[15]", ["."]) c = n.children() sn = c.next() self.assertEqual(sn.path, "/files/etc/nsswitch.conf/database[15]/service[1]") self.assertEqual(sn.setpath(), "/files/etc/nsswitch.conf/database[.='aliases']/service[.='files']") sn = c.next() self.assertEqual(sn.path, "/files/etc/nsswitch.conf/database[15]/service[2]") self.assertEqual(sn.setpath(), "/files/etc/nsswitch.conf/database[.='aliases']/service[.='nisplus']") self.assertRaises(StopIteration, next, c) def test_value(self): """Test value is retrieved""" n = aug2cmds.Node(self.aug, self.parent, uniqpaths=["."], path="/files/etc/nsswitch.conf/database[1]") self.assertEqual(n.value(), "passwd") def test_value_none(self): """Test when no value is set""" n = aug2cmds.Node(self.aug, self.parent, uniqpaths=["."], path="/files/etc/nsswitch.conf") self.assertEqual(n.value(), None)
class HAProxyConfManager(object): conf_path = 'etc/haproxy/haproxy.cfg' def __init__(self, root_path=None): if root_path is None: root_path = '/' self.conf_path = root_path + self.conf_path self._conf_xpath = '/files' + self.conf_path + '/' self._augeas = Augeas(root=root_path, loadpath=HAPROXY_LENS_DIR) def load(self): LOG.debug('Loading haproxy.conf') self._augeas.load() def save(self): LOG.debug('Saving haproxy.conf') self._augeas.save() def get(self, xpath): """ Returns values of label at given xpath. If label is presented but has no value, returns True xpath is relative to haproxy.cfg file """ if self._augeas.match(self._conf_xpath + xpath) == []: return None value = self._augeas.get(self._conf_xpath + xpath) return value if value is not None else True def _find_xpath_gen(self, base_xpath, sublabel, value_pattern): section_xpaths = self._augeas.match(self._conf_xpath + base_xpath) for xpath in section_xpaths: match = re.search(value_pattern, self._augeas.get(xpath + '/' + sublabel)) if match != None: yield xpath.replace(self._conf_xpath, '') def find_all_xpaths(self, base_xpath, sublabel, value_pattern): """ Returns list of all labels from given base_xpath which sublabel matches `value_pattern` `value_pattern` is regexp This metod is useful when you need to find certain section from config that contains number of such sections (listen[1], listen[2]...) ..Example: cnf.get_matched_xpaths('listen', 'name', 'test*') this will find every listen whose name begins with 'test' """ return list(self._find_xpath_gen(base_xpath, sublabel, value_pattern)) def find_one_xpath(self, base_xpath, sublabel, value_pattern): """ Returns label xpath by given value_pattern of sublabel. Returns None if no label is found. This metod is useful when you need to find certain section from config that contains number of such sections (listen[1], listen[2]...) """ try: return self._find_xpath_gen(base_xpath, sublabel, value_pattern).next() except StopIteration: return None def get_all_xpaths(self, base_xpath): return [ x.replace(self._conf_xpath, '') for x in self._augeas.match(self._conf_xpath + base_xpath) ] def set(self, xpath, value=None, save_conf=True): """ Sets label at given xpath with given value. If there is no label - creates one. If there is - updates its value. `value` can be None/True to set label without actual value or string or iterable or dict to set number of sublabels at once. `xpath` is relative to haproxy.cfg file. """ LOG.debug('Setting %s with value: %s' % (xpath, value)) if isinstance(value, dict): self._augeas.set(self._conf_xpath + xpath, None) for k, v in value.items(): self.set(xpath + '/' + k, v, save_conf=False) elif hasattr(value, '__iter__'): for v in value: self.add(xpath, v, save_conf=False) else: if value == True: value = None elif value == False: return self.remove(xpath) elif value is not None: value = str(value) self._augeas.set(self._conf_xpath + xpath, value) if save_conf: self.save() def add(self, xpath, value=None, save_conf=True): """ Adds node at given xpath. New nodes are appended xpath is relative to haproxy.cfg file Returns xpath of created node """ nodes_qty = len(self._augeas.match(self._conf_xpath + xpath)) if nodes_qty != 0: xpath += '[%s]' % (nodes_qty + 1) self.set(xpath, value, save_conf) return xpath def insert(self, xpath, label, value=None, before=True): """ Inserts label before or after given in xpath xpath is relative to haproxy.cfg file """ xpath = self._conf_xpath + xpath self._augeas.insert(xpath, label, before) if value is not None: labels = self._augeas.match(os.path.dirname(xpath)) base_label = os.path.basename(xpath) inserted_xpath = None if label == base_label: if xpath.endswith(']'): inserted_xpath = xpath if before else labels[ labels.index(xpath) + 1] else: inserted_xpath += '[1]' if before else '[2]' else: index = labels.index(xpath) + (-1 if before else 1) inserted_xpath = labels[index] self.set(inserted_xpath.replace(self._conf_xpath, ''), value) def add_conf(self, conf, append=False): """ Append raw conf to the end of haproxy.conf file or insert at the beginning before everything """ LOG.debug('Adding raw conf part to haproxy.conf:\n%s' % conf) raw = None with open(self.conf_path, 'r') as conf_file: raw = conf_file.read() if not conf.endswith('\n'): conf += '\n' raw = '\n'.join((raw, conf) if append else (conf, raw)) with open(self.conf_path, 'w') as conf_file: conf_file.write(raw) self.load() def extend_section(self, conf, section_name): """ Appends raw conf to the section with given name """ LOG.debug( 'Adding raw conf part to section of haproxy.conf with name %s :\n%s' % (section_name, conf)) raw = None with open(self.conf_path, 'r') as conf_file: raw = conf_file.read() if conf.endswith('\n'): conf = conf[:-1] # reindenting conf conf = dedent(conf) conf = ' ' + conf.replace('\n', '\n ') raw = re.sub(section_name + r'\s*\n', '%s\n%s\n' % (section_name, conf), raw) with open(self.conf_path, 'w') as conf_file: conf_file.write(raw) self.load() def remove(self, xpath): LOG.debug('Removing %s' % xpath) return self._augeas.remove(self._conf_xpath + xpath)