Add support for augeas loading of non stock lenses

Fixes #30131

Pylint cleanup

Updated tests
This commit is contained in:
Andrew Colin Kissa 2016-01-04 14:48:44 +02:00
parent 752c657f92
commit a8fe0df0ac
3 changed files with 181 additions and 25 deletions

View File

@ -100,7 +100,29 @@ def _lstrip_word(word, prefix):
return word
def execute(context=None, lens=None, commands=()):
def _check_load_paths(load_path):
'''
Checks the validity of the load_path, returns a sanitized version
with invalid paths removed.
'''
if load_path is None or not isinstance(load_path, six.string_types):
return None
_paths = []
for _path in load_path.split(':'):
if os.path.isabs(_path) and os.path.isdir(_path):
_paths.append(_path)
else:
log.info('Invalid augeas_cfg load_path entry: %s removed', _path)
if len(_paths) == 0:
return None
return ':'.join(_paths)
def execute(context=None, lens=None, commands=(), load_path=None):
'''
Execute Augeas commands
@ -110,7 +132,24 @@ def execute(context=None, lens=None, commands=()):
.. code-block:: bash
salt '*' augeas.execute /files/etc/redis/redis.conf commands='["set bind 0.0.0.0", "set maxmemory 1G"]'
salt '*' augeas.execute /files/etc/redis/redis.conf \\
commands='["set bind 0.0.0.0", "set maxmemory 1G"]'
context
The Augeas context
lens
The Augeas lens to use
commands
The Augeas commands to execute
.. versionadded:: Boron
load_path
A colon-spearated list of directories that modules should be searched
in. This is in addition to the standard load path and the directories
in AUGEAS_LENS_LIB.
'''
ret = {'retval': False}
@ -138,8 +177,10 @@ def execute(context=None, lens=None, commands=()):
else:
return context
load_path = _check_load_paths(load_path)
flags = _Augeas.NO_MODL_AUTOLOAD if lens and context else _Augeas.NONE
aug = _Augeas(flags=flags)
aug = _Augeas(flags=flags, loadpath=load_path)
if lens and context:
aug.add_transform(lens, re.sub('^/files', '', context))
@ -147,7 +188,8 @@ def execute(context=None, lens=None, commands=()):
for command in commands:
try:
# first part up to space is always the command name (i.e.: set, move)
# first part up to space is always the
# command name (i.e.: set, move)
cmd, arg = command.split(' ', 1)
if cmd not in METHOD_MAP:
@ -178,9 +220,13 @@ def execute(context=None, lens=None, commands=()):
elif method == 'insert':
label, where, path = parts
if where not in ('before', 'after'):
raise ValueError('Expected "before" or "after", not {0}'.format(where))
raise ValueError(
'Expected "before" or "after", not {0}'.format(where))
path = make_path(path)
args = {'path': path, 'label': label, 'before': where == 'before'}
args = {
'path': path,
'label': label,
'before': where == 'before'}
elif method == 'remove':
path = make_path(parts[0])
args = {'path': path}
@ -212,7 +258,7 @@ def execute(context=None, lens=None, commands=()):
return ret
def get(path, value=''):
def get(path, value='', load_path=None):
'''
Get a value for a specific augeas path
@ -221,8 +267,23 @@ def get(path, value=''):
.. code-block:: bash
salt '*' augeas.get /files/etc/hosts/1/ ipaddr
path
The path to get the value of
value
The optional value to get
.. versionadded:: Boron
load_path
A colon-spearated list of directories that modules should be searched
in. This is in addition to the standard load path and the directories
in AUGEAS_LENS_LIB.
'''
aug = _Augeas()
load_path = _check_load_paths(load_path)
aug = _Augeas(loadpath=load_path)
ret = {}
path = path.rstrip('/')
@ -279,10 +340,24 @@ def setvalue(*args):
%wheel ALL = PASSWD : ALL , NOPASSWD : /usr/bin/apt-get , /usr/bin/aptitude
'''
aug = _Augeas()
load_path = None
load_paths = [x for x in args if str(x).startswith('load_path=')]
if load_paths:
if len(load_paths) > 1:
raise SaltInvocationError(
'Only one \'load_path=\' value is permitted'
)
else:
load_path = load_paths[0].split('=', 1)[1]
load_path = _check_load_paths(load_path)
aug = _Augeas(loadpath=load_path)
ret = {'retval': False}
tuples = [x for x in args if not str(x).startswith('prefix=')]
tuples = [
x for x in args
if not str(x).startswith('prefix=') and
not str(x).startswith('load_path=')]
prefix = [x for x in args if str(x).startswith('prefix=')]
if prefix:
if len(prefix) > 1:
@ -313,7 +388,7 @@ def setvalue(*args):
return ret
def match(path, value=''):
def match(path, value='', load_path=None):
'''
Get matches for path expression
@ -322,8 +397,23 @@ def match(path, value=''):
.. code-block:: bash
salt '*' augeas.match /files/etc/services/service-name ssh
path
The path to match
value
The value to match on
.. versionadded:: Boron
load_path
A colon-spearated list of directories that modules should be searched
in. This is in addition to the standard load path and the directories
in AUGEAS_LENS_LIB.
'''
aug = _Augeas()
load_path = _check_load_paths(load_path)
aug = _Augeas(loadpath=load_path)
ret = {}
try:
@ -339,7 +429,7 @@ def match(path, value=''):
return ret
def remove(path):
def remove(path, load_path=None):
'''
Get matches for path expression
@ -347,9 +437,22 @@ def remove(path):
.. code-block:: bash
salt '*' augeas.remove /files/etc/sysctl.conf/net.ipv4.conf.all.log_martians
salt '*' augeas.remove \\
/files/etc/sysctl.conf/net.ipv4.conf.all.log_martians
path
The path to remove
.. versionadded:: Boron
load_path
A colon-spearated list of directories that modules should be searched
in. This is in addition to the standard load path and the directories
in AUGEAS_LENS_LIB.
'''
aug = _Augeas()
load_path = _check_load_paths(load_path)
aug = _Augeas(loadpath=load_path)
ret = {'retval': False}
try:
count = aug.remove(path)
@ -366,7 +469,7 @@ def remove(path):
return ret
def ls(path): # pylint: disable=C0103
def ls(path, load_path=None): # pylint: disable=C0103
'''
List the direct children of a node
@ -375,6 +478,16 @@ def ls(path): # pylint: disable=C0103
.. code-block:: bash
salt '*' augeas.ls /files/etc/passwd
path
The path to list
.. versionadded:: Boron
load_path
A colon-spearated list of directories that modules should be searched
in. This is in addition to the standard load path and the directories
in AUGEAS_LENS_LIB.
'''
def _match(path):
''' Internal match function '''
@ -388,7 +501,9 @@ def ls(path): # pylint: disable=C0103
ret[_ma] = aug.get(_ma)
return ret
aug = _Augeas()
load_path = _check_load_paths(load_path)
aug = _Augeas(loadpath=load_path)
path = path.rstrip('/') + '/'
match_path = path + '*'
@ -405,7 +520,7 @@ def ls(path): # pylint: disable=C0103
return ret
def tree(path):
def tree(path, load_path=None):
'''
Returns recursively the complete tree of a node
@ -414,8 +529,20 @@ def tree(path):
.. code-block:: bash
salt '*' augeas.tree /files/etc/
path
The base of the recursive listing
.. versionadded:: Boron
load_path
A colon-spearated list of directories that modules should be searched
in. This is in addition to the standard load path and the directories
in AUGEAS_LENS_LIB.
'''
aug = _Augeas()
load_path = _check_load_paths(load_path)
aug = _Augeas(loadpath=load_path)
path = path.rstrip('/') + '/'
match_path = path

View File

@ -107,7 +107,8 @@ def _check_filepath(changes):
return filename
def change(name, context=None, changes=None, lens=None, **kwargs):
def change(name, context=None, changes=None, lens=None,
load_path=None, **kwargs):
'''
.. versionadded:: 2014.7.0
@ -141,13 +142,21 @@ def change(name, context=None, changes=None, lens=None, **kwargs):
changes
List of changes that are issued to Augeas. Available commands are
``set``, ``setm``, ``mv``/``move``, ``ins``/``insert``, and ``rm``/``remove``.
``set``, ``setm``, ``mv``/``move``, ``ins``/``insert``, and
``rm``/``remove``.
lens
The lens to use, needs to be suffixed with `.lns`, e.g.: `Nginx.lns`. See
the `list of stock lenses <http://augeas.net/stock_lenses.html>`_
The lens to use, needs to be suffixed with `.lns`, e.g.: `Nginx.lns`.
See the `list of stock lenses <http://augeas.net/stock_lenses.html>`_
shipped with Augeas.
.. versionadded:: Boron
load_path
A list of directories that modules should be searched in. This is in
addition to the standard load path and the directories in
AUGEAS_LENS_LIB.
Usage examples:
@ -238,6 +247,13 @@ def change(name, context=None, changes=None, lens=None, **kwargs):
ret['comment'] = '\'changes\' must be specified as a list'
return ret
if load_path is not None:
if not isinstance(load_path, list):
ret['comment'] = '\'load_path\' must be specified as a list'
return ret
else:
load_path = ':'.join(load_path)
filename = None
if context is None:
try:
@ -262,7 +278,9 @@ def change(name, context=None, changes=None, lens=None, **kwargs):
with salt.utils.fopen(filename, 'r') as file_:
old_file = file_.readlines()
result = __salt__['augeas.execute'](context=context, lens=lens, commands=changes)
result = __salt__['augeas.execute'](
context=context, lens=lens,
commands=changes, load_path=load_path)
ret['result'] = result['retval']
if ret['result'] is False:
@ -271,7 +289,8 @@ def change(name, context=None, changes=None, lens=None, **kwargs):
if old_file:
with salt.utils.fopen(filename, 'r') as file_:
diff = ''.join(difflib.unified_diff(old_file, file_.readlines(), n=0))
diff = ''.join(
difflib.unified_diff(old_file, file_.readlines(), n=0))
if diff:
ret['comment'] = 'Changes have been saved'

View File

@ -67,6 +67,16 @@ class AugeasTestCase(TestCase):
self.assertDictEqual(augeas.change(self.name), self.ret)
def test_change_non_list_load_path(self):
'''
Test if none list load_path is handled correctly
'''
comt = ('\'load_path\' must be specified as a list')
self.ret.update({'comment': comt})
self.assertDictEqual(augeas.change(
self.name, self.context, self.changes, load_path='x'), self.ret)
def test_change_in_test_mode(self):
'''
Test test mode handling