Update states/pkg, modules/ebuildpkg (#84)

This commit is contained in:
niku64 2020-04-06 13:47:03 +03:00 committed by GitHub
parent 78c84e3d19
commit 6cc508a9b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 699 additions and 1482 deletions

View File

@ -13,19 +13,27 @@ Support for Portage
For now all package names *MUST* include the package category,
i.e. ``'vim'`` will not work, ``'app-editors/vim'`` will.
'''
from __future__ import absolute_import, print_function, unicode_literals
# Import python libs
import os
import copy
import logging
import re
import importlib
import datetime
# Import salt libs
import salt.utils
import salt.utils.args
import salt.utils.compat
import salt.utils.data
import salt.utils.functools
import salt.utils.path
import salt.utils.pkg
import salt.utils.systemd
import salt.utils.versions
from salt.exceptions import CommandExecutionError, MinionError
import salt.ext.six as six
from salt.ext import six
# Import third party libs
HAS_PORTAGE = False
@ -49,6 +57,21 @@ log = logging.getLogger(__name__)
# Define the module's virtual name
__virtualname__ = 'pkg'
r_gt_lt_eq_verstr = re.compile(r'^([<>])?(=)?([^<>=]*)$')
r_split_ver = re.compile(r'^~?([^:\[]+)[\:\[]?.*$')
r_split_repo = re.compile(r'^.+::([^\[]+).*$')
r_strip_ver = re.compile(r'^~?[<>]?=?([^<>=:\[]+).*$')
r_find_upgradeable = re.compile(r'(?m)^\[.+\] '
r'([^ ]+/[^ ]+)' # Package string
'-'
r'([0-9]+[^ ]+)' # Version
r'.*$')
r_find_changes_in_section = re.compile(r'^[<>=][^ ]+/[^ ]+ [^\n]+', re.M)
r_find_slot_conflicts = re.compile(r'^[^ \n]+/[^ ]+:[^ ]', re.M)
r_find_blocked = re.compile(r'(?m)^\[blocks .+\] '
r'([^ ]+/[^ ]+-[0-9]+[^ ]+)'
r'.*$')
r_find_unsatisfied = re.compile(r'Error: The above package list contains')
def __virtual__():
'''
@ -61,21 +84,38 @@ def __virtual__():
def _vartree():
import portage # pylint: disable=3rd-party-module-not-gated
portage = importlib.reload(portage)
portage = salt.utils.compat.reload(portage)
return portage.db[portage.root]['vartree']
def _porttree():
import portage # pylint: disable=3rd-party-module-not-gated
portage = importlib.reload(portage)
portage = salt.utils.compat.reload(portage)
return portage.db[portage.root]['porttree']
def _p_to_cp(p):
ret = _porttree().dbapi.xmatch("match-all", p)
if ret:
return portage.cpv_getkey(ret[0])
log.error('_p_to_cp: Package {0} not found by xmatch'.format(p))
try:
ret = portage.dep_getkey(p)
if ret:
return ret
except portage.exception.InvalidAtom:
pass
try:
ret = _porttree().dbapi.xmatch('bestmatch-visible', p)
if ret:
return portage.dep_getkey(ret)
except portage.exception.InvalidAtom:
pass
try:
ret = _porttree().dbapi.xmatch("match-all", p)
if ret:
return portage.cpv_getkey(ret[0])
except portage.exception.InvalidAtom:
pass
return None
@ -89,11 +129,21 @@ def _allnodes():
def _cpv_to_cp(cpv):
ret = portage.cpv_getkey(cpv)
if ret:
return ret
else:
return cpv
try:
ret = portage.dep_getkey(cpv)
if ret:
return ret
except portage.exception.InvalidAtom:
pass
try:
ret = portage.cpv_getkey(cpv)
if ret:
return ret
except portage.exception.InvalidAtom:
pass
return cpv
def _cpv_to_version(cpv):
@ -105,18 +155,14 @@ def _process_emerge_err(stdout, stderr):
Used to parse emerge output to provide meaningful output when emerge fails
'''
ret = {}
rexp = re.compile(r'^[<>=][^ ]+/[^ ]+ [^\n]+', re.M)
slot_conflicts = re.compile(r'^[^ \n]+/[^ ]+:[^ ]', re.M).findall(stderr)
slot_conflicts = r_find_slot_conflicts.findall(stderr)
if slot_conflicts:
ret['slot conflicts'] = slot_conflicts
blocked = re.compile(r'(?m)^\[blocks .+\] '
r'([^ ]+/[^ ]+-[0-9]+[^ ]+)'
r'.*$').findall(stdout)
blocked = r_find_blocked.findall(stdout)
unsatisfied = re.compile(
r'Error: The above package list contains').findall(stderr)
unsatisfied = r_find_unsatisfied.findall(stderr)
# If there were blocks and emerge could not resolve it.
if blocked and unsatisfied:
@ -125,13 +171,13 @@ def _process_emerge_err(stdout, stderr):
sections = re.split('\n\n', stderr)
for section in sections:
if 'The following keyword changes' in section:
ret['keywords'] = rexp.findall(section)
ret['keywords'] = r_find_changes_in_section.findall(section)
elif 'The following license changes' in section:
ret['license'] = rexp.findall(section)
ret['license'] = r_find_changes_in_section.findall(section)
elif 'The following USE changes' in section:
ret['use'] = rexp.findall(section)
ret['use'] = r_find_changes_in_section.findall(section)
elif 'The following mask changes' in section:
ret['mask'] = rexp.findall(section)
ret['mask'] = r_find_changes_in_section.findall(section)
return ret
@ -220,7 +266,7 @@ def latest_version(*names, **kwargs):
salt '*' pkg.latest_version <package name>
salt '*' pkg.latest_version <package1> <package2> <package3> ...
'''
refresh = salt.utils.is_true(kwargs.pop('refresh', True))
refresh = salt.utils.data.is_true(kwargs.pop('refresh', True))
if len(names) == 0:
return ''
@ -235,7 +281,7 @@ def latest_version(*names, **kwargs):
ret[name] = ''
installed = _cpv_to_version(_vartree().dep_bestmatch(name))
avail = _cpv_to_version(_porttree().dep_bestmatch(name))
if avail and (not installed or salt.utils.compare_versions(ver1=installed, oper='<', ver2=avail, cmp_func=version_cmp)):
if avail and (not installed or salt.utils.versions.compare(ver1=installed, oper='<', ver2=avail, cmp_func=version_cmp)):
ret[name] = avail
# Return a string if only one package name passed
@ -244,6 +290,10 @@ def latest_version(*names, **kwargs):
return ret
# available_version is being deprecated
available_version = salt.utils.functools.alias_function(latest_version, 'available_version')
def _get_upgradable(backtrack=3):
'''
Utility function to get upgradable packages
@ -275,15 +325,10 @@ def _get_upgradable(backtrack=3):
else:
out = call['stdout']
rexp = re.compile(r'(?m)^\[.+\] '
r'([^ ]+/[^ ]+)' # Package string
'-'
r'([0-9]+[^ ]+)' # Version
r'.*$')
keys = ['name', 'version']
_get = lambda l, k: l[keys.index(k)]
upgrades = rexp.findall(out)
upgrades = r_find_upgradeable.findall(out)
ret = {}
for line in upgrades:
@ -314,7 +359,7 @@ def list_upgrades(refresh=True, backtrack=3, **kwargs): # pylint: disable=W0613
salt '*' pkg.list_upgrades
'''
if salt.utils.is_true(refresh):
if salt.utils.data.is_true(refresh):
refresh_db()
return _get_upgradable(backtrack)
@ -345,21 +390,8 @@ def version(*names, **kwargs):
salt '*' pkg.version <package name>
salt '*' pkg.version <package1> <package2> <package3> ...
'''
if len(names) == 0:
return ''
return __salt__['pkg_resource.version'](*names, **kwargs)
ret = {}
# Initialize the dict with empty strings
for name in names:
ret[name] = ''
installed = _cpv_to_version(_vartree().dep_bestmatch(name))
if installed:
ret[name] = installed
# Return a string if only one package name passed
if len(names) == 1:
return ret[names[0]]
return ret
def porttree_matches(name):
'''
@ -387,9 +419,9 @@ def list_pkgs(versions_as_list=False, **kwargs):
salt '*' pkg.list_pkgs
'''
versions_as_list = salt.utils.is_true(versions_as_list)
versions_as_list = salt.utils.data.is_true(versions_as_list)
# not yet implemented or not applicable
if any([salt.utils.is_true(kwargs.get(x))
if any([salt.utils.data.is_true(kwargs.get(x))
for x in ('removed', 'purge_desired')]):
return {}
@ -416,7 +448,21 @@ def list_pkgs(versions_as_list=False, **kwargs):
def refresh_db():
'''
Updates the portage tree (emerge --sync). Uses eix-sync if available.
Update the portage tree using the first available method from the following
list:
- emaint sync
- eix-sync
- emerge-webrsync
- emerge --sync
To prevent the portage tree from being synced within one day of the
previous sync, add the following pillar data for this minion:
.. code-block:: yaml
portage:
sync_wait_one_day: True
CLI Example:
@ -424,28 +470,37 @@ def refresh_db():
salt '*' pkg.refresh_db
'''
has_emaint = os.path.isdir('/etc/portage/repos.conf')
has_eix = True if 'eix.sync' in __salt__ else False
has_webrsync = True if __salt__['makeconf.features_contains']('webrsync-gpg') else False
# Remove rtag file to keep multiple refreshes from happening in pkg states
salt.utils.pkg.clear_rtag(__opts__)
if 'eix.sync' in __salt__:
return __salt__['eix.sync']()
# Option to prevent syncing package tree if done in the last 24 hours
if __salt__['pillar.get']('portage:sync_wait_one_day', False):
main_repo_root = __salt__['cmd.run']('portageq get_repo_path / gentoo')
day = datetime.timedelta(days=1)
now = datetime.datetime.now()
timestamp = datetime.datetime.fromtimestamp(os.path.getmtime(main_repo_root))
if now - timestamp < day:
log.info('Did not sync package tree since last sync was done at'
' {0}, less than 1 day ago'.format(timestamp))
return False
if 'makeconf.features_contains'in __salt__ and __salt__['makeconf.features_contains']('webrsync-gpg'):
# GPG sign verify is supported only for "webrsync"
if has_emaint:
return __salt__['cmd.retcode']('emaint sync -a') == 0
elif has_eix:
return __salt__['eix.sync']()
elif has_webrsync:
# GPG sign verify is supported only for 'webrsync'
cmd = 'emerge-webrsync -q'
# We prefer 'delta-webrsync' to 'webrsync'
if salt.utils.which('emerge-delta-webrsync'):
# Prefer 'delta-webrsync' to 'webrsync'
if salt.utils.path.which('emerge-delta-webrsync'):
cmd = 'emerge-delta-webrsync -q'
return __salt__['cmd.retcode'](cmd, python_shell=False) == 0
return __salt__['cmd.retcode'](cmd) == 0
else:
if __salt__['cmd.retcode']('emerge --ask n --quiet --sync',
python_shell=False) == 0:
return True
# We fall back to "webrsync" if "rsync" fails for some reason
cmd = 'emerge-webrsync -q'
# We prefer 'delta-webrsync' to 'webrsync'
if salt.utils.which('emerge-delta-webrsync'):
cmd = 'emerge-delta-webrsync -q'
return __salt__['cmd.retcode'](cmd, python_shell=False) == 0
# Default to deprecated `emerge --sync` form
return __salt__['cmd.retcode']('emerge --ask n --quiet --sync') == 0
def _flags_changed(inst_flags, conf_flags):
@ -466,72 +521,6 @@ def _flags_changed(inst_flags, conf_flags):
return True
return True if conf_flags else False
def process_target(param, version_num):
installed = _cpv_to_version(_vartree().dep_bestmatch(param))
if version_num is None:
keyword, prefix = None, None
target = param
else:
match = re.match('^(~|-|\*)?([<>]?=?)?([^<>=]*)$', version_num)
if match:
keyword, prefix, verstr = match.groups()
# If no prefix characters were supplied and verstr contains a version, use '='
if len(verstr) > 0 and verstr[0] != ':' and verstr[0] != '[':
prefix = prefix or '='
target = '{0}{1}-{2}'.format(prefix, param, verstr)
else:
target = '{0}{1}'.format(param, verstr)
else:
raise AttributeError(
'Unable to parse version {0} of {1}'.\
format(repr(version_num), param))
changes = {}
if '[' in target:
# Clean target from use flags, since some of them may not exist.
# This will raise an AttributeError, or some weird stack trace if portage_config is not patched.
uses = portage.dep.dep_getusedeps(target)
pv_target = target[:target.rfind('[')]
if installed:
old = __salt__['portage_config.get_flags_from_package_conf']('use', pv_target)
else:
old = []
__salt__['portage_config.append_use_flags'](pv_target, uses)
new = __salt__['portage_config.get_flags_from_package_conf']('use', pv_target)
if old != new:
changes[pv_target] = {'old': {'use': old},
'new': {'use': new}}
else:
pv_target = target
if keyword is not None:
if keyword == '~':
kws = ['~*']
elif keyword == '*':
kws = ['**']
elif keyword == '-':
# Or [''] ?
kws = ['~ARCH']
else:
kws = [keyword]
if installed:
old = __salt__['portage_config.get_flags_from_package_conf']('accept_keywords', pv_target)
else:
old = []
__salt__['portage_config.append_to_package_conf']('accept_keywords', pv_target, kws)
new = __salt__['portage_config.get_flags_from_package_conf']('accept_keywords', pv_target)
if old != new:
changes[pv_target] = {'old': {'accept_keywords': old},
'new': {'accept_keywords': new}}
if installed and not changes:
# Check if package has some changed use.
all_uses = __salt__['portage_config.get_cleared_flags'](param)
if _flags_changed(*all_uses):
changes[pv_target] = {'old': {'use': all_uses[0]},
'new': {'use': all_uses[1]}}
return target, changes
def install(name=None,
refresh=False,
@ -543,6 +532,20 @@ def install(name=None,
binhost=None,
**kwargs):
'''
.. versionchanged:: 2015.8.12,2016.3.3,2016.11.0
On minions running systemd>=205, `systemd-run(1)`_ is now used to
isolate commands which modify installed packages from the
``salt-minion`` daemon's control group. This is done to keep systemd
from killing any emerge commands spawned by Salt when the
``salt-minion`` service is restarted. (see ``KillMode`` in the
`systemd.kill(5)`_ manpage for more information). If desired, usage of
`systemd-run(1)`_ can be suppressed by setting a :mod:`config option
<salt.modules.config.get>` called ``systemd.scope``, with a value of
``False`` (no quotes).
.. _`systemd-run(1)`: https://www.freedesktop.org/software/systemd/man/systemd-run.html
.. _`systemd.kill(5)`: https://www.freedesktop.org/software/systemd/man/systemd.kill.html
Install the passed package(s), add refresh=True to sync the portage tree
before package is installed.
@ -642,7 +645,7 @@ def install(name=None,
'binhost': binhost,
}
))
if salt.utils.is_true(refresh):
if salt.utils.data.is_true(refresh):
refresh_db()
try:
@ -655,9 +658,7 @@ def install(name=None,
# Handle version kwarg for a single package target
if pkgs is None and sources is None:
version_num = kwargs.get('version')
if version_num:
pkg_params = {name: version_num}
else:
if not version_num:
version_num = ''
if slot is not None:
version_num += ':{0}'.format(slot)
@ -681,27 +682,60 @@ def install(name=None,
else:
bin_opts = []
if 'oneshot' in kwargs and kwargs.get('oneshot') == True:
oneshot = ['--oneshot']
else:
oneshot = []
changes = {}
if not pkg_type == 'repository':
targets = pkg_params
else:
if pkg_type == 'repository':
targets = list()
for param, version_num in six.iteritems(pkg_params):
target, flag_changes = process_target(param, version_num)
changes.update(flag_changes)
targets.append(target)
original_param = param
param = _p_to_cp(param)
if param is None:
raise portage.dep.InvalidAtom(original_param)
if version_num is None:
targets.append(param)
else:
keyword = None
match = re.match(r_gt_lt_eq_verstr, version_num)
if match:
gt_lt, eq, verstr = match.groups()
prefix = gt_lt or ''
prefix += eq or ''
# If no prefix characters were supplied and verstr contains a version, use '='
if len(verstr) > 0 and verstr[0] != ':' and verstr[0] != '[':
prefix = prefix or '='
target = '{0}{1}-{2}'.format(prefix, param, verstr)
else:
target = '{0}{1}'.format(param, verstr)
else:
target = '{0}'.format(param)
if '[' in target:
target = target[:target.rfind('[')]
if not changes:
inst_v = version(param)
# Prevent latest_version from calling refresh_db. Either we
# just called it or we were asked not to.
if latest_version(param, refresh=False) == inst_v:
all_uses = __salt__['portage_config.get_cleared_flags'](param)
if _flags_changed(*all_uses):
changes[param] = {'version': inst_v,
'old': {'use': all_uses[0]},
'new': {'use': all_uses[1]}}
targets.append(target)
else:
targets = pkg_params
cmd = []
if salt.utils.systemd.has_scope(__context__) \
and __salt__['config.get']('systemd.scope', True):
cmd.extend(['systemd-run', '--scope'])
cmd.extend(['emerge', '--ask', 'n', '--quiet'])
cmd.extend(bin_opts)
cmd.extend(emerge_opts)
cmd.extend(oneshot)
cmd.extend(targets)
old = list_pkgs()
@ -715,22 +749,33 @@ def install(name=None,
__context__.pop('pkg.list_pkgs', None)
new = list_pkgs()
changes.update(salt.utils.compare_dicts(old, new))
changes.update(salt.utils.data.compare_dicts(old, new))
if needed_changes:
raise CommandExecutionError(
'Error occurred installing package(s)',
info={'needed changes': needed_changes, 'changes': changes}
)
if call['retcode'] != 0:
raise CommandExecutionError(
'unknown error stdout: {}, stderr: {}'.format(call['stdout'], call['stderr'])
)
return changes
def update(pkg, slot=None, fromrepo=None, refresh=False, binhost=None):
'''
.. versionchanged:: 2015.8.12,2016.3.3,2016.11.0
On minions running systemd>=205, `systemd-run(1)`_ is now used to
isolate commands which modify installed packages from the
``salt-minion`` daemon's control group. This is done to keep systemd
from killing any emerge commands spawned by Salt when the
``salt-minion`` service is restarted. (see ``KillMode`` in the
`systemd.kill(5)`_ manpage for more information). If desired, usage of
`systemd-run(1)`_ can be suppressed by setting a :mod:`config option
<salt.modules.config.get>` called ``systemd.scope``, with a value of
``False`` (no quotes).
.. _`systemd-run(1)`: https://www.freedesktop.org/software/systemd/man/systemd-run.html
.. _`systemd.kill(5)`: https://www.freedesktop.org/software/systemd/man/systemd.kill.html
Updates the passed package (emerge --update package)
slot
@ -756,7 +801,7 @@ def update(pkg, slot=None, fromrepo=None, refresh=False, binhost=None):
salt '*' pkg.update <package name>
'''
if salt.utils.is_true(refresh):
if salt.utils.data.is_true(refresh):
refresh_db()
full_atom = pkg
@ -768,14 +813,17 @@ def update(pkg, slot=None, fromrepo=None, refresh=False, binhost=None):
full_atom = '{0}::{1}'.format(full_atom, fromrepo)
if binhost == 'try':
bin_opts = ['--getbinpkg']
bin_opts = ['-g']
elif binhost == 'force':
bin_opts = ['--getbinpkgonly']
bin_opts = ['-G']
else:
bin_opts = []
old = list_pkgs()
cmd = []
if salt.utils.systemd.has_scope(__context__) \
and __salt__['config.get']('systemd.scope', True):
cmd.extend(['systemd-run', '--scope'])
cmd.extend(['emerge',
'--ask', 'n',
'--quiet',
@ -794,7 +842,7 @@ def update(pkg, slot=None, fromrepo=None, refresh=False, binhost=None):
__context__.pop('pkg.list_pkgs', None)
new = list_pkgs()
ret = salt.utils.compare_dicts(old, new)
ret = salt.utils.data.compare_dicts(old, new)
if needed_changes:
raise CommandExecutionError(
@ -807,6 +855,20 @@ def update(pkg, slot=None, fromrepo=None, refresh=False, binhost=None):
def upgrade(refresh=True, binhost=None, backtrack=3):
'''
.. versionchanged:: 2015.8.12,2016.3.3,2016.11.0
On minions running systemd>=205, `systemd-run(1)`_ is now used to
isolate commands which modify installed packages from the
``salt-minion`` daemon's control group. This is done to keep systemd
from killing any emerge commands spawned by Salt when the
``salt-minion`` service is restarted. (see ``KillMode`` in the
`systemd.kill(5)`_ manpage for more information). If desired, usage of
`systemd-run(1)`_ can be suppressed by setting a :mod:`config option
<salt.modules.config.get>` called ``systemd.scope``, with a value of
``False`` (no quotes).
.. _`systemd-run(1)`: https://www.freedesktop.org/software/systemd/man/systemd-run.html
.. _`systemd.kill(5)`: https://www.freedesktop.org/software/systemd/man/systemd.kill.html
Run a full system upgrade (emerge -uDN @world)
binhost
@ -839,7 +901,7 @@ def upgrade(refresh=True, binhost=None, backtrack=3):
'result': True,
'comment': ''}
if salt.utils.is_true(refresh):
if salt.utils.data.is_true(refresh):
refresh_db()
if binhost == 'try':
@ -851,6 +913,9 @@ def upgrade(refresh=True, binhost=None, backtrack=3):
old = list_pkgs()
cmd = []
if salt.utils.systemd.has_scope(__context__) \
and __salt__['config.get']('systemd.scope', True):
cmd.extend(['systemd-run', '--scope'])
cmd.extend(['emerge',
'--ask', 'n',
'--quiet',
@ -867,7 +932,7 @@ def upgrade(refresh=True, binhost=None, backtrack=3):
python_shell=False)
__context__.pop('pkg.list_pkgs', None)
new = list_pkgs()
ret = salt.utils.compare_dicts(old, new)
ret = salt.utils.data.compare_dicts(old, new)
if result['retcode'] != 0:
raise CommandExecutionError(
@ -880,6 +945,20 @@ def upgrade(refresh=True, binhost=None, backtrack=3):
def remove(name=None, slot=None, fromrepo=None, pkgs=None, **kwargs):
'''
.. versionchanged:: 2015.8.12,2016.3.3,2016.11.0
On minions running systemd>=205, `systemd-run(1)`_ is now used to
isolate commands which modify installed packages from the
``salt-minion`` daemon's control group. This is done to keep systemd
from killing any emerge commands spawned by Salt when the
``salt-minion`` service is restarted. (see ``KillMode`` in the
`systemd.kill(5)`_ manpage for more information). If desired, usage of
`systemd-run(1)`_ can be suppressed by setting a :mod:`config option
<salt.modules.config.get>` called ``systemd.scope``, with a value of
``False`` (no quotes).
.. _`systemd-run(1)`: https://www.freedesktop.org/software/systemd/man/systemd-run.html
.. _`systemd.kill(5)`: https://www.freedesktop.org/software/systemd/man/systemd.kill.html
Remove packages via emerge --unmerge.
name
@ -930,6 +1009,9 @@ def remove(name=None, slot=None, fromrepo=None, pkgs=None, **kwargs):
return {}
cmd = []
if salt.utils.systemd.has_scope(__context__) \
and __salt__['config.get']('systemd.scope', True):
cmd.extend(['systemd-run', '--scope'])
cmd.extend(['emerge',
'--ask', 'n',
'--quiet',
@ -949,7 +1031,7 @@ def remove(name=None, slot=None, fromrepo=None, pkgs=None, **kwargs):
__context__.pop('pkg.list_pkgs', None)
new = list_pkgs()
ret = salt.utils.compare_dicts(old, new)
ret = salt.utils.data.compare_dicts(old, new)
if errors:
raise CommandExecutionError(
@ -962,6 +1044,20 @@ def remove(name=None, slot=None, fromrepo=None, pkgs=None, **kwargs):
def purge(name=None, slot=None, fromrepo=None, pkgs=None, **kwargs):
'''
.. versionchanged:: 2015.8.12,2016.3.3,2016.11.0
On minions running systemd>=205, `systemd-run(1)`_ is now used to
isolate commands which modify installed packages from the
``salt-minion`` daemon's control group. This is done to keep systemd
from killing any emerge commands spawned by Salt when the
``salt-minion`` service is restarted. (see ``KillMode`` in the
`systemd.kill(5)`_ manpage for more information). If desired, usage of
`systemd-run(1)`_ can be suppressed by setting a :mod:`config option
<salt.modules.config.get>` called ``systemd.scope``, with a value of
``False`` (no quotes).
.. _`systemd-run(1)`: https://www.freedesktop.org/software/systemd/man/systemd-run.html
.. _`systemd.kill(5)`: https://www.freedesktop.org/software/systemd/man/systemd.kill.html
Portage does not have a purge, this function calls remove followed
by depclean to emulate a purge process
@ -1048,7 +1144,7 @@ def depclean(name=None, slot=None, fromrepo=None, pkgs=None):
python_shell=False)
__context__.pop('pkg.list_pkgs', None)
new = list_pkgs()
return salt.utils.compare_dicts(old, new)
return salt.utils.data.compare_dicts(old, new)
def version_cmp(pkg1, pkg2, **kwargs):
@ -1068,14 +1164,13 @@ def version_cmp(pkg1, pkg2, **kwargs):
# definition (and thus have it show up in the docs), we just pop it out of
# the kwargs dict and then raise an exception if any kwargs other than
# ignore_epoch were passed.
kwargs = salt.utils.clean_kwargs(**kwargs)
kwargs = salt.utils.args.clean_kwargs(**kwargs)
kwargs.pop('ignore_epoch', None)
if kwargs:
salt.utils.invalid_kwargs(kwargs)
salt.utils.args.invalid_kwargs(kwargs)
regex = r'^(?:~|-|\*)?([^:\[\~\*]+):?[^\[]*\[?.*$'
ver1 = re.match(regex, pkg1)
ver2 = re.match(regex, pkg2)
ver1 = r_split_ver.match(pkg1)
ver2 = r_split_ver.match(pkg2)
if ver1 and ver2:
return portage.versions.vercmp(ver1.group(1), ver2.group(1))
@ -1092,7 +1187,7 @@ def version_clean(version):
salt '*' pkg.version_clean <version_string>
'''
return re.match(r'^(?:~|-|\*)?[<>]?=?([^<>=:\[\~\-\*]+).*$', version)
return re.match(r_strip_ver, version)
def check_extra_requirements(pkgname, pkgver):
@ -1107,13 +1202,15 @@ def check_extra_requirements(pkgname, pkgver):
'''
keyword = None
match = re.match('^(~|-|\*)?([<>])?(=)?([^<>=]*)$', pkgver)
match = re.match('^(~)?([<>])?(=)?([^<>=]*)$', pkgver)
if match:
keyword, gt_lt, eq, verstr = match.groups()
prefix = gt_lt or ''
prefix += eq or ''
# We need to delete quotes around use flag list elements
verstr = verstr.replace("'", "")
# If no prefix characters were supplied and verstr contains a version, use '='
if len(verstr) > 0 and verstr[0] != ':' and verstr[0] != '[':
if verstr[0] != ':' and verstr[0] != '[':
prefix = prefix or '='
atom = '{0}{1}-{2}'.format(prefix, pkgname, verstr)
else:
@ -1135,26 +1232,16 @@ def check_extra_requirements(pkgname, pkgver):
except KeyError:
return False
des_repo = re.match(r'^.+::([^\[\~\-\*]+).*$', atom)
des_repo = re.match(r_split_repo, atom)
if des_repo and des_repo.group(1) != cur_repo:
return False
des_uses = set(portage.dep.dep_getusedeps(atom))
cur_use = cur_use.split()
# TODO: clarify this filter
# Filters out -flag that is not currently enabled thus not in cur_use
if len([x for x in des_uses.difference(cur_use)
if x[0] != '-' or x[1:] in cur_use]) > 0:
return False
if keyword is not None:
if keyword == '~':
v = '~*'
elif keyword == '*':
v = '**'
elif keyword == '-':
v = '~ARCH'
else:
v = keyword
if not __salt__['portage_config.has_flag']('accept_keywords', atom, v):
return False
return True

View File

@ -1,735 +0,0 @@
# -*- coding: utf-8 -*-
'''
Configure ``portage(5)``
'''
# Import python libs
import logging
import os
import shutil
import importlib
# Import salt libs
import salt.utils
from salt.utils.files import fopen
# Import third party libs
import salt.ext.six as six
# pylint: disable=import-error
try:
import portage
HAS_PORTAGE = True
except ImportError:
HAS_PORTAGE = False
import sys
if os.path.isdir('/usr/lib/portage/pym'):
try:
# In a virtualenv, the portage python path needs to be manually
# added
sys.path.insert(0, '/usr/lib/portage/pym')
import portage
HAS_PORTAGE = True
except ImportError:
pass
# pylint: enable=import-error
BASE_PATH = '/etc/portage/package.{0}'
SUPPORTED_CONFS = ('accept_keywords', 'env', 'license', 'mask', 'properties',
'unmask', 'use')
log = logging.getLogger(__name__)
def __virtual__():
'''
Confirm this module is on a Gentoo based system.
'''
if HAS_PORTAGE and __grains__['os'] == 'Gentoo':
return 'portage_config'
return (False, 'portage_config execution module cannot be loaded: only available on Gentoo with portage installed.')
def _get_portage():
'''
portage module must be reloaded or it can't catch the changes
in portage.* which had been added after when the module was loaded
'''
return importlib.reload(portage)
def _porttree():
return portage.db[portage.root]['porttree']
def _get_config_file(conf, atom):
'''
Parse the given atom, allowing access to its parts
Success does not mean that the atom exists, just that it
is in the correct format.
Returns none if the atom is invalid.
'''
if '*' in atom:
parts = portage.dep.Atom(atom, allow_wildcard=True)
if not parts:
raise AttributeError('Matching c/p form not found for atom {0}'.format(atom))
if parts.cp == '*/*':
# parts.repo will be empty if there is no repo part
relative_path = parts.repo or "gentoo"
else:
relative_path = os.path.join(*[x for x in os.path.split(parts.cp) if x != '*'])
else:
relative_path = _p_to_cp(atom)
if not relative_path:
raise AttributeError('Matching c/p form not found for atom {0}'.format(atom))
complete_file_path = BASE_PATH.format(conf) + '/' + relative_path
return complete_file_path
def _p_to_cp(p):
'''
Convert a package name or a DEPEND atom to category/package format.
Raises an exception if program name is ambiguous.
'''
ret = _porttree().dbapi.xmatch("match-all", p)
if ret:
return portage.cpv_getkey(ret[0])
log.error('_p_to_cp: Package {0} not found by xmatch'.format(p))
return None
def _get_cpv(cp, installed=True):
'''
add version to category/package
@cp - name of package in format category/name
@installed - boolean value, if False, function returns cpv
for latest available package
'''
if installed:
return _get_portage().db[portage.root]['vartree'].dep_bestmatch(cp)
else:
return _porttree().dep_bestmatch(cp)
def enforce_nice_config():
'''
Enforce a nice tree structure for /etc/portage/package.* configuration
files.
.. seealso::
:py:func:`salt.modules.ebuild.ex_mod_init`
for information on automatically running this when pkg is used.
CLI Example:
.. code-block:: bash
salt '*' portage_config.enforce_nice_config
'''
_convert_all_package_confs_to_dir()
_order_all_package_confs()
def _convert_all_package_confs_to_dir():
'''
Convert all /etc/portage/package.* configuration files to directories.
'''
for conf_file in SUPPORTED_CONFS:
_package_conf_file_to_dir(conf_file)
def _order_all_package_confs():
'''
Place all entries in /etc/portage/package.* config dirs in the correct
file.
'''
for conf_file in SUPPORTED_CONFS:
_package_conf_ordering(conf_file)
_unify_keywords()
def _unify_keywords():
'''
Merge /etc/portage/package.keywords and
/etc/portage/package.accept_keywords.
'''
old_path = BASE_PATH.format('keywords')
if os.path.exists(old_path):
if os.path.isdir(old_path):
for triplet in os.walk(old_path):
for file_name in triplet[2]:
file_path = '{0}/{1}'.format(triplet[0], file_name)
with fopen(file_path) as fh_:
for line in fh_:
line = line.strip()
if line and not line.startswith('#'):
append_to_package_conf(
'accept_keywords', string=line)
shutil.rmtree(old_path)
else:
with fopen(old_path) as fh_:
for line in fh_:
line = line.strip()
if line and not line.startswith('#'):
append_to_package_conf('accept_keywords', string=line)
os.remove(old_path)
def _package_conf_file_to_dir(file_name):
'''
Convert a config file to a config directory.
'''
if file_name in SUPPORTED_CONFS:
path = BASE_PATH.format(file_name)
if os.path.exists(path):
if os.path.isdir(path):
return False
else:
os.rename(path, path + '.tmpbak')
os.mkdir(path, 0o755)
with fopen(path + '.tmpbak') as fh_:
for line in fh_:
line = line.strip()
if line and not line.startswith('#'):
append_to_package_conf(file_name, string=line)
os.remove(path + '.tmpbak')
return True
else:
os.mkdir(path, 0o755)
return True
def _package_conf_ordering(conf, clean=True, keep_backup=False):
'''
Move entries in the correct file.
'''
if conf in SUPPORTED_CONFS:
rearrange = []
path = BASE_PATH.format(conf)
backup_files = []
for triplet in os.walk(path):
for file_name in triplet[2]:
file_path = '{0}/{1}'.format(triplet[0], file_name)
cp = triplet[0][len(path) + 1:] + '/' + file_name
shutil.copy(file_path, file_path + '.bak')
backup_files.append(file_path + '.bak')
if cp[0] == '/' or cp.split('/') > 2:
with fopen(file_path) as fp_:
rearrange.extend(fp_.readlines())
os.remove(file_path)
else:
new_contents = ''
with fopen(file_path, 'r+') as file_handler:
for line in file_handler:
try:
atom = line.strip().split()[0]
except IndexError:
new_contents += line
else:
if atom[0] == '#' or \
portage.dep_getkey(atom) == cp:
new_contents += line
else:
rearrange.append(line.strip())
if len(new_contents) != 0:
file_handler.seek(0)
file_handler.truncate(len(new_contents))
file_handler.write(new_contents)
if len(new_contents) == 0:
os.remove(file_path)
for line in rearrange:
append_to_package_conf(conf, string=line)
if not keep_backup:
for bfile in backup_files:
try:
os.remove(bfile)
except OSError:
pass
if clean:
for triplet in os.walk(path):
if len(triplet[1]) == 0 and len(triplet[2]) == 0 and \
triplet[0] != path:
shutil.rmtree(triplet[0])
def _check_accept_keywords(approved, flag):
'''check compatibility of accept_keywords'''
if flag in approved:
return False
elif (flag.startswith('~') and flag[1:] in approved) \
or ('~'+flag in approved):
return False
else:
return True
def _merge_flags(new_flags, old_flags=None, conf='any'):
'''
Merges multiple lists of flags removing duplicates and resolving conflicts
giving priority to lasts lists.
'''
if not old_flags:
old_flags = []
args = [old_flags, new_flags]
if conf == 'accept_keywords':
tmp = new_flags + \
[i for i in old_flags if _check_accept_keywords(new_flags, i)]
else:
tmp = portage.flatten(args)
flags = {}
for flag in tmp:
if flag[0] == '-':
flags[flag[1:]] = False
else:
flags[flag] = True
tmp = []
for key, val in six.iteritems(flags):
if val:
tmp.append(key)
else:
tmp.append('-' + key)
# Next sort is just aesthetic, can be commented for a small performance
# boost
tmp.sort(key=lambda x: x.lstrip('-'))
return tmp
def append_to_package_conf(conf, atom='', flags=None, string='', overwrite=False):
'''
Append a string or a list of flags for a given package or DEPEND atom to a
given configuration file.
CLI Example:
.. code-block:: bash
salt '*' portage_config.append_to_package_conf use string="app-admin/salt ldap -libvirt"
salt '*' portage_config.append_to_package_conf use atom="> = app-admin/salt-0.14.1" flags="['ldap', '-libvirt']"
'''
if flags is None:
flags = []
if conf in SUPPORTED_CONFS:
if not string:
if '/' not in atom:
atom = _p_to_cp(atom)
if not atom:
return
string = '{0} {1}'.format(atom, ' '.join(flags))
new_flags = list(flags)
else:
atom = string.strip().split()[0]
new_flags = [flag for flag in string.strip().split(' ') if flag][1:]
if '/' not in atom:
atom = _p_to_cp(atom)
string = '{0} {1}'.format(atom, ' '.join(new_flags))
if not atom:
return
to_delete_if_empty = []
if conf == 'accept_keywords':
if '-~ARCH' in new_flags:
new_flags.remove('-~ARCH')
to_delete_if_empty.append(atom)
if '~ARCH' in new_flags:
new_flags.remove('~ARCH')
append_to_package_conf(conf, string=atom, overwrite=overwrite)
if not new_flags:
return
# Next sort is just aesthetic, can be commented for a small performance
# boost
new_flags.sort(key=lambda x: x.lstrip('-'))
complete_file_path = _get_config_file(conf, atom)
pdir = os.path.dirname(complete_file_path)
if not os.path.exists(pdir):
os.makedirs(pdir, 0o755)
try:
shutil.copy(complete_file_path, complete_file_path + '.bak')
except IOError:
pass
try:
file_handler = fopen(complete_file_path, 'r+') # pylint: disable=resource-leakage
except IOError:
file_handler = fopen(complete_file_path, 'w+') # pylint: disable=resource-leakage
new_contents = ''
added = False
try:
for l in file_handler:
l_strip = l.strip()
if l_strip == '':
new_contents += '\n'
elif l_strip[0] == '#':
new_contents += l
elif l_strip.split()[0] == atom:
if l_strip in to_delete_if_empty:
continue
if overwrite:
new_contents += string.strip() + '\n'
added = True
else:
old_flags = [flag for flag in l_strip.split(' ') if flag][1:]
if conf == 'accept_keywords':
if not old_flags:
new_contents += l
if not new_flags:
added = True
continue
elif not new_flags:
continue
merged_flags = _merge_flags(new_flags, old_flags, conf)
if merged_flags:
new_contents += '{0} {1}\n'.format(
atom, ' '.join(merged_flags))
else:
new_contents += '{0}\n'.format(atom)
added = True
else:
new_contents += l
if not added:
new_contents += string.strip() + '\n'
except Exception as exc:
log.error('Failed to write to %s: %s', complete_file_path, exc)
else:
file_handler.seek(0)
file_handler.truncate(len(new_contents))
file_handler.write(new_contents)
finally:
file_handler.close()
try:
os.remove(complete_file_path + '.bak')
except OSError:
pass
def append_use_flags(atom, uses=None, overwrite=False):
'''
Append a list of use flags for a given package or DEPEND atom
CLI Example:
.. code-block:: bash
salt '*' portage_config.append_use_flags "app-admin/salt[ldap, -libvirt]"
salt '*' portage_config.append_use_flags ">=app-admin/salt-0.14.1" "['ldap', '-libvirt']"
'''
if not uses:
uses = portage.dep.dep_getusedeps(atom)
atom = atom[:atom.rfind('[')]
if len(uses) == 0:
return
append_to_package_conf('use', atom=atom, flags=uses, overwrite=overwrite)
def get_flags_from_package_conf(conf, atom):
'''
Get flags for a given package or DEPEND atom.
Warning: This only works if the configuration files tree is in the correct
format (the one enforced by enforce_nice_config)
CLI Example:
.. code-block:: bash
salt '*' portage_config.get_flags_from_package_conf license salt
'''
if conf in SUPPORTED_CONFS:
if '/' not in atom:
atom = _p_to_cp(atom)
if not atom:
raise AttributeError('Matching c/p form not found for atom {0}'.format(atom))
package_file = _get_config_file(conf, atom)
has_wildcard = '*' in atom
if has_wildcard:
match_list = set(atom)
else:
try:
match_list = set(_porttree().dbapi.xmatch("match-all", atom))
except AttributeError:
return []
flags = []
try:
with fopen(package_file) as fp_:
for line in fp_:
line = line.strip()
line_package = line.split()[0]
if has_wildcard:
found_match = line_package == atom
else:
line_list = _porttree().dbapi.xmatch("match-all", line_package)
found_match = match_list.issubset(line_list)
if found_match:
f_tmp = [flag for flag in line.strip().split(' ') if flag][1:]
if f_tmp:
flags.extend(f_tmp)
else:
flags.append('~ARCH')
return _merge_flags(flags)
except IOError:
return []
def has_flag(conf, atom, flag):
'''
Verify if the given package or DEPEND atom has the given flag.
Warning: This only works if the configuration files tree is in the correct
format (the one enforced by enforce_nice_config)
CLI Example:
.. code-block:: bash
salt '*' portage_config.has_flag license salt Apache-2.0
'''
if flag in get_flags_from_package_conf(conf, atom):
return True
return False
def get_missing_flags(conf, atom, flags):
'''
Find out which of the given flags are currently not set.
CLI Example:
.. code-block:: bash
salt '*' portage_config.get_missing_flags use salt "['ldap', '-libvirt', 'openssl']"
'''
new_flags = []
for flag in flags:
if not has_flag(conf, atom, flag):
new_flags.append(flag)
return new_flags
def has_use(atom, use):
'''
Verify if the given package or DEPEND atom has the given use flag.
Warning: This only works if the configuration files tree is in the correct
format (the one enforced by enforce_nice_config)
CLI Example:
.. code-block:: bash
salt '*' portage_config.has_use salt libvirt
'''
return has_flag('use', atom, use)
def is_present(conf, atom):
'''
Tell if a given package or DEPEND atom is present in the configuration
files tree.
Warning: This only works if the configuration files tree is in the correct
format (the one enforced by enforce_nice_config)
CLI Example:
.. code-block:: bash
salt '*' portage_config.is_present unmask salt
'''
if conf in SUPPORTED_CONFS:
if not isinstance(atom, portage.dep.Atom):
atom = portage.dep.Atom(atom, allow_wildcard=True)
has_wildcard = '*' in atom
package_file = _get_config_file(conf, str(atom))
# wildcards are valid in confs
if has_wildcard:
match_list = set(atom)
else:
match_list = set(_porttree().dbapi.xmatch("match-all", atom))
try:
with fopen(package_file) as fp_:
for line in fp_:
line = line.strip()
line_package = line.split()[0]
if has_wildcard:
if line_package == str(atom):
return True
else:
line_list = _porttree().dbapi.xmatch("match-all", line_package)
if match_list.issubset(line_list):
return True
except IOError:
pass
return False
def get_iuse(cp):
'''
.. versionadded:: 2015.8.0
Gets the current IUSE flags from the tree.
@type: cpv: string
@param cpv: cat/pkg
@rtype list
@returns [] or the list of IUSE flags
'''
cpv = _get_cpv(cp)
try:
# aux_get might return dupes, so run them through set() to remove them
dirty_flags = _porttree().dbapi.aux_get(cpv, ["IUSE"])[0].split()
return list(set(dirty_flags))
except Exception as e:
return []
def get_installed_use(cp, use="USE"):
'''
.. versionadded:: 2015.8.0
Gets the installed USE flags from the VARDB.
@type: cp: string
@param cp: cat/pkg
@type use: string
@param use: 1 of ["USE", "PKGUSE"]
@rtype list
@returns [] or the list of IUSE flags
'''
portage = _get_portage()
cpv = _get_cpv(cp)
return portage.db[portage.root]["vartree"].dbapi.aux_get(cpv, [use])[0].split()
def filter_flags(use, use_expand_hidden, usemasked, useforced):
'''
.. versionadded:: 2015.8.0
Filter function to remove hidden or otherwise not normally
visible USE flags from a list.
@type use: list
@param use: the USE flag list to be filtered.
@type use_expand_hidden: list
@param use_expand_hidden: list of flags hidden.
@type usemasked: list
@param usemasked: list of masked USE flags.
@type useforced: list
@param useforced: the forced USE flags.
@rtype: list
@return the filtered USE flags.
'''
portage = _get_portage()
# clean out some environment flags, since they will most probably
# be confusing for the user
for f in use_expand_hidden:
f = f.lower()+ "_"
for x in use:
if f in x:
use.remove(x)
# clean out any arch's
archlist = portage.settings["PORTAGE_ARCHLIST"].split()
for a in use[:]:
if a in archlist:
use.remove(a)
# dbl check if any from usemasked or useforced are still there
masked = usemasked + useforced
for a in use[:]:
if a in masked:
use.remove(a)
return use
def get_all_cpv_use(cp):
'''
.. versionadded:: 2015.8.0
Uses portage to determine final USE flags and settings for an emerge.
@type cp: string
@param cp: eg cat/pkg
@rtype: lists
@return use, use_expand_hidden, usemask, useforce
'''
cpv = _get_cpv(cp)
portage = _get_portage()
use = None
_porttree().dbapi.settings.unlock()
try:
_porttree().dbapi.settings.setcpv(cpv, mydb=portage.portdb)
use = portage.settings['PORTAGE_USE'].split()
use_expand_hidden = portage.settings["USE_EXPAND_HIDDEN"].split()
usemask = list(_porttree().dbapi.settings.usemask)
useforce = list(_porttree().dbapi.settings.useforce)
except KeyError:
_porttree().dbapi.settings.reset()
_porttree().dbapi.settings.lock()
return [], [], [], []
# reset cpv filter
_porttree().dbapi.settings.reset()
_porttree().dbapi.settings.lock()
return use, use_expand_hidden, usemask, useforce
def get_cleared_flags(cp):
'''
.. versionadded:: 2015.8.0
Uses portage for compare use flags which is used for installing package
and use flags which now exist int /etc/portage/package.use/
@type cp: string
@param cp: eg cat/pkg
@rtype: tuple
@rparam: tuple with two lists - list of used flags and
list of flags which will be used
'''
cpv = _get_cpv(cp)
final_use, use_expand_hidden, usemasked, useforced = get_all_cpv_use(cpv)
inst_flags = filter_flags(get_installed_use(cpv), use_expand_hidden,
usemasked, useforced)
final_flags = filter_flags(final_use, use_expand_hidden,
usemasked, useforced)
return inst_flags, final_flags
def is_changed_uses(cp):
'''
.. versionadded:: 2015.8.0
Uses portage for determine if the use flags of installed package
is compatible with use flags in portage configs.
@type cp: string
@param cp: eg cat/pkg
'''
cpv = _get_cpv(cp)
i_flags, conf_flags = get_cleared_flags(cpv)
for i in i_flags:
try:
conf_flags.remove(i)
except ValueError:
return True
return True if conf_flags else False

View File

@ -1,284 +0,0 @@
# -*- coding: utf-8 -*-
'''
Manage ini files
================
:maintainer: <akilesh1597@gmail.com>
:maturity: new
:depends: re
:platform: all
'''
# Import Python libs
# Import Salt libs
import salt.ext.six as six
__virtualname__ = 'ini'
def __virtual__():
'''
Only load if the ini module is available
'''
return __virtualname__ if 'ini.set_option' in __salt__ else False
def options_present(name, sections=None, separator='=', strict=False):
'''
.. code-block:: yaml
/home/saltminion/api-paste.ini:
ini.options_present:
- separator: '='
- strict: True
- sections:
test:
testkey: 'testval'
secondoption: 'secondvalue'
test1:
testkey1: 'testval121'
options present in file and not specified in sections
dict will be untouched, unless `strict: True` flag is
used
changes dict will contain the list of changes made
'''
ret = {'name': name,
'changes': {},
'result': True,
'comment': 'No anomaly detected'
}
if __opts__['test']:
ret['result'] = True
ret['comment'] = ''
for section in sections or {}:
section_name = ' in section ' + section if section != 'DEFAULT_IMPLICIT' else ''
try:
cur_section = __salt__['ini.get_section'](name, section, separator)
except IOError as err:
ret['comment'] = "{0}".format(err)
ret['result'] = False
return ret
for key in sections[section]:
cur_value = cur_section.get(key)
if cur_value == str(sections[section][key]):
ret['comment'] += 'Key {0}{1} unchanged.\n'.format(key, section_name)
continue
ret['comment'] += 'Changed key {0}{1}.\n'.format(key, section_name)
ret['result'] = None
if ret['comment'] == '':
ret['comment'] = 'No changes detected.'
return ret
try:
changes = {}
if sections:
for section_name, section_body in sections.items():
changes[section_name] = {}
if strict:
original = __salt__['ini.get_section'](name, section_name, separator)
for key_to_remove in set(original.keys()).difference(section_body.keys()):
orig_value = __salt__['ini.get_option'](name, section_name, key_to_remove, separator)
__salt__['ini.remove_option'](name, section_name, key_to_remove, separator)
changes[section_name].update({key_to_remove: ''})
changes[section_name].update({key_to_remove: {'before': orig_value,
'after': None}})
options_updated = __salt__['ini.set_option'](name, {section_name: section_body}, separator)
if options_updated:
changes[section_name].update(options_updated[section_name])
else:
changes = __salt__['ini.set_option'](name, sections, separator)
except IOError as err:
ret['comment'] = "{0}".format(err)
ret['result'] = False
return ret
if 'error' in changes:
ret['result'] = False
ret['comment'] = 'Errors encountered. {0}'.format(changes['error'])
ret['changes'] = {}
else:
if all(value == {} for value in changes.values()):
ret['changes'] = {}
ret['comment'] = 'No changes take effect'
else:
ret['changes'] = changes
ret['comment'] = 'Changes take effect'
return ret
def options_absent(name, sections=None, separator='='):
'''
.. code-block:: yaml
/home/saltminion/api-paste.ini:
ini.options_absent:
- separator: '='
- sections:
test:
- testkey
- secondoption
test1:
- testkey1
options present in file and not specified in sections
dict will be untouched
changes dict will contain the list of changes made
'''
ret = {'name': name,
'changes': {},
'result': True,
'comment': 'No anomaly detected'
}
if __opts__['test']:
ret['result'] = True
ret['comment'] = ''
for section in sections or {}:
section_name = ' in section ' + section if section != 'DEFAULT_IMPLICIT' else ''
try:
cur_section = __salt__['ini.get_section'](name, section, separator)
except IOError as err:
ret['comment'] = "{0}".format(err)
ret['result'] = False
return ret
for key in sections[section]:
cur_value = cur_section.get(key)
if not cur_value:
ret['comment'] += 'Key {0}{1} does not exist.\n'.format(key, section_name)
continue
ret['comment'] += 'Deleted key {0}{1}.\n'.format(key, section_name)
ret['result'] = None
if ret['comment'] == '':
ret['comment'] = 'No changes detected.'
return ret
sections = sections or {}
for section, keys in six.iteritems(sections):
for key in keys:
try:
current_value = __salt__['ini.remove_option'](name, section, key, separator)
except IOError as err:
ret['comment'] = "{0}".format(err)
ret['result'] = False
return ret
if not current_value:
continue
if section not in ret['changes']:
ret['changes'].update({section: {}})
ret['changes'][section].update({key: current_value})
ret['comment'] = 'Changes take effect'
return ret
def sections_present(name, sections=None, separator='='):
'''
.. code-block:: yaml
/home/saltminion/api-paste.ini:
ini.sections_present:
- separator: '='
- sections:
- section_one
- section_two
This will only create empty sections. To also create options, use
options_present state
options present in file and not specified in sections will be deleted
changes dict will contain the sections that changed
'''
ret = {'name': name,
'changes': {},
'result': True,
'comment': 'No anomaly detected'
}
if __opts__['test']:
ret['result'] = True
ret['comment'] = ''
for section in sections or {}:
try:
cur_section = __salt__['ini.get_section'](name, section, separator)
except IOError as err:
ret['result'] = False
ret['comment'] = "{0}".format(err)
return ret
if cmp(dict(sections[section]), cur_section) == 0:
ret['comment'] += 'Section unchanged {0}.\n'.format(section)
continue
elif cur_section:
ret['comment'] += 'Changed existing section {0}.\n'.format(section)
else:
ret['comment'] += 'Created new section {0}.\n'.format(section)
ret['result'] = None
if ret['comment'] == '':
ret['comment'] = 'No changes detected.'
return ret
section_to_update = {}
for section_name in sections or []:
section_to_update.update({section_name: {}})
try:
changes = __salt__['ini.set_option'](name, section_to_update, separator)
except IOError as err:
ret['result'] = False
ret['comment'] = "{0}".format(err)
return ret
if 'error' in changes:
ret['result'] = False
ret['changes'] = 'Errors encountered {0}'.format(changes['error'])
return ret
ret['changes'] = changes
ret['comment'] = 'Changes take effect'
return ret
def sections_absent(name, sections=None, separator='='):
'''
.. code-block:: yaml
/home/saltminion/api-paste.ini:
ini.sections_absent:
- separator: '='
- sections:
- test
- test1
options present in file and not specified in sections will be deleted
changes dict will contain the sections that changed
'''
ret = {'name': name,
'changes': {},
'result': True,
'comment': 'No anomaly detected'
}
if __opts__['test']:
ret['result'] = True
ret['comment'] = ''
for section in sections or []:
try:
cur_section = __salt__['ini.get_section'](name, section, separator)
except IOError as err:
ret['result'] = False
ret['comment'] = "{0}".format(err)
return ret
if not cur_section:
ret['comment'] += 'Section {0} does not exist.\n'.format(section)
continue
ret['comment'] += 'Deleted section {0}.\n'.format(section)
ret['result'] = None
if ret['comment'] == '':
ret['comment'] = 'No changes detected.'
return ret
for section in sections or []:
try:
cur_section = __salt__['ini.remove_section'](name, section, separator)
except IOError as err:
ret['result'] = False
ret['comment'] = "{0}".format(err)
return ret
if not cur_section:
continue
ret['changes'][section] = cur_section
ret['comment'] = 'Changes take effect'
return ret

File diff suppressed because it is too large Load Diff

View File

@ -28,9 +28,6 @@ include:
{% set main_reponame = salt['pillar.get']('salt:repos:main:name', 'local') %}
{% set main_remote_uri = salt['pillar.get']('salt:repos:main:remote') %}
{% set common_reponame = salt['pillar.get']('salt:repos:common:name', 'common') %}
{% set common_remote_uri = salt['pillar.get']('salt:repos:common:remote',
"git+ssh://git@git.bakka.su/salt-common.git") %}
{% set extra_repos = salt['pillar.get']('salt:repos:extra', {} ) %}
{% set extra_reponames = extra_repos|list %}
@ -122,26 +119,6 @@ salt-repo-{{ reponame }}-{{ branch }}:
{% endif %}
{% endfor %}
/var/salt/{{ common_reponame }}:
file.directory:
- user: root
- group: root
- file_mode: 644
- dir_mode: 755
git.latest:
- name: {{ common_remote_uri }}
- target: /var/salt/{{ common_reponame }}
- rev: master
- force_clone: True
- force_checkout: True
- force_fetch: True
- force_reset: True
- identity: /var/salt/ssh/salt
- require:
- file: /var/salt/ssh/salt
- require_in:
- file: /etc/salt/master.d/roots.conf
salt-roots-restart:
service.running:
- name: {{ 'salt-minion' if salt['grains.get']('salt:masterless', False) else 'salt-master' }}

View File

@ -6,7 +6,6 @@ from glob import glob
d_salt = '/var/salt'
main_reponame = __salt__['pillar.get']('salt:repos:main:name', 'local')
common_reponame = __salt__['pillar.get']('salt:repos:common:name', 'common')
extra_repos = __salt__['pillar.get']('salt:repos:extra', {})
branches = filter(lambda x: x not in (main_reponame, '.git', '_mirror'),
map(path.basename,
@ -19,20 +18,23 @@ for branch in branches:
env_name = 'base'
else:
env_name = branch
content['file_roots'][env_name] = (
[path.join(d_salt, main_reponame, branch, 'sls'),
path.join(d_salt, 'private', 'files'),
path.join(d_salt, common_reponame, 'sls')] +
filter(path.isdir, [path.join(d_salt, repo_name, extra_repos[repo_name].get('branch', branch), 'sls')
for repo_name in extra_repos.keys()]))
content['pillar_roots'][env_name] = (
[path.join(d_salt, main_reponame, branch, 'pillar'),
path.join(d_salt, 'private', 'pillar'),
path.join(d_salt, common_reponame, 'pillar')] +
filter(path.isdir, [path.join(d_salt, repo_name, extra_repos[repo_name].get('branch', branch), 'pillar')
for repo_name in extra_repos.keys()]))
content['file_roots'][env_name] = [path.join(d_salt, main_reponame, branch, 'sls')]
content['pillar_roots'][env_name] = [path.join(d_salt, main_reponame, branch, 'pillar')]
for repo_name, data in extra_repos.items():
if 'branch' in data:
content['file_roots'][env_name].append(path.join(d_salt, repo_name, data['branch'], 'sls'))
content['pillar_roots'][env_name].append(path.join(d_salt, repo_name, data['branch'], 'pillar'))
else:
if (path.isdir(path.join(d_salt, repo_name, branch, 'sls'))
and path.isdir(path.join(d_salt, repo_name, branch, 'pillar'))):
content['file_roots'][env_name].append(path.join(d_salt, repo_name, branch, 'sls'))
content['pillar_roots'][env_name].append(path.join(d_salt, repo_name, branch, 'pillar'))
elif 'default-branch' in data:
content['file_roots'][env_name].append(path.join(d_salt, repo_name, data['default-branch'], 'sls'))
content['pillar_roots'][env_name].append(path.join(d_salt, repo_name, data['default-branch'], 'pillar'))
content['pillar_roots']['base'].append(path.join(d_salt, 'private', 'pillar'))
content['file_roots'][env_name].append(path.join(d_salt, 'private', 'files'))
content['pillar_roots'][env_name].append(path.join(d_salt, 'private', 'pillar'))
state('/etc/salt/master.d/roots.conf').file.managed(
mode='644', user='root',