> (...)
> So if anyone has anything outstanding (patches, modules,
> must have features, must fix bugs, docs, etc) it would be
> great if you could get it ready soon ;-)
>(...)
> Adrian
Hello, so here is a new module proposal.
This is a module to handle (get, set, list...) parameters in configuration
files.
It relies on augeas (http://augeas.net/).
I wanted to post later,
because in comparison with augeas.py I didn't make the move and insert methods,
because I have improvements in mind,
and because I want to add more behaviour consistency with augtool of Augeas.
But I will not have time for this before Christmas, and the module is
working fine as-is.
A quick example:
#!/usr/bin/env python
import sys
import func.overlord.client as fc
c = fc.Client("*")
print 'Set PermitRootLogin to no in sshd_config'
print c.confmgt_augeas.set('/etc/ssh/sshd_config','PermitRootLogin','no')
(which does what is expected, of course)
In attachment you'll find:
- the module
- a test script I use to check the behaviour of the module
- the result of this test script on my test platform
(so you can see what the module does)
- a minimal doc. inspired from the quick tour on the augeas web site
Let me know if you think it is useful.
Louis Coilliot
#!/usr/bin/env python # # Copyright 2008 # Louis Coilliot <louis.coilliot@xxxxxxxxxxxx> # # This software may be freely redistributed under the terms of the GNU # general public license. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. import func_module from os import path as ospath def lstripstr(the_string,the_prefix): """Return a copy of the string with leading prefix removed.""" if the_string.startswith(the_prefix): return the_string[len(the_prefix):] return the_string def recurmatch(aug, path): """Generate all tree children of a start path.""" #Function adapted from test_augeas.py in python-augeas-0.3.0 #Original Author: Harald Hoyer <harald@xxxxxxxxxx>""" if path: if path != "/": val = aug.get(path) if val: yield (path, val) # here is the modification to (almost) match augtool print behavior: else: yield (path, '(none)') # end of modification m = [] if path != "/": aug.match(path) for i in m: for x in recurmatch(aug, i): yield x else: for i in aug.match(path + "/*"): for x in recurmatch(aug, i): yield x class confMgtAug(func_module.FuncModule): version = "0.0.1" api_version = "0.0.1" description = "Manage parameters in configuration files, with the help of Augeas." def get(self,entryPath,param='',hierarchy='/files'): """Get a value for a config. parameter in a config. file, with the help of Augeas, a configuration API (cf http://augeas.net)""" try: from augeas import Augeas aug=Augeas() except Exception, e: return str(e) path=hierarchy+entryPath+'/' try: matchtest=aug.match(path+param) except Exception, e: return str(e) if matchtest: try: pvalue=aug.get(path+param) #aug.close() except Exception, e: return str(e) else: # The node doesn't exist pvalue='(o)' if not pvalue: # The node doesn't have a value pvalue='(none)' return { 'path': entryPath, 'parameter': param, 'value': pvalue } def set(self,entryPath,param='',pvalue='',hierarchy='/files'): """Set/change a value for a config. parameter in a config. file, with the help of Augeas, a configuration API (cf http://augeas.net)""" try: from augeas import Augeas aug=Augeas() except Exception, e: return str(e) path=hierarchy+entryPath try: aug.set(path+"/"+param,pvalue) except Exception, e: return str(e) # Here is a little workaround for a bug in save for augeas 0.3.2. # In the future this won't be necessary anymore. try: aug.save() except: pass # End of workaround try: aug.save() except Exception, e: return str(e) try: pvalue=aug.get(path+"/"+param) #aug.close() except Exception, e: return str(e) return { 'path': entryPath, 'parameter': param, 'value': pvalue } def match(self,entryPath,param='',pvalue='',hierarchy='/files'): """Match a value for a config. parameter in a config. file, with the help of Augeas, a configuration API (cf http://augeas.net)""" try: from augeas import Augeas aug=Augeas() except Exception, e: return str(e) path=hierarchy+entryPath childpath=path+'/*/' if pvalue: try: matchlist = [ ospath.dirname(lstripstr(item,'/files')) for item in aug.match(path+'/'+param) + aug.match(childpath+'/'+param) if ( aug.get(item) == pvalue ) ] #aug.close() except Exception, e: return str(e) else: try: matchlist = [ ospath.dirname(lstripstr(item,'/files')) for item in aug.match(path+'/'+param) + aug.match(childpath+'/'+param) ] #aug.close() except Exception, e: return str(e) return matchlist def ls(self,entryPath,hierarchy='/files'): """List the direct children of an entry in a config. file, with the help of Augeas, a configuration API (cf http://augeas.net)""" try: from augeas import Augeas aug=Augeas() except Exception, e: return str(e) path=hierarchy+entryPath # We can't use a dict here because the same key can appear many times. nodes=[] try: for match in aug.match(path+'/*'): pvalue=aug.get(match) if not pvalue: pvalue='(none)' nodes.append([ospath.basename(match),pvalue]) except Exception, e: return str(e) #try: # aug.close() #except Exception, e: return str(e) return { 'path': entryPath, 'nodes': nodes } # print is a reserved word so we use printconf instead def printconf(self,entryPath,hierarchy='/files'): """Print all tree children nodes from the path provided, with the help of Augeas, a configuration API (cf http://augeas.net)""" path=hierarchy+entryPath try: from augeas import Augeas aug=Augeas() except Exception, e: return str(e) matches = recurmatch(aug, path) # Here we loose the benefit of the generator function: return { 'path': entryPath, 'nodes':[ [lstripstr(p,'/files'),attr] for (p,attr) in matches ] } def rm(self,entryPath,param='',hierarchy='/files'): """Delete a parameter (and all its children) in a config. file, with the help of Augeas, a configuration API (cf http://augeas.net)""" try: from augeas import Augeas aug=Augeas() except Exception, e: return str(e) path=hierarchy+entryPath try: result=aug.remove(path+"/"+param) #aug.close() except Exception, e: return str(e) # Here is a little workaround for a bug in save for augeas 0.3.2. # In the future this won't be necessary anymore. try: aug.save() except: pass # End of workaround try: aug.save() except Exception, e: return str(e) if result == -1: msg = 'Invalid node' else: msg = repr(result)+' node(s) removed.' return msg def register_method_args(self): """ Implementing the method arg getter """ return { 'get':{ 'args':{ 'entryPath':{ 'type':'string', 'optional':False, 'description':'The path to the config. file (fs or Augeas path)', }, 'param':{ 'type':'string', 'optional':True, 'default':'', 'description':'The target parameter in the config. file' }, 'hierarchy':{ 'type':'string', 'optional':True, 'default':'/files', 'description':'The augeas base path hierarchy' } }, 'description':"Get a value for a config. parameter in a config. file." }, 'set':{ 'args':{ 'entryPath':{ 'type':'string', 'optional':False, 'description':'The path to the config. file (fs or Augeas path)', }, 'param':{ 'type':'string', 'optional':True, 'default':'', 'description':'The target parameter in the config. file' }, 'pvalue':{ 'type':'string', 'optional':True, 'default':'', 'description':'The value to set for the parameter in the config. file' }, 'hierarchy':{ 'type':'string', 'optional':True, 'default':'/files', 'description':'The augeas base path hierarchy' } }, 'description':"Set/change a value for a config. parameter in a config. file." }, 'match':{ 'args':{ 'entryPath':{ 'type':'string', 'optional':False, 'description':'The path to the config. file (fs or Augeas path)', }, 'param':{ 'type':'string', 'optional':True, 'default':'', 'description':'The target parameter in the config. file' }, 'pvalue':{ 'type':'string', 'optional':True, 'default':'', 'description':'The value to set for the parameter in the config. file' }, 'hierarchy':{ 'type':'string', 'optional':True, 'default':'/files', 'description':'The augeas base path hierarchy' } }, 'description':"Match a value for a config. parameter in a config. file." }, 'ls':{ 'args':{ 'entryPath':{ 'type':'string', 'optional':False, 'description':'The path to the config. file (fs or Augeas path)', }, 'hierarchy':{ 'type':'string', 'optional':True, 'default':'/files', 'description':'The augeas base path hierarchy' } }, 'description':"List the direct children of an entry in a config. file." }, 'printconf':{ 'args':{ 'entryPath':{ 'type':'string', 'optional':False, 'description':'The path to the config. file (fs or Augeas path)', }, 'hierarchy':{ 'type':'string', 'optional':True, 'default':'/files', 'description':'The augeas base path hierarchy' } }, 'description':"Print all tree children nodes from the path provided." }, 'rm':{ 'args':{ 'entryPath':{ 'type':'string', 'optional':False, 'description':'The path to the config. file (fs or Augeas path)', }, 'param':{ 'type':'string', 'optional':True, 'default':'', 'description':'The target parameter in the config. file' }, 'hierarchy':{ 'type':'string', 'optional':True, 'default':'/files', 'description':'The augeas base path hierarchy' } }, 'description':"Delete a parameter (and all its children) in a config. file." } }
Here is a brief overview of using the module confmgt_augeas for func. This module relies on Augeas (htt://augeas.net). Augeas is a configuration API, for handling (set, get, list...) the parameters of configuration files on the func minions. It is inspired from the 'Quick tour' of Augeas (http://augeas.net/tour.html) Below, the guinea pig minion is named 'kermit'. - Set a parameter in a configuration file: i.e. PermitRootLogin yes in sshd_config func 'kermit' call confmgt_augeas set '/etc/ssh/sshd_config' 'PermitRootLogin' 'yes' {'kermit': {'parameter': 'PermitRootLogin', 'path': '/etc/ssh/sshd_config', 'value': 'yes'} } The arguments are config. file, parameter and value. - Get a parameter in a configuration file: i.e. port in sshd_config func 'kermit' call confmgt_augeas get '/etc/ssh/sshd_config' 'Port' The arguments are config. file and parameter. {'kermit': {'parameter': 'Port', 'path': '/etc/ssh/sshd_config', 'value': '22'} } Most actions involve a file, a parameter, and a value. But Augeas purists would perhaps prefer using the very graceful path-like syntax of Augeas. They can do so: func 'kermit' call confmgt_augeas get '/etc/ssh/sshd_config/Port' {'kermit': {'parameter': 'Port', 'path': '/etc/ssh/sshd_config', 'value': '22'} } - Make sshd accept an additional environment variable The example is in python this time. In sshd_config some settings can be repeated in the file, and values are accumulated. These values are best viewed as arrays. To illustrate this, we will add a new environment variable FOO to the AcceptEnv setting in /etc/ssh/sshd_config. These values are mapped into a tree (see http://augeas.net for more details on augeas schemas, tree an d path expressions). import func.overlord.client as fc c = fc.Client("kermit") print c.confmgt_augeas.printconf('/etc/ssh/sshd_config/AcceptEnv') If sshd_config on minion 'kermit' contains: AcceptEnv LANG LC_CTYPE AcceptEnv LC_IDENTIFICATION LC_ALL FOO You'll get: {'kermit': {'path': '/etc/ssh/sshd_config/AcceptEnv', 'nodes': [ ['/etc/ssh/sshd_config/AcceptEnv', '(none)'], ['/etc/ssh/sshd_config/AcceptEnv[1]/1', 'LANG'], ['/etc/ssh/sshd_config/AcceptEnv[1]/2', 'LC_CTYPE'], ['/etc/ssh/sshd_config/AcceptEnv[2]/3', 'LC_IDENTIFICATION'], ['/etc/ssh/sshd_config/AcceptEnv[2]/4', 'LC_ALL'], ] } } To add a new variable FOO at the end of the last AcceptEnv line, we perform print c.confmgt_augeas.set('/etc/ssh/sshd_config/AcceptEnv[last()]','10000','FOO') Which gives: {'kermit': {'path': '/etc/ssh/sshd_config/AcceptEnv[last()]', 'parameter': '10000', 'value': 'FOO'}} After the action (on the target minion), sshd_config contains: AcceptEnv LANG LC_CTYPE AcceptEnv LC_IDENTIFICATION LC_ALL FOO The addition of [last()] to AcceptEnv in the path tells Augeas that we are talking about the last node named AcceptEnv. Augeas requires that for a set. The path expression corresponds either to an existing node, or to no node at all (in which case a new node is created). '10000' is 'very big' to be sure we add the value in last position.
#!/usr/bin/env python import sys import func.overlord.client as fc c = fc.Client("*") print 'Delete the Parameter PermitRootLogin in sshd_config' print c.confmgt_augeas.rm('/etc/ssh/sshd_config','PermitRootLogin') print print 'Delete the Parameter Port in sshd_config with an Augeas-style path' print c.confmgt_augeas.rm('/etc/ssh/sshd_config/Port') print print 'Get sshd_config Port value.' print c.confmgt_augeas.get('/etc/ssh/sshd_config','Port') print print 'Set Port to 22 in sshd_config' print c.confmgt_augeas.set('/etc/ssh/sshd_config','Port','22') print print 'Get sshd_config Port value.' print c.confmgt_augeas.get('/etc/ssh/sshd_config','Port') print print 'Try to delete a non existant parameter in sshd_config' print c.confmgt_augeas.rm('/etc/ssh/sshd_config','Nawak') print print 'Try to delete a parameter in a non existant file.' print c.confmgt_augeas.rm('/etc/ssh/nimp','Nawak') print print 'Get sshd_config PermitRootLogin value.' print c.confmgt_augeas.get('/etc/ssh/sshd_config','PermitRootLogin') print print 'Set PermitRootLogin to yes in sshd_config' print c.confmgt_augeas.set('/etc/ssh/sshd_config','PermitRootLogin','yes') print print 'Set PermitRootLogin to no in sshd_config with an Augeas-style path.' print c.confmgt_augeas.set('/etc/ssh/sshd_config/PermitRootLogin','','no') print print 'Set PermitRootLogin to yes in sshd_config with an Augeas-style path.' print c.confmgt_augeas.set('/etc/ssh/sshd_config/PermitRootLogin','','yes') print print 'Get sshd_config PermitRootLogin value.' print c.confmgt_augeas.get('/etc/ssh/sshd_config','PermitRootLogin') print print 'Get sshd_config PermitRootLogin value with an Augeas-style path.' print c.confmgt_augeas.get('/etc/ssh/sshd_config/PermitRootLogin') print print 'Attempt to get a value for a non existant param in sshd_config' print c.confmgt_augeas.get('/etc/ssh/sshd_config','Nawak') print print 'Attempt to get a value for an empty param in sshd_config' print c.confmgt_augeas.get('/etc/ssh/sshd_config','Subsystem') print print 'Search for conf. entry in hosts file with canonical hostname = pim' print c.confmgt_augeas.match('/etc/hosts','canonical','pim') print #print 'List all direct children of hosts (not very useful)' #print c.confmgt_augeas.ls('/etc/hosts/*') #print print 'List all direct children parameters of 1st hosts entry.' for host,paramlist in c.confmgt_augeas.ls('/etc/hosts/1').iteritems(): print "Host: "+host for node in paramlist['nodes']: print node[0]+" = "+node[1] print print 'List all children nodes of 1st hosts entry.' for host,paramlist in c.confmgt_augeas.printconf('/etc/hosts/1').iteritems(): print "Host: "+host for node in paramlist['nodes']: print node[0]+" = "+node[1] print print 'Get values of 1st host entry.' print c.confmgt_augeas.get('/etc/hosts/','1') print print 'List all values for parameter of 1st fstab entry.' minionDict=c.confmgt_augeas.ls('/etc/fstab/1') for host,entry in minionDict.iteritems(): print "Host: "+host print "Entry path: "+entry['path'] for node in entry['nodes']: print node[0]+" = "+node[1] print print 'Get ipaddr of /etc/hosts 1st entry.' print c.confmgt_augeas.get('/etc/hosts/1','ipaddr') print # #print 'List all direct children parameters of sshd_config' #for host,paramlist in c.confmgt_augeas.ls('/etc/ssh/sshd_config').iteritems(): # print "Host: "+host # for node in paramlist['nodes']: # print node[0]+" = "+node[1] #print # #print 'List all children nodes of sshd_config' #for host,paramlist in c.confmgt_augeas.printconf('/etc/ssh/sshd_config').iteritems(): # print "Host: "+host # for node in paramlist['nodes']: # print node[0]+" = "+node[1] #print # print 'List all direct children of AcceptEnv entries in sshd_config' for host,paramlist in c.confmgt_augeas.ls('/etc/ssh/sshd_config/AcceptEnv').iteritems(): print "Host: "+host for node in paramlist['nodes']: print node[0]+" = "+node[1] print print 'See all AcceptEnv entries in sshd_config' for host,paramlist in c.confmgt_augeas.printconf('/etc/ssh/sshd_config/AcceptEnv').iteritems(): print "Host: "+host for node in paramlist['nodes']: print node[0]+" = "+node[1] print print 'Try to match PermitRootLogin yes in sshd_config' print c.confmgt_augeas.match('/etc/ssh/sshd_config','PermitRootLogin','yes') print print 'Try to match PermitRootLogin yes in sshd_config with an Augeas-style path' print c.confmgt_augeas.match('/etc/ssh/sshd_config/PermitRootLogin','','yes') print print 'Try to match PermitRootLogin yes in some config. files.' print c.confmgt_augeas.match('/etc/*/*','PermitRootLogin','yes') print print 'Try to match AcceptEnv in sshd_config' print c.confmgt_augeas.match('/etc/ssh/sshd_config','AcceptEnv') print print 'Try to match PermitRootLogin in sshd_config' print c.confmgt_augeas.match('/etc/ssh/sshd_config','PermitRootLogin') print print 'Try to match PermitRootLogin in sshd_config with an Augeas-style path.' print c.confmgt_augeas.match('/etc/ssh/sshd_config/PermitRootLogin') print print 'Try to match canonical entries in hosts file.' print c.confmgt_augeas.match('/etc/hosts','canonical') print print 'Try to match canonical entries in hosts file with an Augeas-style path.' print c.confmgt_augeas.match('/etc/hosts/*/canonical') print print 'Augeas metainformation.' print c.confmgt_augeas.ls('/','augeas') print c.confmgt_augeas.get('/','save','augeas') print c.confmgt_augeas.set('/','save','backup','augeas') print c.confmgt_augeas.get('/','save','augeas') print c.confmgt_augeas.get('/files/etc/hosts/lens/','info','augeas') print print 'Change the (canonical) hostname associated to a specific IP in hosts file.' hostfile='/etc/hosts' ip='192.168.0.253' newCanonical='fozzie' #newCanonical='piggy' # We search which entry in /etc/hosts refers to the IP ipmatch = c.confmgt_augeas.match(hostfile,'ipaddr',ip) # for each minion concerned for host,entry in ipmatch.iteritems(): # The first and unique entry in the list, entry[0], is what we searched for # We check that the target canonical hostname is not already set oldCanonical=c.confmgt_augeas.get(entry[0],'canonical')[host]['value'] if oldCanonical != newCanonical: print c.confmgt_augeas.set(entry[0],'canonical',newCanonical) else: print 'Nothing to do' print print 'Add a new variable FOO at the end of the last AcceptEnv line of sshd_config' print "And we don't want to do this twice." foomatch=c.confmgt_augeas.match('/etc/ssh/sshd_config','AcceptEnv/*','FOO') for host,matchlist in foomatch.iteritems(): if not matchlist: client = fc.Client(host) print client.confmgt_augeas.set('/etc/ssh/sshd_config/AcceptEnv[last()]','10000','FOO') print
Attachment:
test-confmgt_augeas.out
Description: Binary data
_______________________________________________ Func-list mailing list Func-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/func-list