mirror of
https://github.com/valitydev/salt.git
synced 2024-11-07 00:55:19 +00:00
Merge branch '2018.3' of https://github.com/saltstack/salt into requests_2018.3
This commit is contained in:
commit
242389b151
6
.ci/docs
6
.ci/docs
@ -29,7 +29,7 @@ pipeline {
|
|||||||
}
|
}
|
||||||
stage('build') {
|
stage('build') {
|
||||||
steps {
|
steps {
|
||||||
sh 'eval "$(pyenv init -)"; make -C doc clean html'
|
sh 'eval "$(pyenv init -)"; make SPHINXOPTS="-W" -C doc clean html'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -48,8 +48,8 @@ pipeline {
|
|||||||
description: 'The docs job has failed',
|
description: 'The docs job has failed',
|
||||||
status: 'FAILURE',
|
status: 'FAILURE',
|
||||||
context: "jenkins/pr/docs"
|
context: "jenkins/pr/docs"
|
||||||
slackSend channel: "#jenkins-prod-pr",
|
slackSend channel: "#jenkins-prod-pr",
|
||||||
color: '#FF0000',
|
color: '#FF0000',
|
||||||
message: "FAILED: PR-Job: '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
|
message: "FAILED: PR-Job: '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
120
.ci/lint
120
.ci/lint
@ -3,7 +3,7 @@ pipeline {
|
|||||||
options {
|
options {
|
||||||
timestamps()
|
timestamps()
|
||||||
ansiColor('xterm')
|
ansiColor('xterm')
|
||||||
timeout(time: 1, unit: 'HOURS')
|
timeout(time: 3, unit: 'HOURS')
|
||||||
}
|
}
|
||||||
environment {
|
environment {
|
||||||
PYENV_ROOT = "/usr/local/pyenv"
|
PYENV_ROOT = "/usr/local/pyenv"
|
||||||
@ -14,7 +14,7 @@ pipeline {
|
|||||||
stage('github-pending') {
|
stage('github-pending') {
|
||||||
steps {
|
steps {
|
||||||
githubNotify credentialsId: 'test-jenkins-credentials',
|
githubNotify credentialsId: 'test-jenkins-credentials',
|
||||||
description: 'Testing lint...',
|
description: 'Python lint on changes begins...',
|
||||||
status: 'PENDING',
|
status: 'PENDING',
|
||||||
context: "jenkins/pr/lint"
|
context: "jenkins/pr/lint"
|
||||||
}
|
}
|
||||||
@ -24,12 +24,15 @@ pipeline {
|
|||||||
sh '''
|
sh '''
|
||||||
# Need -M to detect renames otherwise they are reported as Delete and Add, need -C to detect copies, -C includes -M
|
# Need -M to detect renames otherwise they are reported as Delete and Add, need -C to detect copies, -C includes -M
|
||||||
# -M is on by default in git 2.9+
|
# -M is on by default in git 2.9+
|
||||||
git diff --name-status -l99999 -C "origin/$CHANGE_TARGET" "origin/$BRANCH_NAME" > file-list-status.log
|
git diff --name-status -l99999 -C "origin/$CHANGE_TARGET" > file-list-status.log
|
||||||
# the -l increase the search limit, lets use awk so we do not need to repeat the search above.
|
# the -l increase the search limit, lets use awk so we do not need to repeat the search above.
|
||||||
gawk 'BEGIN {FS="\\t"} {if ($1 != "D") {print $NF}}' file-list-status.log > file-list-changed.log
|
gawk 'BEGIN {FS="\\t"} {if ($1 != "D") {print $NF}}' file-list-status.log > file-list-changed.log
|
||||||
gawk 'BEGIN {FS="\\t"} {if ($1 == "D") {print $NF}}' file-list-status.log > file-list-deleted.log
|
gawk 'BEGIN {FS="\\t"} {if ($1 == "D") {print $NF}}' file-list-status.log > file-list-deleted.log
|
||||||
(git diff --name-status -l99999 -C "origin/$CHANGE_TARGET";echo "---";git diff --name-status -l99999 -C "origin/$BRANCH_NAME";printenv|grep -E '=[0-9a-z]{40,}+$|COMMIT=|BRANCH') > file-list-experiment.log
|
(git diff --name-status -l99999 -C "origin/$CHANGE_TARGET" "origin/$BRANCH_NAME";echo "---";git diff --name-status -l99999 -C "origin/$BRANCH_NAME";printenv|grep -E '=[0-9a-z]{40,}+$|COMMIT=|BRANCH') > file-list-experiment.log
|
||||||
touch pylint-report-salt.log pylint-report-tests.log
|
echo 254 > pylint-salt-chg.exit # assume failure
|
||||||
|
echo 254 > pylint-salt-full.exit # assume failure
|
||||||
|
echo 254 > pylint-tests-chg.exit # assume failure
|
||||||
|
echo 254 > pylint-tests-full.exit # assume failure
|
||||||
eval "$(pyenv init -)"
|
eval "$(pyenv init -)"
|
||||||
pyenv --version
|
pyenv --version
|
||||||
pyenv install --skip-existing 2.7.14
|
pyenv install --skip-existing 2.7.14
|
||||||
@ -41,63 +44,126 @@ pipeline {
|
|||||||
archiveArtifacts artifacts: 'file-list-status.log,file-list-changed.log,file-list-deleted.log,file-list-experiment.log'
|
archiveArtifacts artifacts: 'file-list-status.log,file-list-changed.log,file-list-deleted.log,file-list-experiment.log'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage('linting') {
|
stage('linting chg') {
|
||||||
failFast false
|
|
||||||
parallel {
|
parallel {
|
||||||
stage('salt linting') {
|
stage('lint salt chg') {
|
||||||
when {
|
when {
|
||||||
expression { return readFile('file-list-changed.log') =~ /(?i)(^|\n)(salt\/.*\.py|setup\.py)\n/ }
|
expression { return readFile('file-list-changed.log') =~ /(?i)(^|\n)(salt\/.*\.py|setup\.py)\n/ }
|
||||||
}
|
}
|
||||||
steps {
|
steps {
|
||||||
sh '''
|
sh '''
|
||||||
eval "$(pyenv init - --no-rehash)"
|
eval "$(pyenv init - --no-rehash)"
|
||||||
grep -Ei '^salt/.*\\.py$|^setup\\.py$' file-list-changed.log | xargs -r '--delimiter=\\n' tox -e pylint-salt | tee pylint-report-salt.log
|
# tee makes the exit/return code always 0
|
||||||
|
grep -Ei '^salt/.*\\.py$|^setup\\.py$' file-list-changed.log | (xargs -r '--delimiter=\\n' tox -e pylint-salt ; echo "$?" > pylint-salt-chg.exit) | tee pylint-report-salt-chg.log
|
||||||
# remove color escape coding
|
# remove color escape coding
|
||||||
sed -ri 's/\\x1B\\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g' pylint-report-salt.log
|
sed -ri 's/\\x1B\\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g' pylint-report-salt-chg.log
|
||||||
|
read rc_exit < pylint-salt-chg.exit
|
||||||
|
exit "$rc_exit"
|
||||||
'''
|
'''
|
||||||
archiveArtifacts artifacts: 'pylint-report-salt.log'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage('test linting') {
|
stage('lint test chg') {
|
||||||
when {
|
when {
|
||||||
expression { return readFile('file-list-changed.log') =~ /(?i)(^|\n)tests\/.*\.py\n/ }
|
expression { return readFile('file-list-changed.log') =~ /(?i)(^|\n)tests\/.*\.py\n/ }
|
||||||
}
|
}
|
||||||
steps {
|
steps {
|
||||||
sh '''
|
sh '''
|
||||||
eval "$(pyenv init - --no-rehash)"
|
eval "$(pyenv init - --no-rehash)"
|
||||||
grep -Ei '^tests/.*\\.py$' file-list-changed.log | xargs -r '--delimiter=\\n' tox -e pylint-tests | tee pylint-report-tests.log
|
# tee makes the exit/return code always 0
|
||||||
|
grep -Ei '^tests/.*\\.py$' file-list-changed.log | (xargs -r '--delimiter=\\n' tox -e pylint-tests ; echo "$?" > pylint-tests-chg.exit) | tee pylint-report-tests-chg.log
|
||||||
# remove color escape coding
|
# remove color escape coding
|
||||||
sed -ri 's/\\x1B\\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g' pylint-report-tests.log
|
sed -ri 's/\\x1B\\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g' pylint-report-tests-chg.log
|
||||||
|
read rc_exit < pylint-tests-chg.exit
|
||||||
|
exit "$rc_exit"
|
||||||
'''
|
'''
|
||||||
archiveArtifacts artifacts: 'pylint-report-tests.log'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
post {
|
||||||
|
always {
|
||||||
|
archiveArtifacts artifacts: 'pylint-report-*-chg.log', allowEmptyArchive: true
|
||||||
|
step([$class: 'WarningsPublisher',
|
||||||
|
parserConfigurations: [[
|
||||||
|
parserName: 'PyLint',
|
||||||
|
pattern: 'pylint-report-*-chg.log'
|
||||||
|
]],
|
||||||
|
failedTotalAll: '0',
|
||||||
|
useDeltaValues: false,
|
||||||
|
canRunOnFailed: true,
|
||||||
|
usePreviousBuildAsReference: true
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('linting all') {
|
||||||
|
// perform a full linit if this is a merge forward and the change only lint passed.
|
||||||
|
when {
|
||||||
|
expression { return params.CHANGE_BRANCH =~ /(?i)^merge[._-]/ }
|
||||||
|
}
|
||||||
|
parallel {
|
||||||
|
stage('setup full') {
|
||||||
|
steps {
|
||||||
|
githubNotify credentialsId: 'test-jenkins-credentials',
|
||||||
|
description: 'Python lint on everything begins...',
|
||||||
|
status: 'PENDING',
|
||||||
|
context: "jenkins/pr/lint"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('lint salt full') {
|
||||||
|
steps {
|
||||||
|
sh '''
|
||||||
|
eval "$(pyenv init - --no-rehash)"
|
||||||
|
(tox -e pylint-salt ; echo "$?" > pylint-salt-full.exit) | tee pylint-report-salt-full.log
|
||||||
|
# remove color escape coding
|
||||||
|
sed -ri 's/\\x1B\\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g' pylint-report-salt-full.log
|
||||||
|
read rc_exit < pylint-salt-full.exit
|
||||||
|
exit "$rc_exit"
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('lint test full') {
|
||||||
|
steps {
|
||||||
|
sh '''
|
||||||
|
eval "$(pyenv init - --no-rehash)"
|
||||||
|
(tox -e pylint-tests ; echo "$?" > pylint-tests-full.exit) | tee pylint-report-tests-full.log
|
||||||
|
# remove color escape coding
|
||||||
|
sed -ri 's/\\x1B\\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g' pylint-report-tests-full.log
|
||||||
|
read rc_exit < pylint-tests-full.exit
|
||||||
|
exit "$rc_exit"
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
post {
|
||||||
|
always {
|
||||||
|
archiveArtifacts artifacts: 'pylint-report-*-full.log', allowEmptyArchive: true
|
||||||
|
step([$class: 'WarningsPublisher',
|
||||||
|
parserConfigurations: [[
|
||||||
|
parserName: 'PyLint',
|
||||||
|
pattern: 'pylint-report-*-full.log'
|
||||||
|
]],
|
||||||
|
failedTotalAll: '0',
|
||||||
|
useDeltaValues: false,
|
||||||
|
canRunOnFailed: true,
|
||||||
|
usePreviousBuildAsReference: true
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
post {
|
post {
|
||||||
always {
|
always {
|
||||||
step([$class: 'WarningsPublisher',
|
|
||||||
parserConfigurations: [[
|
|
||||||
parserName: 'PyLint',
|
|
||||||
pattern: 'pylint-report*.log'
|
|
||||||
]],
|
|
||||||
failedTotalAll: '0',
|
|
||||||
useDeltaValues: false,
|
|
||||||
canRunOnFailed: true,
|
|
||||||
usePreviousBuildAsReference: true
|
|
||||||
])
|
|
||||||
cleanWs()
|
cleanWs()
|
||||||
}
|
}
|
||||||
success {
|
success {
|
||||||
githubNotify credentialsId: 'test-jenkins-credentials',
|
githubNotify credentialsId: 'test-jenkins-credentials',
|
||||||
description: 'The lint job has passed',
|
description: 'Python lint test has passed',
|
||||||
status: 'SUCCESS',
|
status: 'SUCCESS',
|
||||||
context: "jenkins/pr/lint"
|
context: "jenkins/pr/lint"
|
||||||
}
|
}
|
||||||
failure {
|
failure {
|
||||||
githubNotify credentialsId: 'test-jenkins-credentials',
|
githubNotify credentialsId: 'test-jenkins-credentials',
|
||||||
description: 'The lint job has failed',
|
description: 'Python lint test has failed',
|
||||||
status: 'FAILURE',
|
status: 'FAILURE',
|
||||||
context: "jenkins/pr/lint"
|
context: "jenkins/pr/lint"
|
||||||
slackSend channel: "#jenkins-prod-pr",
|
slackSend channel: "#jenkins-prod-pr",
|
||||||
|
4
.github/CODEOWNERS
vendored
4
.github/CODEOWNERS
vendored
@ -13,6 +13,7 @@ salt/*/*boto* @saltstack/team-boto
|
|||||||
|
|
||||||
# Team Core
|
# Team Core
|
||||||
requirements/* @saltstack/team-core
|
requirements/* @saltstack/team-core
|
||||||
|
rfcs/* @saltstack/team-core
|
||||||
salt/auth/* @saltstack/team-core
|
salt/auth/* @saltstack/team-core
|
||||||
salt/cache/* @saltstack/team-core
|
salt/cache/* @saltstack/team-core
|
||||||
salt/cli/* @saltstack/team-core
|
salt/cli/* @saltstack/team-core
|
||||||
@ -73,3 +74,6 @@ salt/modules/reg.py @saltstack/team-windows
|
|||||||
salt/states/reg.py @saltstack/team-windows
|
salt/states/reg.py @saltstack/team-windows
|
||||||
tests/*/*win* @saltstack/team-windows
|
tests/*/*win* @saltstack/team-windows
|
||||||
tests/*/test_reg.py @saltstack/team-windows
|
tests/*/test_reg.py @saltstack/team-windows
|
||||||
|
|
||||||
|
# Jenkins Integration
|
||||||
|
.ci/* @saltstack/saltstack-sre-team @saltstack/team-core
|
||||||
|
@ -30,7 +30,7 @@ provisioner:
|
|||||||
salt_install: bootstrap
|
salt_install: bootstrap
|
||||||
salt_version: latest
|
salt_version: latest
|
||||||
salt_bootstrap_url: https://bootstrap.saltstack.com
|
salt_bootstrap_url: https://bootstrap.saltstack.com
|
||||||
salt_bootstrap_options: -X -p rsync stable <%= version %>
|
salt_bootstrap_options: -X -p rsync git v<%= version %>
|
||||||
log_level: info
|
log_level: info
|
||||||
sudo: true
|
sudo: true
|
||||||
require_chef: false
|
require_chef: false
|
||||||
|
3
Gemfile
3
Gemfile
@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
source 'https://rubygems.org'
|
source 'https://rubygems.org'
|
||||||
|
|
||||||
gem 'test-kitchen', '~>1.21'
|
# Point this back at the test-kitchen package after 1.23.3 is relased
|
||||||
|
gem 'test-kitchen', :git => 'https://github.com/dwoz/test-kitchen.git', :branch => 'winrm_opts'
|
||||||
gem 'kitchen-salt', '~>0.2'
|
gem 'kitchen-salt', '~>0.2'
|
||||||
gem 'kitchen-sync'
|
gem 'kitchen-sync'
|
||||||
gem 'git'
|
gem 'git'
|
||||||
|
@ -7,7 +7,7 @@ from docutils.parsers.rst import Directive
|
|||||||
|
|
||||||
from docutils.statemachine import ViewList
|
from docutils.statemachine import ViewList
|
||||||
from sphinx import addnodes
|
from sphinx import addnodes
|
||||||
from sphinx.directives import ObjectDescription, Directive
|
from sphinx.directives import ObjectDescription
|
||||||
from sphinx.domains import Domain, ObjType
|
from sphinx.domains import Domain, ObjType
|
||||||
from sphinx.domains.python import PyObject
|
from sphinx.domains.python import PyObject
|
||||||
from sphinx.locale import l_, _
|
from sphinx.locale import l_, _
|
||||||
|
2
doc/_themes/saltstack2/layout.html
vendored
2
doc/_themes/saltstack2/layout.html
vendored
@ -256,7 +256,7 @@
|
|||||||
<!--
|
<!--
|
||||||
<a href="https://saltstack.com/saltstack-enterprise/" target="_blank"><img class="nolightbox footer-banner center" src="{{ pathto('_static/images/enterprise_ad.jpg', 1) }}"/></a>
|
<a href="https://saltstack.com/saltstack-enterprise/" target="_blank"><img class="nolightbox footer-banner center" src="{{ pathto('_static/images/enterprise_ad.jpg', 1) }}"/></a>
|
||||||
-->
|
-->
|
||||||
<a href="http://saltconf.com" target="_blank"><img class="nolightbox footer-banner center" src="{{ pathto('_static/images/DOCBANNER.jpg', 1) }}"/></a>
|
<a href="http://saltconf.com/saltconf18-speakers/" target="_blank"><img class="nolightbox footer-banner center" src="{{ pathto('_static/images/DOCBANNER.png', 1) }}"/></a>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
BIN
doc/_themes/saltstack2/static/images/DOCBANNER.jpg
vendored
BIN
doc/_themes/saltstack2/static/images/DOCBANNER.jpg
vendored
Binary file not shown.
Before Width: | Height: | Size: 497 KiB |
BIN
doc/_themes/saltstack2/static/images/DOCBANNER.png
vendored
Normal file
BIN
doc/_themes/saltstack2/static/images/DOCBANNER.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 767 KiB |
154
doc/conf.py
154
doc/conf.py
@ -3,7 +3,6 @@
|
|||||||
'''
|
'''
|
||||||
Sphinx documentation for Salt
|
Sphinx documentation for Salt
|
||||||
'''
|
'''
|
||||||
import functools
|
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
import types
|
import types
|
||||||
@ -12,7 +11,6 @@ import time
|
|||||||
from sphinx.directives import TocTree
|
from sphinx.directives import TocTree
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=R0903
|
|
||||||
class Mock(object):
|
class Mock(object):
|
||||||
'''
|
'''
|
||||||
Mock out specified imports.
|
Mock out specified imports.
|
||||||
@ -24,7 +22,7 @@ class Mock(object):
|
|||||||
|
|
||||||
http://read-the-docs.readthedocs.org/en/latest/faq.html#i-get-import-errors-on-libraries-that-depend-on-c-modules
|
http://read-the-docs.readthedocs.org/en/latest/faq.html#i-get-import-errors-on-libraries-that-depend-on-c-modules
|
||||||
'''
|
'''
|
||||||
def __init__(self, mapping=None, *args, **kwargs):
|
def __init__(self, mapping=None, *args, **kwargs): # pylint: disable=unused-argument
|
||||||
"""
|
"""
|
||||||
Mapping allows autodoc to bypass the Mock object, but actually assign
|
Mapping allows autodoc to bypass the Mock object, but actually assign
|
||||||
a specific value, expected by a specific attribute returned.
|
a specific value, expected by a specific attribute returned.
|
||||||
@ -40,14 +38,12 @@ class Mock(object):
|
|||||||
return Mock(mapping=self.__mapping)
|
return Mock(mapping=self.__mapping)
|
||||||
|
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
#__mapping = {'total': 0}
|
|
||||||
data = None
|
|
||||||
if name in self.__mapping:
|
if name in self.__mapping:
|
||||||
data = self.__mapping.get(name)
|
data = self.__mapping.get(name)
|
||||||
elif name in ('__file__', '__path__'):
|
elif name in ('__file__', '__path__'):
|
||||||
data = '/dev/null'
|
data = '/dev/null'
|
||||||
elif name == '__qualname__':
|
elif name in ('__mro_entries__', '__qualname__'):
|
||||||
raise AttributeError("'Mock' object has no attribute '__qualname__'")
|
raise AttributeError("'Mock' object has no attribute '%s'" % (name))
|
||||||
else:
|
else:
|
||||||
data = Mock(mapping=self.__mapping)
|
data = Mock(mapping=self.__mapping)
|
||||||
return data
|
return data
|
||||||
@ -55,10 +51,30 @@ class Mock(object):
|
|||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def next(self):
|
@staticmethod
|
||||||
|
def __next__():
|
||||||
raise StopIteration
|
raise StopIteration
|
||||||
|
|
||||||
# pylint: enable=R0903
|
# For Python 2
|
||||||
|
next = __next__
|
||||||
|
|
||||||
|
|
||||||
|
def mock_decorator_with_params(*oargs, **okwargs): # pylint: disable=unused-argument
|
||||||
|
'''
|
||||||
|
Optionally mock a decorator that takes parameters
|
||||||
|
|
||||||
|
E.g.:
|
||||||
|
|
||||||
|
@blah(stuff=True)
|
||||||
|
def things():
|
||||||
|
pass
|
||||||
|
'''
|
||||||
|
def inner(fn, *iargs, **ikwargs): # pylint: disable=unused-argument
|
||||||
|
if hasattr(fn, '__call__'):
|
||||||
|
return fn
|
||||||
|
return Mock()
|
||||||
|
return inner
|
||||||
|
|
||||||
|
|
||||||
MOCK_MODULES = [
|
MOCK_MODULES = [
|
||||||
# Python stdlib
|
# Python stdlib
|
||||||
@ -153,7 +169,6 @@ MOCK_MODULES = [
|
|||||||
'napalm',
|
'napalm',
|
||||||
'dson',
|
'dson',
|
||||||
'jnpr',
|
'jnpr',
|
||||||
'json',
|
|
||||||
'lxml',
|
'lxml',
|
||||||
'lxml.etree',
|
'lxml.etree',
|
||||||
'jnpr.junos',
|
'jnpr.junos',
|
||||||
@ -178,40 +193,24 @@ MOCK_MODULES = [
|
|||||||
'msgpack',
|
'msgpack',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
MOCK_MODULES_MAPPING = {
|
||||||
|
'cherrypy': {'config': mock_decorator_with_params},
|
||||||
|
'ntsecuritycon': {
|
||||||
|
'STANDARD_RIGHTS_REQUIRED': 0,
|
||||||
|
'SYNCHRONIZE': 0,
|
||||||
|
},
|
||||||
|
'psutil': {'total': 0}, # Otherwise it will crash Sphinx
|
||||||
|
}
|
||||||
|
|
||||||
for mod_name in MOCK_MODULES:
|
for mod_name in MOCK_MODULES:
|
||||||
if mod_name == 'psutil':
|
sys.modules[mod_name] = Mock(mapping=MOCK_MODULES_MAPPING.get(mod_name))
|
||||||
mock = Mock(mapping={'total': 0}) # Otherwise it will crash Sphinx
|
|
||||||
else:
|
|
||||||
mock = Mock()
|
|
||||||
sys.modules[mod_name] = mock
|
|
||||||
|
|
||||||
def mock_decorator_with_params(*oargs, **okwargs):
|
|
||||||
'''
|
|
||||||
Optionally mock a decorator that takes parameters
|
|
||||||
|
|
||||||
E.g.:
|
|
||||||
|
|
||||||
@blah(stuff=True)
|
|
||||||
def things():
|
|
||||||
pass
|
|
||||||
'''
|
|
||||||
def inner(fn, *iargs, **ikwargs):
|
|
||||||
if hasattr(fn, '__call__'):
|
|
||||||
return fn
|
|
||||||
else:
|
|
||||||
return Mock()
|
|
||||||
return inner
|
|
||||||
|
|
||||||
# Define a fake version attribute for the following libs.
|
# Define a fake version attribute for the following libs.
|
||||||
sys.modules['libcloud'].__version__ = '0.0.0'
|
sys.modules['libcloud'].__version__ = '0.0.0'
|
||||||
sys.modules['msgpack'].version = (1, 0, 0)
|
sys.modules['msgpack'].version = (1, 0, 0)
|
||||||
sys.modules['psutil'].version_info = (3, 0, 0)
|
sys.modules['psutil'].version_info = (3, 0, 0)
|
||||||
sys.modules['pymongo'].version = '0.0.0'
|
sys.modules['pymongo'].version = '0.0.0'
|
||||||
sys.modules['ntsecuritycon'].STANDARD_RIGHTS_REQUIRED = 0
|
sys.modules['tornado'].version_info = (0, 0, 0)
|
||||||
sys.modules['ntsecuritycon'].SYNCHRONIZE = 0
|
|
||||||
|
|
||||||
# Define a fake version attribute for the following libs.
|
|
||||||
sys.modules['cherrypy'].config = mock_decorator_with_params
|
|
||||||
|
|
||||||
|
|
||||||
# -- Add paths to PYTHONPATH ---------------------------------------------------
|
# -- Add paths to PYTHONPATH ---------------------------------------------------
|
||||||
@ -223,12 +222,12 @@ except NameError:
|
|||||||
docs_basepath = os.path.abspath(os.path.dirname('.'))
|
docs_basepath = os.path.abspath(os.path.dirname('.'))
|
||||||
|
|
||||||
addtl_paths = (
|
addtl_paths = (
|
||||||
os.pardir, # salt itself (for autodoc)
|
os.pardir, # salt itself (for autodoc)
|
||||||
'_ext', # custom Sphinx extensions
|
'_ext', # custom Sphinx extensions
|
||||||
)
|
)
|
||||||
|
|
||||||
for path in addtl_paths:
|
for addtl_path in addtl_paths:
|
||||||
sys.path.insert(0, os.path.abspath(os.path.join(docs_basepath, path)))
|
sys.path.insert(0, os.path.abspath(os.path.join(docs_basepath, addtl_path)))
|
||||||
|
|
||||||
|
|
||||||
# We're now able to import salt
|
# We're now able to import salt
|
||||||
@ -302,12 +301,12 @@ extensions = [
|
|||||||
'sphinx.ext.intersphinx',
|
'sphinx.ext.intersphinx',
|
||||||
'httpdomain',
|
'httpdomain',
|
||||||
'youtube',
|
'youtube',
|
||||||
'saltautodoc', # Must be AFTER autodoc
|
#'saltautodoc', # Must be AFTER autodoc
|
||||||
'shorturls',
|
#'shorturls',
|
||||||
]
|
]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import sphinxcontrib.spelling
|
import sphinxcontrib.spelling # false positive, pylint: disable=unused-import
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
@ -371,7 +370,8 @@ gettext_compact = False
|
|||||||
|
|
||||||
|
|
||||||
### HTML options
|
### HTML options
|
||||||
html_theme = os.environ.get('HTML_THEME', 'saltstack2') # set 'HTML_THEME=saltstack' to use previous theme
|
# set 'HTML_THEME=saltstack' to use previous theme
|
||||||
|
html_theme = os.environ.get('HTML_THEME', 'saltstack2')
|
||||||
html_theme_path = ['_themes']
|
html_theme_path = ['_themes']
|
||||||
html_title = u''
|
html_title = u''
|
||||||
html_short_title = 'Salt'
|
html_short_title = 'Salt'
|
||||||
@ -439,7 +439,7 @@ html_show_copyright = True
|
|||||||
### Latex options
|
### Latex options
|
||||||
|
|
||||||
latex_documents = [
|
latex_documents = [
|
||||||
('contents', 'Salt.tex', 'Salt Documentation', 'SaltStack, Inc.', 'manual'),
|
('contents', 'Salt.tex', 'Salt Documentation', 'SaltStack, Inc.', 'manual'),
|
||||||
]
|
]
|
||||||
|
|
||||||
latex_logo = '_static/salt-logo.png'
|
latex_logo = '_static/salt-logo.png'
|
||||||
@ -447,7 +447,7 @@ latex_logo = '_static/salt-logo.png'
|
|||||||
latex_elements = {
|
latex_elements = {
|
||||||
'inputenc': '', # use XeTeX instead of the inputenc LaTeX package.
|
'inputenc': '', # use XeTeX instead of the inputenc LaTeX package.
|
||||||
'utf8extra': '',
|
'utf8extra': '',
|
||||||
'preamble': '''
|
'preamble': r'''
|
||||||
\usepackage{fontspec}
|
\usepackage{fontspec}
|
||||||
\setsansfont{Linux Biolinum O}
|
\setsansfont{Linux Biolinum O}
|
||||||
\setromanfont{Linux Libertine O}
|
\setromanfont{Linux Libertine O}
|
||||||
@ -459,35 +459,36 @@ latex_elements = {
|
|||||||
|
|
||||||
|
|
||||||
### Linkcheck options
|
### Linkcheck options
|
||||||
linkcheck_ignore = [r'http://127.0.0.1',
|
linkcheck_ignore = [
|
||||||
r'http://salt:\d+',
|
r'http://127.0.0.1',
|
||||||
r'http://local:\d+',
|
r'http://salt:\d+',
|
||||||
r'https://console.aws.amazon.com',
|
r'http://local:\d+',
|
||||||
r'http://192.168.33.10',
|
r'https://console.aws.amazon.com',
|
||||||
r'http://domain:\d+',
|
r'http://192.168.33.10',
|
||||||
r'http://123.456.789.012:\d+',
|
r'http://domain:\d+',
|
||||||
r'http://localhost',
|
r'http://123.456.789.012:\d+',
|
||||||
r'https://groups.google.com/forum/#!forum/salt-users',
|
r'http://localhost',
|
||||||
r'http://logstash.net/docs/latest/inputs/udp',
|
r'https://groups.google.com/forum/#!forum/salt-users',
|
||||||
r'http://logstash.net/docs/latest/inputs/zeromq',
|
r'http://logstash.net/docs/latest/inputs/udp',
|
||||||
r'http://www.youtube.com/saltstack',
|
r'http://logstash.net/docs/latest/inputs/zeromq',
|
||||||
r'https://raven.readthedocs.io',
|
r'http://www.youtube.com/saltstack',
|
||||||
r'https://getsentry.com',
|
r'https://raven.readthedocs.io',
|
||||||
r'https://salt-cloud.readthedocs.io',
|
r'https://getsentry.com',
|
||||||
r'https://salt.readthedocs.io',
|
r'https://salt-cloud.readthedocs.io',
|
||||||
r'http://www.pip-installer.org/',
|
r'https://salt.readthedocs.io',
|
||||||
r'http://www.windowsazure.com/',
|
r'http://www.pip-installer.org/',
|
||||||
r'https://github.com/watching',
|
r'http://www.windowsazure.com/',
|
||||||
r'dash-feed://',
|
r'https://github.com/watching',
|
||||||
r'https://github.com/saltstack/salt/',
|
r'dash-feed://',
|
||||||
r'http://bootstrap.saltstack.org',
|
r'https://github.com/saltstack/salt/',
|
||||||
r'https://bootstrap.saltstack.com',
|
r'http://bootstrap.saltstack.org',
|
||||||
r'https://raw.githubusercontent.com/saltstack/salt-bootstrap/stable/bootstrap-salt.sh',
|
r'https://bootstrap.saltstack.com',
|
||||||
r'media.readthedocs.org/dash/salt/latest/salt.xml',
|
r'https://raw.githubusercontent.com/saltstack/salt-bootstrap/stable/bootstrap-salt.sh',
|
||||||
r'https://portal.aws.amazon.com/gp/aws/securityCredentials',
|
r'media.readthedocs.org/dash/salt/latest/salt.xml',
|
||||||
r'https://help.github.com/articles/fork-a-repo',
|
r'https://portal.aws.amazon.com/gp/aws/securityCredentials',
|
||||||
r'dash-feed://https%3A//media.readthedocs.org/dash/salt/latest/salt.xml'
|
r'https://help.github.com/articles/fork-a-repo',
|
||||||
]
|
r'dash-feed://https%3A//media.readthedocs.org/dash/salt/latest/salt.xml',
|
||||||
|
]
|
||||||
|
|
||||||
linkcheck_anchors = False
|
linkcheck_anchors = False
|
||||||
|
|
||||||
@ -531,6 +532,7 @@ epub_tocdup = False
|
|||||||
|
|
||||||
|
|
||||||
def skip_mod_init_member(app, what, name, obj, skip, options):
|
def skip_mod_init_member(app, what, name, obj, skip, options):
|
||||||
|
# pylint: disable=too-many-arguments,unused-argument
|
||||||
if name.startswith('_'):
|
if name.startswith('_'):
|
||||||
return True
|
return True
|
||||||
if isinstance(obj, types.FunctionType) and obj.__name__ == 'mod_init':
|
if isinstance(obj, types.FunctionType) and obj.__name__ == 'mod_init':
|
||||||
|
@ -92,13 +92,13 @@ RunnerClient
|
|||||||
------------
|
------------
|
||||||
|
|
||||||
.. autoclass:: salt.runner.RunnerClient
|
.. autoclass:: salt.runner.RunnerClient
|
||||||
:members: cmd, async, cmd_sync, cmd_async
|
:members: cmd, asynchronous, cmd_sync, cmd_async
|
||||||
|
|
||||||
WheelClient
|
WheelClient
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
.. autoclass:: salt.wheel.WheelClient
|
.. autoclass:: salt.wheel.WheelClient
|
||||||
:members: cmd, async, cmd_sync, cmd_async
|
:members: cmd, asynchronous, cmd_sync, cmd_async
|
||||||
|
|
||||||
CloudClient
|
CloudClient
|
||||||
-----------
|
-----------
|
||||||
|
@ -16,7 +16,7 @@ engine modules
|
|||||||
ircbot
|
ircbot
|
||||||
junos_syslog
|
junos_syslog
|
||||||
logentries
|
logentries
|
||||||
logstash
|
logstash_engine
|
||||||
napalm_syslog
|
napalm_syslog
|
||||||
reactor
|
reactor
|
||||||
redis_sentinel
|
redis_sentinel
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
=====================
|
|
||||||
salt.engines.logstash
|
|
||||||
=====================
|
|
||||||
|
|
||||||
.. automodule:: salt.engines.logstash
|
|
||||||
:members:
|
|
6
doc/ref/engines/all/salt.engines.logstash_engine.rst
Normal file
6
doc/ref/engines/all/salt.engines.logstash_engine.rst
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
============================
|
||||||
|
salt.engines.logstash_engine
|
||||||
|
============================
|
||||||
|
|
||||||
|
.. automodule:: salt.engines.logstash_engine
|
||||||
|
:members:
|
@ -1,57 +0,0 @@
|
|||||||
.. _module-sync:
|
|
||||||
.. _dynamic-module-distribution:
|
|
||||||
|
|
||||||
===========================
|
|
||||||
Dynamic Module Distribution
|
|
||||||
===========================
|
|
||||||
|
|
||||||
.. versionadded:: 0.9.5
|
|
||||||
|
|
||||||
Custom Salt execution, state, and other modules can be distributed to Salt
|
|
||||||
minions using the Salt file server.
|
|
||||||
|
|
||||||
Under the root of any environment defined via the :conf_master:`file_roots`
|
|
||||||
option on the master server directories corresponding to the type of module can
|
|
||||||
be used.
|
|
||||||
|
|
||||||
The directories are prepended with an underscore:
|
|
||||||
|
|
||||||
- :file:`_beacons`
|
|
||||||
- :file:`_clouds`
|
|
||||||
- :file:`_engines`
|
|
||||||
- :file:`_grains`
|
|
||||||
- :file:`_modules`
|
|
||||||
- :file:`_output`
|
|
||||||
- :file:`_proxy`
|
|
||||||
- :file:`_renderers`
|
|
||||||
- :file:`_returners`
|
|
||||||
- :file:`_states`
|
|
||||||
- :file:`_tops`
|
|
||||||
- :file:`_utils`
|
|
||||||
|
|
||||||
The contents of these directories need to be synced over to the minions after
|
|
||||||
Python modules have been created in them. There are a number of ways to sync
|
|
||||||
the modules.
|
|
||||||
|
|
||||||
Sync Via States
|
|
||||||
===============
|
|
||||||
|
|
||||||
The minion configuration contains an option ``autoload_dynamic_modules``
|
|
||||||
which defaults to ``True``. This option makes the state system refresh all
|
|
||||||
dynamic modules when states are run. To disable this behavior set
|
|
||||||
:conf_minion:`autoload_dynamic_modules` to ``False`` in the minion config.
|
|
||||||
|
|
||||||
When dynamic modules are autoloaded via states, modules only pertinent to
|
|
||||||
the environments matched in the master's top file are downloaded.
|
|
||||||
|
|
||||||
This is important to remember, because modules can be manually loaded from
|
|
||||||
any specific environment that environment specific modules will be loaded
|
|
||||||
when a state run is executed.
|
|
||||||
|
|
||||||
Sync Via the saltutil Module
|
|
||||||
============================
|
|
||||||
|
|
||||||
The saltutil module has a number of functions that can be used to sync all
|
|
||||||
or specific dynamic modules. The saltutil module function ``saltutil.sync_all``
|
|
||||||
will sync all module types over to a minion. For more information see:
|
|
||||||
:mod:`salt.modules.saltutil`
|
|
@ -290,6 +290,8 @@ All beacons are configured using a similar process of enabling the beacon,
|
|||||||
writing a reactor SLS (and state SLS if needed), and mapping a beacon event to
|
writing a reactor SLS (and state SLS if needed), and mapping a beacon event to
|
||||||
the reactor SLS.
|
the reactor SLS.
|
||||||
|
|
||||||
|
.. _writing-beacons:
|
||||||
|
|
||||||
Writing Beacon Plugins
|
Writing Beacon Plugins
|
||||||
======================
|
======================
|
||||||
|
|
||||||
@ -360,5 +362,5 @@ new execution modules and functions to back specific beacons.
|
|||||||
Distributing Custom Beacons
|
Distributing Custom Beacons
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
Custom beacons can be distributed to minions using ``saltutil``, see
|
Custom beacons can be distributed to minions via the standard methods, see
|
||||||
:ref:`Dynamic Module Distribution <dynamic-module-distribution>`.
|
:ref:`Modular Systems <modular-systems>`.
|
||||||
|
@ -526,6 +526,19 @@ GPG key with ``git`` locally, and linking the GPG key to your GitHub account.
|
|||||||
Once these steps are completed, the commit signing verification will look like
|
Once these steps are completed, the commit signing verification will look like
|
||||||
the example in GitHub's `GPG Signature Verification feature announcement`_.
|
the example in GitHub's `GPG Signature Verification feature announcement`_.
|
||||||
|
|
||||||
|
Bootstrap Script Changes
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
Salt's Bootstrap Script, known as `bootstrap-salt.sh`_ in the Salt repo, has it's own
|
||||||
|
repository, contributing guidelines, and release cadence.
|
||||||
|
|
||||||
|
All changes to the Bootstrap Script should be made to `salt-bootstrap repo`_. Any
|
||||||
|
pull requests made to the `bootstrap-salt.sh`_ file in the Salt repository will be
|
||||||
|
automatically overwritten upon the next stable release of the Bootstrap Script.
|
||||||
|
|
||||||
|
For more information on the release process or how to contribute to the Bootstrap
|
||||||
|
Script, see the Bootstrap Script's `Contributing Guidelines`_.
|
||||||
|
|
||||||
.. _`saltstack/salt`: https://github.com/saltstack/salt
|
.. _`saltstack/salt`: https://github.com/saltstack/salt
|
||||||
.. _`GitHub Fork a Repo Guide`: https://help.github.com/articles/fork-a-repo
|
.. _`GitHub Fork a Repo Guide`: https://help.github.com/articles/fork-a-repo
|
||||||
.. _`GitHub issue tracker`: https://github.com/saltstack/salt/issues
|
.. _`GitHub issue tracker`: https://github.com/saltstack/salt/issues
|
||||||
@ -537,3 +550,6 @@ the example in GitHub's `GPG Signature Verification feature announcement`_.
|
|||||||
.. _GPG Probot: https://probot.github.io/apps/gpg/
|
.. _GPG Probot: https://probot.github.io/apps/gpg/
|
||||||
.. _help articles: https://help.github.com/articles/signing-commits-with-gpg/
|
.. _help articles: https://help.github.com/articles/signing-commits-with-gpg/
|
||||||
.. _GPG Signature Verification feature announcement: https://github.com/blog/2144-gpg-signature-verification
|
.. _GPG Signature Verification feature announcement: https://github.com/blog/2144-gpg-signature-verification
|
||||||
|
.. _bootstrap-salt.sh: https://github.com/saltstack/salt/blob/develop/salt/cloud/deploy/bootstrap-salt.sh
|
||||||
|
.. _salt-bootstrap repo: https://github.com/saltstack/salt-bootstrap
|
||||||
|
.. _Contributing Guidelines: https://github.com/saltstack/salt-bootstrap/blob/develop/CONTRIBUTING.md
|
||||||
|
@ -1,122 +0,0 @@
|
|||||||
.. _dunder-dictionaries:
|
|
||||||
|
|
||||||
===================
|
|
||||||
Dunder Dictionaries
|
|
||||||
===================
|
|
||||||
|
|
||||||
Salt provides several special "dunder" dictionaries as a convenience for Salt
|
|
||||||
development. These include ``__opts__``, ``__context__``, ``__salt__``, and
|
|
||||||
others. This document will describe each dictionary and detail where they exist
|
|
||||||
and what information and/or functionality they provide.
|
|
||||||
|
|
||||||
|
|
||||||
__opts__
|
|
||||||
--------
|
|
||||||
|
|
||||||
Available in
|
|
||||||
~~~~~~~~~~~~
|
|
||||||
|
|
||||||
- All loader modules
|
|
||||||
|
|
||||||
The ``__opts__`` dictionary contains all of the options passed in the
|
|
||||||
configuration file for the master or minion.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
In many places in salt, instead of pulling raw data from the __opts__
|
|
||||||
dict, configuration data should be pulled from the salt `get` functions
|
|
||||||
such as config.get, aka - __salt__['config.get']('foo:bar')
|
|
||||||
The `get` functions also allow for dict traversal via the *:* delimiter.
|
|
||||||
Consider using get functions whenever using __opts__ or __pillar__ and
|
|
||||||
__grains__ (when using grains for configuration data)
|
|
||||||
|
|
||||||
The configuration file data made available in the ``__opts__`` dictionary is the
|
|
||||||
configuration data relative to the running daemon. If the modules are loaded and
|
|
||||||
executed by the master, then the master configuration data is available, if the
|
|
||||||
modules are executed by the minion, then the minion configuration is
|
|
||||||
available. Any additional information passed into the respective configuration
|
|
||||||
files is made available
|
|
||||||
|
|
||||||
__salt__
|
|
||||||
--------
|
|
||||||
|
|
||||||
Available in
|
|
||||||
~~~~~~~~~~~~
|
|
||||||
|
|
||||||
- Execution Modules
|
|
||||||
- State Modules
|
|
||||||
- Returners
|
|
||||||
- Runners
|
|
||||||
- SDB Modules
|
|
||||||
|
|
||||||
``__salt__`` contains the execution module functions. This allows for all
|
|
||||||
functions to be called as they have been set up by the salt loader.
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
__salt__['cmd.run']('fdisk -l')
|
|
||||||
__salt__['network.ip_addrs']()
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
When used in runners, ``__salt__`` references other runner modules, and not
|
|
||||||
execution modules.
|
|
||||||
|
|
||||||
__grains__
|
|
||||||
----------
|
|
||||||
|
|
||||||
Available in
|
|
||||||
~~~~~~~~~~~~
|
|
||||||
|
|
||||||
- Execution Modules
|
|
||||||
- State Modules
|
|
||||||
- Returners
|
|
||||||
- External Pillar
|
|
||||||
|
|
||||||
The ``__grains__`` dictionary contains the grains data generated by the minion
|
|
||||||
that is currently being worked with. In execution modules, state modules and
|
|
||||||
returners this is the grains of the minion running the calls, when generating
|
|
||||||
the external pillar the ``__grains__`` is the grains data from the minion that
|
|
||||||
the pillar is being generated for.
|
|
||||||
|
|
||||||
__pillar__
|
|
||||||
-----------
|
|
||||||
|
|
||||||
Available in
|
|
||||||
~~~~~~~~~~~~
|
|
||||||
|
|
||||||
- Execution Modules
|
|
||||||
- State Modules
|
|
||||||
- Returners
|
|
||||||
|
|
||||||
The ``__pillar__`` dictionary contains the pillar for the respective minion.
|
|
||||||
|
|
||||||
__context__
|
|
||||||
-----------
|
|
||||||
|
|
||||||
``__context__`` exists in state modules and execution modules.
|
|
||||||
|
|
||||||
During a state run the ``__context__`` dictionary persists across all states
|
|
||||||
that are run and then is destroyed when the state ends.
|
|
||||||
|
|
||||||
When running an execution module ``__context__`` persists across all module
|
|
||||||
executions until the modules are refreshed; such as when
|
|
||||||
:py:func:`saltutil.sync_all <salt.modules.saltutil.sync_all>` or
|
|
||||||
:py:func:`state.apply <salt.modules.state.apply_>` are executed.
|
|
||||||
|
|
||||||
A great place to see how to use ``__context__`` is in the cp.py module in
|
|
||||||
salt/modules/cp.py. The fileclient authenticates with the master when it is
|
|
||||||
instantiated and then is used to copy files to the minion. Rather than create a
|
|
||||||
new fileclient for each file that is to be copied down, one instance of the
|
|
||||||
fileclient is instantiated in the ``__context__`` dictionary and is reused for
|
|
||||||
each file. Here is an example from salt/modules/cp.py:
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
if not 'cp.fileclient' in __context__:
|
|
||||||
__context__['cp.fileclient'] = salt.fileclient.get_file_client(__opts__)
|
|
||||||
|
|
||||||
|
|
||||||
.. note:: Because __context__ may or may not have been destroyed, always be
|
|
||||||
sure to check for the existence of the key in __context__ and
|
|
||||||
generate the key before using it.
|
|
@ -7,6 +7,7 @@ Developing Salt
|
|||||||
:glob:
|
:glob:
|
||||||
|
|
||||||
*
|
*
|
||||||
|
modules/index
|
||||||
extend/index
|
extend/index
|
||||||
tests/*
|
tests/*
|
||||||
raet/index
|
raet/index
|
||||||
|
@ -1,170 +0,0 @@
|
|||||||
===============
|
|
||||||
Modular Systems
|
|
||||||
===============
|
|
||||||
|
|
||||||
When first working with Salt, it is not always clear where all of the modular
|
|
||||||
components are and what they do. Salt comes loaded with more modular systems
|
|
||||||
than many users are aware of, making Salt very easy to extend in many places.
|
|
||||||
|
|
||||||
The most commonly used modular systems are execution modules and states. But
|
|
||||||
the modular systems extend well beyond the more easily exposed components
|
|
||||||
and are often added to Salt to make the complete system more flexible.
|
|
||||||
|
|
||||||
Execution Modules
|
|
||||||
=================
|
|
||||||
|
|
||||||
Execution modules make up the core of the functionality used by Salt to
|
|
||||||
interact with client systems. The execution modules create the core system
|
|
||||||
management library used by all Salt systems, including states, which
|
|
||||||
interact with minion systems.
|
|
||||||
|
|
||||||
Execution modules are completely open ended in their execution. They can
|
|
||||||
be used to do anything required on a minion, from installing packages to
|
|
||||||
detecting information about the system. The only restraint in execution
|
|
||||||
modules is that the defined functions always return a JSON serializable
|
|
||||||
object.
|
|
||||||
|
|
||||||
For a list of all built in execution modules, click :ref:`here
|
|
||||||
<all-salt.modules>`
|
|
||||||
|
|
||||||
For information on writing execution modules, see :ref:`this page
|
|
||||||
<writing-execution-modules>`.
|
|
||||||
|
|
||||||
|
|
||||||
Interactive Debugging
|
|
||||||
=====================
|
|
||||||
|
|
||||||
Sometimes debugging with ``print()`` and extra logs sprinkled everywhere is not
|
|
||||||
the best strategy.
|
|
||||||
|
|
||||||
IPython is a helpful debug tool that has an interactive python environment
|
|
||||||
which can be embedded in python programs.
|
|
||||||
|
|
||||||
First the system will require IPython to be installed.
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
# Debian
|
|
||||||
apt-get install ipython
|
|
||||||
|
|
||||||
# Arch Linux
|
|
||||||
pacman -Syu ipython2
|
|
||||||
|
|
||||||
# RHEL/CentOS (via EPEL)
|
|
||||||
yum install python-ipython
|
|
||||||
|
|
||||||
|
|
||||||
Now, in the troubling python module, add the following line at a location where
|
|
||||||
the debugger should be started:
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
test = 'test123'
|
|
||||||
import IPython; IPython.embed_kernel()
|
|
||||||
|
|
||||||
After running a Salt command that hits that line, the following will show up in
|
|
||||||
the log file:
|
|
||||||
|
|
||||||
.. code-block:: text
|
|
||||||
|
|
||||||
[CRITICAL] To connect another client to this kernel, use:
|
|
||||||
[IPKernelApp] --existing kernel-31271.json
|
|
||||||
|
|
||||||
Now on the system that invoked ``embed_kernel``, run the following command from
|
|
||||||
a shell:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
# NOTE: use ipython2 instead of ipython for Arch Linux
|
|
||||||
ipython console --existing
|
|
||||||
|
|
||||||
This provides a console that has access to all the vars and functions, and even
|
|
||||||
supports tab-completion.
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
print(test)
|
|
||||||
test123
|
|
||||||
|
|
||||||
To exit IPython and continue running Salt, press ``Ctrl-d`` to logout.
|
|
||||||
|
|
||||||
|
|
||||||
State Modules
|
|
||||||
=============
|
|
||||||
|
|
||||||
State modules are used to define the state interfaces used by Salt States.
|
|
||||||
These modules are restrictive in that they must follow a number of rules to
|
|
||||||
function properly.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
State modules define the available routines in sls files. If calling
|
|
||||||
an execution module directly is desired, take a look at the `module`
|
|
||||||
state.
|
|
||||||
|
|
||||||
Auth
|
|
||||||
====
|
|
||||||
|
|
||||||
The auth module system allows for external authentication routines to be easily
|
|
||||||
added into Salt. The `auth` function needs to be implemented to satisfy the
|
|
||||||
requirements of an auth module. Use the ``pam`` module as an example.
|
|
||||||
|
|
||||||
Fileserver
|
|
||||||
==========
|
|
||||||
|
|
||||||
The fileserver module system is used to create fileserver backends used by the
|
|
||||||
Salt Master. These modules need to implement the functions used in the
|
|
||||||
fileserver subsystem. Use the ``gitfs`` module as an example.
|
|
||||||
|
|
||||||
Grains
|
|
||||||
======
|
|
||||||
|
|
||||||
Grain modules define extra routines to populate grains data. All defined
|
|
||||||
public functions will be executed and MUST return a Python dict object. The
|
|
||||||
dict keys will be added to the grains made available to the minion.
|
|
||||||
|
|
||||||
Output
|
|
||||||
======
|
|
||||||
|
|
||||||
The output modules supply the outputter system with routines to display data
|
|
||||||
in the terminal. These modules are very simple and only require the `output`
|
|
||||||
function to execute. The default system outputter is the ``nested`` module.
|
|
||||||
|
|
||||||
Pillar
|
|
||||||
======
|
|
||||||
|
|
||||||
Used to define optional external pillar systems. The pillar generated via
|
|
||||||
the filesystem pillar is passed into external pillars. This is commonly used
|
|
||||||
as a bridge to database data for pillar, but is also the backend to the libvirt
|
|
||||||
state used to generate and sign libvirt certificates on the fly.
|
|
||||||
|
|
||||||
Renderers
|
|
||||||
=========
|
|
||||||
|
|
||||||
Renderers are the system used to render sls files into salt highdata for the
|
|
||||||
state compiler. They can be as simple as the ``py`` renderer and as complex as
|
|
||||||
``stateconf`` and ``pydsl``.
|
|
||||||
|
|
||||||
Returners
|
|
||||||
=========
|
|
||||||
|
|
||||||
Returners are used to send data from minions to external sources, commonly
|
|
||||||
databases. A full returner will implement all routines to be supported as an
|
|
||||||
external job cache. Use the ``redis`` returner as an example.
|
|
||||||
|
|
||||||
Runners
|
|
||||||
=======
|
|
||||||
|
|
||||||
Runners are purely master-side execution sequences.
|
|
||||||
|
|
||||||
Tops
|
|
||||||
====
|
|
||||||
|
|
||||||
Tops modules are used to convert external data sources into top file data for
|
|
||||||
the state system.
|
|
||||||
|
|
||||||
Wheel
|
|
||||||
=====
|
|
||||||
|
|
||||||
The wheel system is used to manage master side management routines. These
|
|
||||||
routines are primarily intended for the API to enable master configuration.
|
|
25
doc/topics/development/modules/configuration.rst
Normal file
25
doc/topics/development/modules/configuration.rst
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
=====================
|
||||||
|
Configuration Options
|
||||||
|
=====================
|
||||||
|
|
||||||
|
A number of configuration options can affect the load process. This is a quick
|
||||||
|
list of them:
|
||||||
|
|
||||||
|
* ``autoload_dynamic_modules`` (:conf_minion:`Minion <autoload_dynamic_modules>`)
|
||||||
|
* ``cython_enable`` (:conf_minion:`Minion <cython_enable>`, :conf_master:`Master <cython_enable>`)
|
||||||
|
* ``disable_modules`` (:conf_minion:`Minion <disable_modules>`)
|
||||||
|
* ``disable_returners`` (:conf_minion:`Minion <disable_returners>`)
|
||||||
|
* ``enable_zip_modules`` (:conf_minion:`Minion <enable_zip_modules>`)
|
||||||
|
* ``extension_modules`` (:conf_master:`Master <extension_modules>`)
|
||||||
|
* ``extmod_whitelist`` (:conf_minion:`Minion <extmod_whitelist>`, :conf_master:`Master <extmod_whitelist>`)
|
||||||
|
* ``extmod_blacklist`` (:conf_minion:`Minion <extmod_blacklist>`, :conf_master:`Master <extmod_blacklist>`)
|
||||||
|
* ``whitelist_modules`` (:conf_minion:`Minion <enable_whitelist_modules>`)
|
||||||
|
* ``grains_dirs`` (:conf_minion:`Minion <grains_dirs>`)
|
||||||
|
* ``module_dirs`` (:conf_minion:`Minion <module_dirs>`, :conf_master:`Master <module_dirs>`)
|
||||||
|
* ``outputter_dirs`` (:conf_minion:`Minion <outputter_dirs>`, :conf_master:`Master <outputter_dirs>`)
|
||||||
|
* ``providers`` (:conf_minion:`Minion <providers>`)
|
||||||
|
* ``render_dirs`` (:conf_minion:`Minion <render_dirs>`)
|
||||||
|
* ``returner_dirs`` (:conf_minion:`Minion <returner_dirs>`)
|
||||||
|
* ``runner_dirs`` (:conf_master:`Master <runner_dirs>`)
|
||||||
|
* ``states_dirs`` (:conf_minion:`Minion <states_dirs>`)
|
||||||
|
* ``utils_dirs`` (:conf_minion:`Minion <utils_dirs>`)
|
237
doc/topics/development/modules/developing.rst
Normal file
237
doc/topics/development/modules/developing.rst
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
======================
|
||||||
|
Developing New Modules
|
||||||
|
======================
|
||||||
|
|
||||||
|
Interactive Debugging
|
||||||
|
=====================
|
||||||
|
|
||||||
|
Sometimes debugging with ``print()`` and extra logs sprinkled everywhere is not
|
||||||
|
the best strategy.
|
||||||
|
|
||||||
|
IPython is a helpful debug tool that has an interactive python environment
|
||||||
|
which can be embedded in python programs.
|
||||||
|
|
||||||
|
First the system will require IPython to be installed.
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
# Debian
|
||||||
|
apt-get install ipython
|
||||||
|
|
||||||
|
# Arch Linux
|
||||||
|
pacman -Syu ipython2
|
||||||
|
|
||||||
|
# RHEL/CentOS (via EPEL)
|
||||||
|
yum install python-ipython
|
||||||
|
|
||||||
|
|
||||||
|
Now, in the troubling python module, add the following line at a location where
|
||||||
|
the debugger should be started:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
test = 'test123'
|
||||||
|
import IPython; IPython.embed_kernel()
|
||||||
|
|
||||||
|
After running a Salt command that hits that line, the following will show up in
|
||||||
|
the log file:
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
[CRITICAL] To connect another client to this kernel, use:
|
||||||
|
[IPKernelApp] --existing kernel-31271.json
|
||||||
|
|
||||||
|
Now on the system that invoked ``embed_kernel``, run the following command from
|
||||||
|
a shell:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
# NOTE: use ipython2 instead of ipython for Arch Linux
|
||||||
|
ipython console --existing
|
||||||
|
|
||||||
|
This provides a console that has access to all the vars and functions, and even
|
||||||
|
supports tab-completion.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
print(test)
|
||||||
|
test123
|
||||||
|
|
||||||
|
To exit IPython and continue running Salt, press ``Ctrl-d`` to logout.
|
||||||
|
|
||||||
|
Special Module Contents
|
||||||
|
=======================
|
||||||
|
|
||||||
|
These are things that may be defined by the module to influence various things.
|
||||||
|
|
||||||
|
__virtual__
|
||||||
|
-----------
|
||||||
|
|
||||||
|
__virtual_aliases__
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
__virtualname__
|
||||||
|
---------------
|
||||||
|
|
||||||
|
__init__
|
||||||
|
--------
|
||||||
|
|
||||||
|
Called before ``__virtual__()``
|
||||||
|
|
||||||
|
__proxyenabled__
|
||||||
|
----------------
|
||||||
|
grains and proxy modules
|
||||||
|
|
||||||
|
__proxyenabled__ as a list containing the names of the proxy types that the module supports.
|
||||||
|
|
||||||
|
__load__
|
||||||
|
--------
|
||||||
|
|
||||||
|
__func_alias__
|
||||||
|
--------------
|
||||||
|
|
||||||
|
__outputter__
|
||||||
|
-------------
|
||||||
|
|
||||||
|
.. _dunder-dictionaries:
|
||||||
|
|
||||||
|
Dunder Dictionaries
|
||||||
|
===================
|
||||||
|
|
||||||
|
Salt provides several special "dunder" dictionaries as a convenience for Salt
|
||||||
|
development. These include ``__opts__``, ``__context__``, ``__salt__``, and
|
||||||
|
others. This document will describe each dictionary and detail where they exist
|
||||||
|
and what information and/or functionality they provide.
|
||||||
|
|
||||||
|
The following dunder dictionaries are always defined, but may be empty
|
||||||
|
|
||||||
|
* ``__context__``
|
||||||
|
* ``__grains__``
|
||||||
|
* ``__pillar__``
|
||||||
|
* ``__opts__``
|
||||||
|
|
||||||
|
|
||||||
|
__opts__
|
||||||
|
--------
|
||||||
|
|
||||||
|
Defined in: All modules
|
||||||
|
|
||||||
|
The ``__opts__`` dictionary contains all of the options passed in the
|
||||||
|
configuration file for the master or minion.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
In many places in salt, instead of pulling raw data from the __opts__
|
||||||
|
dict, configuration data should be pulled from the salt `get` functions
|
||||||
|
such as config.get, aka - ``__salt__['config.get']('foo:bar')``
|
||||||
|
The `get` functions also allow for dict traversal via the *:* delimiter.
|
||||||
|
Consider using get functions whenever using ``__opts__`` or ``__pillar__``
|
||||||
|
and ``__grains__`` (when using grains for configuration data)
|
||||||
|
|
||||||
|
The configuration file data made available in the ``__opts__`` dictionary is the
|
||||||
|
configuration data relative to the running daemon. If the modules are loaded and
|
||||||
|
executed by the master, then the master configuration data is available, if the
|
||||||
|
modules are executed by the minion, then the minion configuration is
|
||||||
|
available. Any additional information passed into the respective configuration
|
||||||
|
files is made available
|
||||||
|
|
||||||
|
__salt__
|
||||||
|
--------
|
||||||
|
|
||||||
|
Defined in: Auth, Beacons, Engines, Execution, Executors, Outputters, Pillars,
|
||||||
|
Proxies, Renderers, Returners, Runners, SDB, SSH Wrappers, State, Thorium
|
||||||
|
|
||||||
|
``__salt__`` contains the execution module functions. This allows for all
|
||||||
|
functions to be called as they have been set up by the salt loader.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
__salt__['cmd.run']('fdisk -l')
|
||||||
|
__salt__['network.ip_addrs']()
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
When used in runners or outputters, ``__salt__`` references other
|
||||||
|
runner/outputter modules, and not execution modules.
|
||||||
|
|
||||||
|
__grains__
|
||||||
|
----------
|
||||||
|
|
||||||
|
Filled in for: Execution, Pillar, Renderer, Returner, SSH Wrapper, State.
|
||||||
|
|
||||||
|
The ``__grains__`` dictionary contains the grains data generated by the minion
|
||||||
|
that is currently being worked with. In execution modules, state modules and
|
||||||
|
returners this is the grains of the minion running the calls, when generating
|
||||||
|
the external pillar the ``__grains__`` is the grains data from the minion that
|
||||||
|
the pillar is being generated for.
|
||||||
|
|
||||||
|
While ``__grains__`` is defined for every module, it's only filled in for some.
|
||||||
|
|
||||||
|
__pillar__
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Filled in for: Execution, Returner, SSH Wrapper, State
|
||||||
|
|
||||||
|
The ``__pillar__`` dictionary contains the pillar for the respective minion.
|
||||||
|
|
||||||
|
While ``__pillar__`` is defined for every module, it's only filled in for some.
|
||||||
|
|
||||||
|
__context__
|
||||||
|
-----------
|
||||||
|
|
||||||
|
During a state run the ``__context__`` dictionary persists across all states
|
||||||
|
that are run and then is destroyed when the state ends.
|
||||||
|
|
||||||
|
When running an execution module ``__context__`` persists across all module
|
||||||
|
executions until the modules are refreshed; such as when
|
||||||
|
:py:func:`saltutil.sync_all <salt.modules.saltutil.sync_all>` or
|
||||||
|
:py:func:`state.apply <salt.modules.state.apply_>` are executed.
|
||||||
|
|
||||||
|
A great place to see how to use ``__context__`` is in the cp.py module in
|
||||||
|
salt/modules/cp.py. The fileclient authenticates with the master when it is
|
||||||
|
instantiated and then is used to copy files to the minion. Rather than create a
|
||||||
|
new fileclient for each file that is to be copied down, one instance of the
|
||||||
|
fileclient is instantiated in the ``__context__`` dictionary and is reused for
|
||||||
|
each file. Here is an example from salt/modules/cp.py:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
if not 'cp.fileclient' in __context__:
|
||||||
|
__context__['cp.fileclient'] = salt.fileclient.get_file_client(__opts__)
|
||||||
|
|
||||||
|
|
||||||
|
.. note:: Because __context__ may or may not have been destroyed, always be
|
||||||
|
sure to check for the existence of the key in __context__ and
|
||||||
|
generate the key before using it.
|
||||||
|
|
||||||
|
__utils__
|
||||||
|
---------
|
||||||
|
Defined in: Cloud, Engine, Execution, File Server, Pillar, Proxy, Runner, SDB.
|
||||||
|
|
||||||
|
__proxy__
|
||||||
|
---------
|
||||||
|
Defined in: Beacon, Engine, Execution, Executor, Proxy, Renderer, Returner, State, Util
|
||||||
|
|
||||||
|
__runners__
|
||||||
|
-----------
|
||||||
|
Defined in: Engine, Roster, Thorium
|
||||||
|
|
||||||
|
__ret__
|
||||||
|
-------
|
||||||
|
Defined in: Proxy, Search
|
||||||
|
|
||||||
|
__thorium__
|
||||||
|
-----------
|
||||||
|
Defined in: Thorium
|
||||||
|
|
||||||
|
__states__
|
||||||
|
----------
|
||||||
|
Defined in: Renderers, State
|
||||||
|
|
||||||
|
__serializers__
|
||||||
|
---------------
|
||||||
|
Defined in: State
|
||||||
|
|
||||||
|
__sdb__
|
||||||
|
-------
|
||||||
|
Defined in: SDB
|
@ -153,7 +153,7 @@ Using our example above:
|
|||||||
|
|
||||||
ext_pillar( id, pillar, 'some argument' ) # example_a
|
ext_pillar( id, pillar, 'some argument' ) # example_a
|
||||||
ext_pillar( id, pillar, 'argumentA', 'argumentB' ) # example_b
|
ext_pillar( id, pillar, 'argumentA', 'argumentB' ) # example_b
|
||||||
ext_pillar( id, pillar, keyA='valueA', keyB='valueB' } ) # example_c
|
ext_pillar( id, pillar, keyA='valueA', keyB='valueB' ) # example_c
|
||||||
|
|
||||||
|
|
||||||
In the ``example_a`` case, ``pillar`` will contain the items from the
|
In the ``example_a`` case, ``pillar`` will contain the items from the
|
394
doc/topics/development/modules/index.rst
Normal file
394
doc/topics/development/modules/index.rst
Normal file
@ -0,0 +1,394 @@
|
|||||||
|
.. _modular-systems:
|
||||||
|
|
||||||
|
===============
|
||||||
|
Modular Systems
|
||||||
|
===============
|
||||||
|
|
||||||
|
When first working with Salt, it is not always clear where all of the modular
|
||||||
|
components are and what they do. Salt comes loaded with more modular systems
|
||||||
|
than many users are aware of, making Salt very easy to extend in many places.
|
||||||
|
|
||||||
|
The most commonly used modular systems are execution modules and states. But
|
||||||
|
the modular systems extend well beyond the more easily exposed components
|
||||||
|
and are often added to Salt to make the complete system more flexible.
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
:glob:
|
||||||
|
|
||||||
|
developing
|
||||||
|
configuration
|
||||||
|
|
||||||
|
|
||||||
|
Loading Modules
|
||||||
|
===============
|
||||||
|
|
||||||
|
Modules come primarily from several sources:
|
||||||
|
|
||||||
|
* The Salt package itself
|
||||||
|
* The Salt File Server
|
||||||
|
* The extmods directory
|
||||||
|
* Secondary packages installed
|
||||||
|
|
||||||
|
Using one source to override another is not supported.
|
||||||
|
|
||||||
|
The Salt Package
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Salt itself ships with a large number of modules. These are part of the Salt
|
||||||
|
package itself and don't require the user to do anything to use them. (Although
|
||||||
|
a number of them have additional dependencies and/or configuration.)
|
||||||
|
|
||||||
|
The Salt File Server
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
The user may add modules by simply placing them in special directories in their
|
||||||
|
:ref:`fileserver <file-server>`.
|
||||||
|
|
||||||
|
The name of the directory inside of the file server is the directory name
|
||||||
|
prepended by underscore, such as:
|
||||||
|
|
||||||
|
- :file:`_grains`
|
||||||
|
- :file:`_modules`
|
||||||
|
- :file:`_states`
|
||||||
|
|
||||||
|
Modules must be synced before they can be used. This can happen a few ways,
|
||||||
|
discussed below.
|
||||||
|
|
||||||
|
.. note:
|
||||||
|
Using saltenvs besides ``base`` may not work in all contexts.
|
||||||
|
|
||||||
|
Sync Via States
|
||||||
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The minion configuration contains an option ``autoload_dynamic_modules``
|
||||||
|
which defaults to ``True``. This option makes the state system refresh all
|
||||||
|
dynamic modules when states are run. To disable this behavior set
|
||||||
|
:conf_minion:`autoload_dynamic_modules` to ``False`` in the minion config.
|
||||||
|
|
||||||
|
When dynamic modules are autoloaded via states, only the modules defined in the
|
||||||
|
same saltenvs as the states currently being run.
|
||||||
|
|
||||||
|
Sync Via the saltutil Module
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The saltutil module has a number of functions that can be used to sync all
|
||||||
|
or specific dynamic modules. The ``saltutil.sync_*``
|
||||||
|
:py:mod:`execution functions <salt.modules.saltutil>` and
|
||||||
|
:py:mod:`runner functions <salt.runners.saltutil>` can be used to sync modules
|
||||||
|
to minions and the master, respectively.
|
||||||
|
|
||||||
|
|
||||||
|
The extmods Directory
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Any files places in the directory set by ``extension_modules`` settings
|
||||||
|
(:conf_minion:`minion <extension_modules>`,
|
||||||
|
:conf_master:`master <extension_modules>`, default
|
||||||
|
``/var/cache/salt/*/extmods``) can also be loaded as modules. Note that these
|
||||||
|
directories are also used by the ``saltutil.sync_*`` functions (mentioned
|
||||||
|
above) and files may be overwritten.
|
||||||
|
|
||||||
|
Secondary Packages
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Third-party packages may also add modules to Salt if they are installed in the
|
||||||
|
same system and Python environment as the Salt Minion or Master.
|
||||||
|
|
||||||
|
This is done via setuptools entry points:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
setup(
|
||||||
|
# ...
|
||||||
|
entry_points={
|
||||||
|
'salt.loader': [
|
||||||
|
'module_dirs=spirofs.loader:module',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
# ...
|
||||||
|
)
|
||||||
|
|
||||||
|
Note that these are not synced from the Salt Master to the Minions. They must be
|
||||||
|
installed indepdendently on each Minion.
|
||||||
|
|
||||||
|
Module Types
|
||||||
|
============
|
||||||
|
|
||||||
|
The specific names used by each loading method above are as follows. See sections below
|
||||||
|
for a short summary of each of these systems.
|
||||||
|
|
||||||
|
.. _module-name-table:
|
||||||
|
|
||||||
|
============ ================================================================ ========================= =====================
|
||||||
|
Module Type Salt Package Name FS/Directory Name Entry Point
|
||||||
|
============ ================================================================ ========================= =====================
|
||||||
|
Auth ``salt.auth`` (:ref:`index <external-logging-handlers>`) ``auth`` [#no-fs]_ ``auth_dirs``
|
||||||
|
Beacon ``salt.beacons`` (:ref:`index <beacons>`) ``beacons`` ``beacons_dirs``
|
||||||
|
Cache ``salt.cache`` (:ref:`index <all-salt.cache>`) ``cache`` ``cache_dirs``
|
||||||
|
Cloud ``salt.cloud.clouds`` (:ref:`index <all-salt.clouds>`) ``clouds`` ``cloud_dirs``
|
||||||
|
Engine ``salt.engines`` (:ref:`index <engines>`) ``engines`` ``engines_dirs``
|
||||||
|
Execution ``salt.modules`` (:ref:`index <all-salt.modules>`) ``modules`` ``module_dirs``
|
||||||
|
Executor ``salt.executors`` (:ref:`index <all-salt.executors>`) ``executors`` [#no-fs]_ ``executor_dirs``
|
||||||
|
File Server ``salt.fileserver`` (:ref:`index <file-server>`) ``fileserver`` [#no-fs]_ ``fileserver_dirs``
|
||||||
|
Grain ``salt.grains`` (:ref:`index <all-salt.grains>`) ``grains`` ``grains_dirs``
|
||||||
|
Log Handler ``salt.log.handlers`` (:ref:`index <external-logging-handlers>`) ``log_handlers`` ``log_handlers_dirs``
|
||||||
|
Net API ``salt.netapi`` (:ref:`index <all-netapi-modules>`) ``netapi`` [#no-fs]_ ``netapi_dirs``
|
||||||
|
Outputter ``salt.output`` (:ref:`index <all-salt.output>`) ``output`` ``outputter_dirs``
|
||||||
|
Pillar ``salt.pillar`` (:ref:`index <all-salt.pillars>`) ``pillar`` ``pillar_dirs``
|
||||||
|
Proxy ``salt.proxy`` (:ref:`index <all-salt.proxy>`) ``proxy`` ``proxy_dirs``
|
||||||
|
Queue ``salt.queues`` (:ref:`index <all-salt.queues>`) ``queues`` ``queue_dirs``
|
||||||
|
Renderer ``salt.renderers`` (:ref:`index <all-salt.renderers>`) ``renderers`` ``render_dirs``
|
||||||
|
Returner ``salt.returners`` (:ref:`index <all-salt.returners>`) ``returners`` ``returner_dirs``
|
||||||
|
Roster ``salt.roster`` (:ref:`index <all-salt.roster>`) ``roster`` ``roster_dirs``
|
||||||
|
Runner ``salt.runners`` (:ref:`index <all-salt.runners>`) ``runners`` ``runner_dirs``
|
||||||
|
SDB ``salt.sdb`` (:ref:`index <all-salt.sdb>`) ``sdb`` ``sdb_dirs``
|
||||||
|
Search ``salt.search`` ``search`` [#no-fs]_ ``search_dirs``
|
||||||
|
Serializer ``salt.serializers`` (:ref:`index <all-salt.serializers>`) ``serializers`` [#no-fs]_ ``serializers_dirs``
|
||||||
|
SPM pkgdb ``salt.spm.pkgdb`` ``pkgdb`` [#no-fs]_ ``pkgdb_dirs``
|
||||||
|
SPM pkgfiles ``salt.spm.pkgfiles`` ``pkgfiles`` [#no-fs]_ ``pkgfiles_dirs``
|
||||||
|
SSH Wrapper ``salt.client.ssh.wrapper`` ``wrapper`` [#no-fs]_ ``wrapper_dirs``
|
||||||
|
State ``salt.states`` (:ref:`index <all-salt.states>`) ``states`` ``states_dirs``
|
||||||
|
Thorium ``salt.thorium`` (:ref:`index <all-salt.thorium>`) ``thorium`` [#no-fs]_ ``thorium_dirs``
|
||||||
|
Top ``salt.tops`` (:ref:`index <all-salt.tops>`) ``tops`` ``top_dirs``
|
||||||
|
Util ``salt.utils`` ``utils`` ``utils_dirs``
|
||||||
|
Wheel ``salt.wheels`` (:ref:`index <all-salt.wheel>`) ``wheel`` ``wheel_dirs``
|
||||||
|
============ ================================================================ ========================= =====================
|
||||||
|
|
||||||
|
.. [#no-fs] These modules cannot be loaded from the Salt File Server.
|
||||||
|
|
||||||
|
.. note:
|
||||||
|
While it is possible to import modules directly with the import statement,
|
||||||
|
it is strongly recommended that the appropriate
|
||||||
|
:ref:`dunder dictionary <dunder-dictionaries>` is used to access them
|
||||||
|
instead. This is because a number of factors affect module names, module
|
||||||
|
selection, and module overloading.
|
||||||
|
|
||||||
|
Auth
|
||||||
|
----
|
||||||
|
|
||||||
|
The auth module system allows for external authentication routines to be easily
|
||||||
|
added into Salt. The `auth` function needs to be implemented to satisfy the
|
||||||
|
requirements of an auth module. Use the ``pam`` module as an example.
|
||||||
|
|
||||||
|
See :ref:`External Authentication System <acl-eauth>` for more about
|
||||||
|
authentication in Salt.
|
||||||
|
|
||||||
|
Beacon
|
||||||
|
------
|
||||||
|
|
||||||
|
* :ref:`Writing Beacons <writing-beacons>`
|
||||||
|
|
||||||
|
Beacons are polled by the Salt event loop to monitor non-salt processes. See
|
||||||
|
:ref:`Beacons <beacons>` for more information about the beacon system.
|
||||||
|
|
||||||
|
Cache
|
||||||
|
-----
|
||||||
|
|
||||||
|
The minion cache is used by the master to store various information about
|
||||||
|
minions. See :ref:`Minion Data Cache <cache>` for more information.
|
||||||
|
|
||||||
|
Cloud
|
||||||
|
-----
|
||||||
|
|
||||||
|
Cloud modules are backend implementations used by :ref:`Salt Cloud <salt-cloud>`.
|
||||||
|
|
||||||
|
Engine
|
||||||
|
------
|
||||||
|
|
||||||
|
Engines are open-ended services managed by the Salt daemon (both master and
|
||||||
|
minion). They may interact with event loop, call other modules, or a variety of
|
||||||
|
non-salt tasks. See :ref:`Salt Engines <engines>` for complete details.
|
||||||
|
|
||||||
|
Execution
|
||||||
|
---------
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 1
|
||||||
|
:glob:
|
||||||
|
|
||||||
|
/ref/modules/index
|
||||||
|
|
||||||
|
Execution modules make up the core of the functionality used by Salt to
|
||||||
|
interact with client systems. The execution modules create the core system
|
||||||
|
management library used by all Salt systems, including states, which
|
||||||
|
interact with minion systems.
|
||||||
|
|
||||||
|
Execution modules are completely open ended in their execution. They can
|
||||||
|
be used to do anything required on a minion, from installing packages to
|
||||||
|
detecting information about the system. The only restraint in execution
|
||||||
|
modules is that the defined functions always return a JSON serializable
|
||||||
|
object.
|
||||||
|
|
||||||
|
Executor
|
||||||
|
--------
|
||||||
|
|
||||||
|
Executors control how execution modules get called. The default is to just call
|
||||||
|
them, but this can be customized.
|
||||||
|
|
||||||
|
File Server
|
||||||
|
-----------
|
||||||
|
|
||||||
|
The file server module system is used to create file server backends used by the
|
||||||
|
Salt Master. These modules need to implement the functions used in the
|
||||||
|
fileserver subsystem. Use the ``gitfs`` module as an example.
|
||||||
|
|
||||||
|
See :ref:`File Server Backends <file-server-backends>` for more information.
|
||||||
|
|
||||||
|
Grains
|
||||||
|
------
|
||||||
|
|
||||||
|
* :ref:`writing-grains`
|
||||||
|
|
||||||
|
Grain modules define extra routines to populate grains data. All defined
|
||||||
|
public functions will be executed and MUST return a Python dict object. The
|
||||||
|
dict keys will be added to the grains made available to the minion.
|
||||||
|
|
||||||
|
See :ref:`Grains <grains>` for more.
|
||||||
|
|
||||||
|
Log Handler
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Log handlers allows the logs from salt (master or minion) to be sent to log
|
||||||
|
aggregation systems.
|
||||||
|
|
||||||
|
Net API
|
||||||
|
-------
|
||||||
|
|
||||||
|
Net API modules are the actual server implementation used by Salt API.
|
||||||
|
|
||||||
|
Output
|
||||||
|
------
|
||||||
|
|
||||||
|
The output modules supply the outputter system with routines to display data
|
||||||
|
in the terminal. These modules are very simple and only require the `output`
|
||||||
|
function to execute. The default system outputter is the ``nested`` module.
|
||||||
|
|
||||||
|
Pillar
|
||||||
|
------
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 1
|
||||||
|
:glob:
|
||||||
|
|
||||||
|
external_pillars
|
||||||
|
|
||||||
|
Used to define optional external pillar systems. The pillar generated via
|
||||||
|
the filesystem pillar is passed into external pillars. This is commonly used
|
||||||
|
as a bridge to database data for pillar, but is also the backend to the libvirt
|
||||||
|
state used to generate and sign libvirt certificates on the fly.
|
||||||
|
|
||||||
|
Proxy
|
||||||
|
-----
|
||||||
|
|
||||||
|
:ref:`Proxy Minions <proxy-minion>` are a way to manage devices that cannot run
|
||||||
|
a full minion directly.
|
||||||
|
|
||||||
|
Renderers
|
||||||
|
---------
|
||||||
|
|
||||||
|
Renderers are the system used to render sls files into salt highdata for the
|
||||||
|
state compiler. They can be as simple as the ``py`` renderer and as complex as
|
||||||
|
``stateconf`` and ``pydsl``.
|
||||||
|
|
||||||
|
Returners
|
||||||
|
---------
|
||||||
|
|
||||||
|
Returners are used to send data from minions to external sources, commonly
|
||||||
|
databases. A full returner will implement all routines to be supported as an
|
||||||
|
external job cache. Use the ``redis`` returner as an example.
|
||||||
|
|
||||||
|
Roster
|
||||||
|
------
|
||||||
|
|
||||||
|
The :ref:`Roster system <ssh-roster>` is used by Salt SSH to enumerate devices.
|
||||||
|
|
||||||
|
Runners
|
||||||
|
-------
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 1
|
||||||
|
:glob:
|
||||||
|
|
||||||
|
/ref/runners/index
|
||||||
|
|
||||||
|
Runners are purely master-side execution sequences.
|
||||||
|
|
||||||
|
SDB
|
||||||
|
---
|
||||||
|
|
||||||
|
* :ref:`Writing SDB Modules <sdb-writing-modules>`
|
||||||
|
|
||||||
|
SDB is a way to store data that's not associated with a minion. See
|
||||||
|
:ref:`Storing Data in Other Databases <sdb>`.
|
||||||
|
|
||||||
|
Search
|
||||||
|
------
|
||||||
|
|
||||||
|
A system for indexing the file server and pillars. Removed in 2018.3.
|
||||||
|
|
||||||
|
Serializer
|
||||||
|
----------
|
||||||
|
|
||||||
|
Primarily used with :py:func:`file.serialize <salt.states.file.serialize>`.
|
||||||
|
|
||||||
|
State
|
||||||
|
-----
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 1
|
||||||
|
:glob:
|
||||||
|
|
||||||
|
/ref/states/index
|
||||||
|
|
||||||
|
State modules are used to define the state interfaces used by Salt States.
|
||||||
|
These modules are restrictive in that they must follow a number of rules to
|
||||||
|
function properly.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
State modules define the available routines in sls files. If calling
|
||||||
|
an execution module directly is desired, take a look at the `module`
|
||||||
|
state.
|
||||||
|
|
||||||
|
SPM pkgdb
|
||||||
|
---------
|
||||||
|
|
||||||
|
* :ref:`SPM Development Guide: Package Database <spm-development-pkgdb>`
|
||||||
|
|
||||||
|
pkgdb modules provides storage backends to the package database.
|
||||||
|
|
||||||
|
SPM pkgfiles
|
||||||
|
------------
|
||||||
|
|
||||||
|
* :ref:`SPM Development Guide: Package Database <spm-development-pkgfiles>`
|
||||||
|
|
||||||
|
pkgfiles modules handle the actual installation.
|
||||||
|
|
||||||
|
SSH Wrapper
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Replacement execution modules for :ref:`Salt SSH <salt-ssh>`.
|
||||||
|
|
||||||
|
Thorium
|
||||||
|
-------
|
||||||
|
|
||||||
|
Modules for use in the :ref:`Thorium <thorium-reactor>` event reactor.
|
||||||
|
|
||||||
|
Tops
|
||||||
|
----
|
||||||
|
|
||||||
|
Tops modules are used to convert external data sources into top file data for
|
||||||
|
the state system.
|
||||||
|
|
||||||
|
Util
|
||||||
|
----
|
||||||
|
|
||||||
|
Just utility modules to use with other modules via ``__utils__`` (see
|
||||||
|
:ref:`Dunder Dictionaries <dunder-dictionaries>`).
|
||||||
|
|
||||||
|
Wheel
|
||||||
|
-----
|
||||||
|
|
||||||
|
The wheel system is used to manage master side management routines. These
|
||||||
|
routines are primarily intended for the API to enable master configuration.
|
@ -13,3 +13,5 @@ Security Fix
|
|||||||
CVE-2018-15751 Remote command execution and incorrect access control when using salt-api.
|
CVE-2018-15751 Remote command execution and incorrect access control when using salt-api.
|
||||||
|
|
||||||
CVE-2018-15750 Directory traversal vulnerability when using salt-api. Allows an attacker to determine what files exist on a server when querying /run or /events.
|
CVE-2018-15750 Directory traversal vulnerability when using salt-api. Allows an attacker to determine what files exist on a server when querying /run or /events.
|
||||||
|
|
||||||
|
Credit and thanks for discovery and responsible disclosure: nullbr4in, xcuter, koredge, loupos, blackcon, Naver Business Platform
|
||||||
|
@ -20,6 +20,8 @@ CVE-2018-15751 Remote command execution and incorrect access control when using
|
|||||||
|
|
||||||
CVE-2018-15750 Directory traversal vulnerability when using salt-api. Allows an attacker to determine what files exist on a server when querying /run or /events.
|
CVE-2018-15750 Directory traversal vulnerability when using salt-api. Allows an attacker to determine what files exist on a server when querying /run or /events.
|
||||||
|
|
||||||
|
Credit and thanks for discovery and responsible disclosure: nullbr4in, xcuter, koredge, loupos, blackcon, Naver Business Platform
|
||||||
|
|
||||||
New win_snmp behavior
|
New win_snmp behavior
|
||||||
=====================
|
=====================
|
||||||
|
|
||||||
|
@ -50,6 +50,8 @@ CVE-2018-15751 Remote command execution and incorrect access control when using
|
|||||||
|
|
||||||
CVE-2018-15750 Directory traversal vulnerability when using salt-api. Allows an attacker to determine what files exist on a server when querying /run or /events.
|
CVE-2018-15750 Directory traversal vulnerability when using salt-api. Allows an attacker to determine what files exist on a server when querying /run or /events.
|
||||||
|
|
||||||
|
Credit and thanks for discovery and responsible disclosure: nullbr4in, xcuter, koredge, loupos, blackcon, Naver Business Platform
|
||||||
|
|
||||||
Changes to win_timezone
|
Changes to win_timezone
|
||||||
=======================
|
=======================
|
||||||
|
|
||||||
@ -92,7 +94,7 @@ Example:
|
|||||||
cmd.run 'echo '\''h=\"baz\"'\''' runas=macuser
|
cmd.run 'echo '\''h=\"baz\"'\''' runas=macuser
|
||||||
|
|
||||||
Changelog for v2018.3.2..v2018.3.3
|
Changelog for v2018.3.2..v2018.3.3
|
||||||
=================================================================
|
==================================
|
||||||
|
|
||||||
*Generated at: 2018-09-21 17:45:27 UTC*
|
*Generated at: 2018-09-21 17:45:27 UTC*
|
||||||
|
|
||||||
@ -507,7 +509,7 @@ Changelog for v2018.3.2..v2018.3.3
|
|||||||
|
|
||||||
* 3d26affa10 Fix remaining file state integration tests (py3)
|
* 3d26affa10 Fix remaining file state integration tests (py3)
|
||||||
|
|
||||||
* **PR** `#49171`_: (`Ch3LL`_) [2018.3.3] cherry pick `#49103`_
|
* **PR** `#49171`_: (`Ch3LL`_) [2018.3.3] cherry pick `#49103`_
|
||||||
@ *2018-08-17 20:23:32 UTC*
|
@ *2018-08-17 20:23:32 UTC*
|
||||||
|
|
||||||
* **PR** `#49103`_: (`dwoz`_) Install the launcher so we can execute py files (refs: `#49171`_)
|
* **PR** `#49103`_: (`dwoz`_) Install the launcher so we can execute py files (refs: `#49171`_)
|
||||||
@ -1630,7 +1632,7 @@ Changelog for v2018.3.2..v2018.3.3
|
|||||||
|
|
||||||
* **ISSUE** `#46896`_: (`Poil`_) Proxy + file.managed => Comment: Failed to cache xxx invalid arguments to setopt (refs: `#48754`_)
|
* **ISSUE** `#46896`_: (`Poil`_) Proxy + file.managed => Comment: Failed to cache xxx invalid arguments to setopt (refs: `#48754`_)
|
||||||
|
|
||||||
* **PR** `#48754`_: (`lomeroe`_) send proxy/ca_cert parameters as strings (not unicode) to tornado httpclient
|
* **PR** `#48754`_: (`lomeroe`_) send proxy/ca_cert parameters as strings (not unicode) to tornado httpclient
|
||||||
@ *2018-07-25 14:55:42 UTC*
|
@ *2018-07-25 14:55:42 UTC*
|
||||||
|
|
||||||
* 030c921914 Merge pull request `#48754`_ from lomeroe/fix-tornado-proxy
|
* 030c921914 Merge pull request `#48754`_ from lomeroe/fix-tornado-proxy
|
||||||
@ -3075,7 +3077,7 @@ Changelog for v2018.3.2..v2018.3.3
|
|||||||
|
|
||||||
* dae65da256 Merge branch '2018.3.1' into '2018.3'
|
* dae65da256 Merge branch '2018.3.1' into '2018.3'
|
||||||
|
|
||||||
* **PR** `#48186`_: (`rallytime`_) Add autodoc module for saltcheck.py
|
* **PR** `#48186`_: (`rallytime`_) Add autodoc module for saltcheck.py
|
||||||
@ *2018-06-19 19:03:55 UTC*
|
@ *2018-06-19 19:03:55 UTC*
|
||||||
|
|
||||||
* 5b4897f050 Merge pull request `#48186`_ from rallytime/saltcheck-docs
|
* 5b4897f050 Merge pull request `#48186`_ from rallytime/saltcheck-docs
|
||||||
@ -3362,11 +3364,11 @@ Changelog for v2018.3.2..v2018.3.3
|
|||||||
* **PR** `#48109`_: (`rallytime`_) Back-port `#47851`_ to 2018.3
|
* **PR** `#48109`_: (`rallytime`_) Back-port `#47851`_ to 2018.3
|
||||||
@ *2018-06-14 13:09:04 UTC*
|
@ *2018-06-14 13:09:04 UTC*
|
||||||
|
|
||||||
* **PR** `#47851`_: (`rares-pop`_) Fixup! add master.py:FileserverUpdate **kwargs (refs: `#48109`_)
|
* **PR** `#47851`_: (`rares-pop`_) Fixup! add master.py:FileserverUpdate \*\*kwargs (refs: `#48109`_)
|
||||||
|
|
||||||
* 2902ee0b14 Merge pull request `#48109`_ from rallytime/bp-47851
|
* 2902ee0b14 Merge pull request `#48109`_ from rallytime/bp-47851
|
||||||
|
|
||||||
* e9dc30bf8e Fixup! add master.py:FileserverUpdate **kwargs
|
* e9dc30bf8e Fixup! add master.py:FileserverUpdate \*\*kwargs
|
||||||
|
|
||||||
* **ISSUE** `#47925`_: (`JonGriggs`_) GitFS looking for files in the master branch only (refs: `#47943`_)
|
* **ISSUE** `#47925`_: (`JonGriggs`_) GitFS looking for files in the master branch only (refs: `#47943`_)
|
||||||
|
|
||||||
@ -3377,7 +3379,7 @@ Changelog for v2018.3.2..v2018.3.3
|
|||||||
|
|
||||||
* 534e1a7100 Merge branch '2018.3' into issue47925
|
* 534e1a7100 Merge branch '2018.3' into issue47925
|
||||||
|
|
||||||
* **PR** `#48089`_: (`rallytime`_) Update release versions for the 2018.3 branch
|
* **PR** `#48089`_: (`rallytime`_) Update release versions for the 2018.3 branch
|
||||||
@ *2018-06-13 14:03:44 UTC*
|
@ *2018-06-13 14:03:44 UTC*
|
||||||
|
|
||||||
* 9e1d0040e4 Merge pull request `#48089`_ from rallytime/update_version_doc_2018.3
|
* 9e1d0040e4 Merge pull request `#48089`_ from rallytime/update_version_doc_2018.3
|
||||||
|
@ -4,3 +4,11 @@ In Progress: Salt 2018.3.4 Release Notes
|
|||||||
|
|
||||||
Version 2018.3.4 is an **unreleased** bugfix release for :ref:`2018.3.0 <release-2018-3-0>`.
|
Version 2018.3.4 is an **unreleased** bugfix release for :ref:`2018.3.0 <release-2018-3-0>`.
|
||||||
This release is still in progress and has not been released yet.
|
This release is still in progress and has not been released yet.
|
||||||
|
|
||||||
|
|
||||||
|
State Changes
|
||||||
|
=============
|
||||||
|
|
||||||
|
- The :py:func:`host.present <salt.states.host.present>` state can now remove
|
||||||
|
the specified hostname from IPs not specified in the state. This can be done
|
||||||
|
by setting the newly-added ``clean`` argument to ``True``.
|
||||||
|
@ -144,6 +144,7 @@ When writing Salt modules, it is not recommended to call ``sdb.get`` directly,
|
|||||||
as it requires the user to provide values in SDB, using a specific URI. Use
|
as it requires the user to provide values in SDB, using a specific URI. Use
|
||||||
``config.get`` instead.
|
``config.get`` instead.
|
||||||
|
|
||||||
|
.. _sdb-writing-modules:
|
||||||
|
|
||||||
Writing SDB Modules
|
Writing SDB Modules
|
||||||
===================
|
===================
|
||||||
|
@ -20,6 +20,7 @@ marked as required are crucial to SPM's core functionality, while arguments that
|
|||||||
are marked as optional are provided as a benefit to the module, if it needs to
|
are marked as optional are provided as a benefit to the module, if it needs to
|
||||||
use them.
|
use them.
|
||||||
|
|
||||||
|
.. _spm-development-pkgdb:
|
||||||
|
|
||||||
Package Database
|
Package Database
|
||||||
----------------
|
----------------
|
||||||
@ -146,6 +147,8 @@ The only argument that is expected is ``db_``, which is the package database
|
|||||||
file.
|
file.
|
||||||
|
|
||||||
|
|
||||||
|
.. _spm-development-pkgfiles:
|
||||||
|
|
||||||
Package Files
|
Package Files
|
||||||
-------------
|
-------------
|
||||||
By default, package files are installed using the ``local`` module. This module
|
By default, package files are installed using the ``local`` module. This module
|
||||||
|
@ -90,9 +90,9 @@ shorthand for having to type out complicated compound expressions.
|
|||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
nodegroups:
|
nodegroups:
|
||||||
group1: 'L@foo.domain.com,bar.domain.com,baz.domain.com and bl*.domain.com'
|
group1: 'L@foo.domain.com,bar.domain.com,baz.domain.com and bl*.domain.com'
|
||||||
group2: 'G@os:Debian and foo.domain.com'
|
group2: 'G@os:Debian and foo.domain.com'
|
||||||
group3: 'G@os:Debian and N@group1'
|
group3: 'G@os:Debian and N@group1'
|
||||||
|
|
||||||
|
|
||||||
Advanced Targeting Methods
|
Advanced Targeting Methods
|
||||||
|
@ -4,386 +4,40 @@
|
|||||||
Salt Bootstrap
|
Salt Bootstrap
|
||||||
==============
|
==============
|
||||||
|
|
||||||
The Salt Bootstrap script allows for a user to install the Salt Minion or
|
The Salt Bootstrap Script allows a user to install the Salt Minion or Master
|
||||||
Master on a variety of system distributions and versions. This shell script
|
on a variety of system distributions and versions.
|
||||||
known as ``bootstrap-salt.sh`` runs through a series of checks to determine
|
|
||||||
the operating system type and version. It then installs the Salt binaries
|
|
||||||
using the appropriate methods. The Salt Bootstrap script installs the
|
|
||||||
minimum number of packages required to run Salt. This means that in the event
|
|
||||||
you run the bootstrap to install via package, Git will not be installed.
|
|
||||||
Installing the minimum number of packages helps ensure the script stays as
|
|
||||||
lightweight as possible, assuming the user will install any other required
|
|
||||||
packages after the Salt binaries are present on the system. The script source
|
|
||||||
is available on GitHub: https://github.com/saltstack/salt-bootstrap
|
|
||||||
|
|
||||||
|
The Salt Bootstrap Script is a shell script is known as ``bootstrap-salt.sh``.
|
||||||
|
It runs through a series of checks to determine the operating system type and
|
||||||
|
version. It then installs the Salt binaries using the appropriate methods.
|
||||||
|
|
||||||
Supported Operating Systems
|
The Salt Bootstrap Script installs the minimum number of packages required to
|
||||||
---------------------------
|
run Salt. This means that in the event you run the bootstrap to install via
|
||||||
|
package, Git will not be installed. Installing the minimum number of packages
|
||||||
|
helps ensure the script stays as lightweight as possible, assuming the user
|
||||||
|
will install any other required packages after the Salt binaries are present
|
||||||
|
on the system.
|
||||||
|
|
||||||
|
The Salt Bootstrap Script is maintained in a separate repo from Salt, complete
|
||||||
|
with its own issues, pull requests, contributing guidelines, release protocol,
|
||||||
|
etc.
|
||||||
|
|
||||||
|
To learn more, please see the Salt Bootstrap repo links:
|
||||||
|
|
||||||
|
- `Salt Bootstrap repo`_
|
||||||
|
- `README`_: includes supported operating systems, example usage, and more.
|
||||||
|
- `Contributing Guidelines`_
|
||||||
|
- `Release Process`_
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
In the event you do not see your distribution or version available please
|
The Salt Bootstrap script can be found in the Salt repo under the
|
||||||
review the develop branch on GitHub as it may contain updates that are
|
``salt/cloud/deploy/bootstrap-salt.sh`` path. Any changes to this file
|
||||||
not present in the stable release:
|
will be overwritten! Bug fixes and feature additions must be submitted
|
||||||
https://github.com/saltstack/salt-bootstrap/tree/develop
|
via the `Salt Bootstrap repo`_. Please see the Salt Bootstrap Script's
|
||||||
|
`Release Process`_ for more information.
|
||||||
|
|
||||||
Debian and derivatives
|
.. _Salt Bootstrap repo: https://github.com/saltstack/salt-bootstrap
|
||||||
~~~~~~~~~~~~~~~~~~~~~~
|
.. _README: https://github.com/saltstack/salt-bootstrap#bootstrapping-salt
|
||||||
|
.. _Contributing Guidelines: https://github.com/saltstack/salt-bootstrap/blob/develop/CONTRIBUTING.md
|
||||||
- Debian GNU/Linux 7/8
|
.. _Release Process: https://github.com/saltstack/salt-bootstrap/blob/develop/CONTRIBUTING.md#release-information
|
||||||
- Linux Mint Debian Edition 1 (based on Debian 8)
|
|
||||||
- Kali Linux 1.0 (based on Debian 7)
|
|
||||||
|
|
||||||
|
|
||||||
Red Hat family
|
|
||||||
~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
- Amazon Linux 2012.09/2013.03/2013.09/2014.03/2014.09
|
|
||||||
- CentOS 5/6/7
|
|
||||||
- Fedora 17/18/20/21/22
|
|
||||||
- Oracle Linux 5/6/7
|
|
||||||
- Red Hat Enterprise Linux 5/6/7
|
|
||||||
- Scientific Linux 5/6/7
|
|
||||||
|
|
||||||
|
|
||||||
SUSE family
|
|
||||||
~~~~~~~~~~~
|
|
||||||
|
|
||||||
- openSUSE 12/13
|
|
||||||
- openSUSE Leap 42
|
|
||||||
- openSUSE Tumbleweed 2015
|
|
||||||
- SUSE Linux Enterprise Server 11 SP1/11 SP2/11 SP3/12
|
|
||||||
|
|
||||||
|
|
||||||
Ubuntu and derivatives
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
- Elementary OS 0.2 (based on Ubuntu 12.04)
|
|
||||||
- Linaro 12.04
|
|
||||||
- Linux Mint 13/14/16/17
|
|
||||||
- Trisquel GNU/Linux 6 (based on Ubuntu 12.04)
|
|
||||||
- Ubuntu 10.x/11.x/12.x/13.x/14.x/15.x/16.x
|
|
||||||
|
|
||||||
|
|
||||||
Other Linux distro
|
|
||||||
~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
- Arch Linux
|
|
||||||
- Gentoo
|
|
||||||
|
|
||||||
|
|
||||||
UNIX systems
|
|
||||||
~~~~~~~~~~~~
|
|
||||||
|
|
||||||
**BSD**:
|
|
||||||
|
|
||||||
- OpenBSD
|
|
||||||
- FreeBSD 9/10/11
|
|
||||||
|
|
||||||
**SunOS**:
|
|
||||||
|
|
||||||
- SmartOS
|
|
||||||
|
|
||||||
|
|
||||||
Example Usage
|
|
||||||
-------------
|
|
||||||
|
|
||||||
If you're looking for the *one-liner* to install Salt, please scroll to the
|
|
||||||
bottom and use the instructions for `Installing via an Insecure One-Liner`_
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
In every two-step example, you would be well-served to examine the downloaded file and examine
|
|
||||||
it to ensure that it does what you expect.
|
|
||||||
|
|
||||||
|
|
||||||
The Salt Bootstrap script has a wide variety of options that can be passed as
|
|
||||||
well as several ways of obtaining the bootstrap script itself.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
These examples below show how to bootstrap Salt directly from GitHub or other Git repository.
|
|
||||||
Run the script without any parameters to get latest stable Salt packages for your system from
|
|
||||||
`SaltStack corporate repository`_. See first example in the `Install using wget`_ section.
|
|
||||||
|
|
||||||
.. _`SaltStack corporate repository`: https://repo.saltstack.com/
|
|
||||||
|
|
||||||
|
|
||||||
Install using curl
|
|
||||||
~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Using ``curl`` to install latest development version from GitHub:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
curl -o bootstrap-salt.sh -L https://bootstrap.saltstack.com
|
|
||||||
sudo sh bootstrap-salt.sh git develop
|
|
||||||
|
|
||||||
If you want to install a specific release version (based on the Git tags):
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
curl -o bootstrap-salt.sh -L https://bootstrap.saltstack.com
|
|
||||||
sudo sh bootstrap-salt.sh git v2015.8.8
|
|
||||||
|
|
||||||
To install a specific branch from a Git fork:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
curl -o bootstrap-salt.sh -L https://bootstrap.saltstack.com
|
|
||||||
sudo sh bootstrap-salt.sh -g https://github.com/myuser/salt.git git mybranch
|
|
||||||
|
|
||||||
If all you want is to install a ``salt-master`` using latest Git:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
curl -o bootstrap-salt.sh -L https://bootstrap.saltstack.com
|
|
||||||
sudo sh bootstrap-salt.sh -M -N git develop
|
|
||||||
|
|
||||||
If your host has Internet access only via HTTP proxy:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
PROXY='http://user:password@myproxy.example.com:3128'
|
|
||||||
curl -o bootstrap-salt.sh -L -x "$PROXY" https://bootstrap.saltstack.com
|
|
||||||
sudo sh bootstrap-salt.sh -G -H "$PROXY" git
|
|
||||||
|
|
||||||
|
|
||||||
Install using wget
|
|
||||||
~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Using ``wget`` to install your distribution's stable packages:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
wget -O bootstrap-salt.sh https://bootstrap.saltstack.com
|
|
||||||
sudo sh bootstrap-salt.sh
|
|
||||||
|
|
||||||
Downloading the script from develop branch:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
wget -O bootstrap-salt.sh https://bootstrap.saltstack.com/develop
|
|
||||||
sudo sh bootstrap-salt.sh
|
|
||||||
|
|
||||||
Installing a specific version from git using ``wget``:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
wget -O bootstrap-salt.sh https://bootstrap.saltstack.com
|
|
||||||
sudo sh bootstrap-salt.sh -P git v2015.8.8
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
On the above example we added `-P` which will allow PIP packages to be installed if required but
|
|
||||||
it's not a necessary flag for Git based bootstraps.
|
|
||||||
|
|
||||||
|
|
||||||
Install using Python
|
|
||||||
~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
If you already have Python installed, ``python 2.6``, then it's as easy as:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
python -m urllib "https://bootstrap.saltstack.com" > bootstrap-salt.sh
|
|
||||||
sudo sh bootstrap-salt.sh git develop
|
|
||||||
|
|
||||||
All Python versions should support the following in-line code:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
python -c 'import urllib; print urllib.urlopen("https://bootstrap.saltstack.com").read()' > bootstrap-salt.sh
|
|
||||||
sudo sh bootstrap-salt.sh git develop
|
|
||||||
|
|
||||||
|
|
||||||
Install using fetch
|
|
||||||
~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
On a FreeBSD base system you usually don't have either of the above binaries available. You **do**
|
|
||||||
have ``fetch`` available though:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
fetch -o bootstrap-salt.sh https://bootstrap.saltstack.com
|
|
||||||
sudo sh bootstrap-salt.sh
|
|
||||||
|
|
||||||
If you have any SSL issues install ``ca_root_nssp``:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
pkg install ca_root_nssp
|
|
||||||
|
|
||||||
And either copy the certificates to the place where fetch can find them:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
cp /usr/local/share/certs/ca-root-nss.crt /etc/ssl/cert.pem
|
|
||||||
|
|
||||||
Or link them to the right place:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
ln -s /usr/local/share/certs/ca-root-nss.crt /etc/ssl/cert.pem
|
|
||||||
|
|
||||||
|
|
||||||
Installing via an Insecure One-Liner
|
|
||||||
------------------------------------
|
|
||||||
|
|
||||||
The following examples illustrate how to install Salt via a one-liner.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
Warning! These methods do not involve a verification step and assume that
|
|
||||||
the delivered file is trustworthy.
|
|
||||||
|
|
||||||
|
|
||||||
Any of the example above which use two-lines can be made to run in a single-line
|
|
||||||
configuration with minor modifications.
|
|
||||||
|
|
||||||
For example, using ``curl`` to install your distribution's stable packages:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
curl -L https://bootstrap.saltstack.com | sudo sh
|
|
||||||
|
|
||||||
|
|
||||||
Using ``wget`` to install your distribution's stable packages:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
wget -O - https://bootstrap.saltstack.com | sudo sh
|
|
||||||
|
|
||||||
|
|
||||||
Installing the latest develop branch of Salt:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
curl -L https://bootstrap.saltstack.com | sudo sh -s -- git develop
|
|
||||||
|
|
||||||
|
|
||||||
Command Line Options
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
Here's a summary of the command line options:
|
|
||||||
|
|
||||||
.. code-block:: text
|
|
||||||
|
|
||||||
$ sh bootstrap-salt.sh -h
|
|
||||||
|
|
||||||
Installation types:
|
|
||||||
- stable Install latest stable release. This is the default
|
|
||||||
install type
|
|
||||||
- stable [branch] Install latest version on a branch. Only supported
|
|
||||||
for packages available at repo.saltstack.com
|
|
||||||
- stable [version] Install a specific version. Only supported for
|
|
||||||
packages available at repo.saltstack.com
|
|
||||||
- daily Ubuntu specific: configure SaltStack Daily PPA
|
|
||||||
- testing RHEL-family specific: configure EPEL testing repo
|
|
||||||
- git Install from the head of the develop branch
|
|
||||||
- git [ref] Install from any git ref (such as a branch, tag, or
|
|
||||||
commit)
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
- bootstrap-salt.sh
|
|
||||||
- bootstrap-salt.sh stable
|
|
||||||
- bootstrap-salt.sh stable 2017.7
|
|
||||||
- bootstrap-salt.sh stable 2017.7.2
|
|
||||||
- bootstrap-salt.sh daily
|
|
||||||
- bootstrap-salt.sh testing
|
|
||||||
- bootstrap-salt.sh git
|
|
||||||
- bootstrap-salt.sh git 2017.7
|
|
||||||
- bootstrap-salt.sh git v2017.7.2
|
|
||||||
- bootstrap-salt.sh git 06f249901a2e2f1ed310d58ea3921a129f214358
|
|
||||||
|
|
||||||
Options:
|
|
||||||
-h Display this message
|
|
||||||
-v Display script version
|
|
||||||
-n No colours
|
|
||||||
-D Show debug output
|
|
||||||
-c Temporary configuration directory
|
|
||||||
-g Salt Git repository URL. Default: https://github.com/saltstack/salt.git
|
|
||||||
-w Install packages from downstream package repository rather than
|
|
||||||
upstream, saltstack package repository. This is currently only
|
|
||||||
implemented for SUSE.
|
|
||||||
-k Temporary directory holding the minion keys which will pre-seed
|
|
||||||
the master.
|
|
||||||
-s Sleep time used when waiting for daemons to start, restart and when
|
|
||||||
checking for the services running. Default: 3
|
|
||||||
-L Also install salt-cloud and required python-libcloud package
|
|
||||||
-M Also install salt-master
|
|
||||||
-S Also install salt-syndic
|
|
||||||
-N Do not install salt-minion
|
|
||||||
-X Do not start daemons after installation
|
|
||||||
-d Disables checking if Salt services are enabled to start on system boot.
|
|
||||||
You can also do this by touching /tmp/disable_salt_checks on the target
|
|
||||||
host. Default: ${BS_FALSE}
|
|
||||||
-P Allow pip based installations. On some distributions the required salt
|
|
||||||
packages or its dependencies are not available as a package for that
|
|
||||||
distribution. Using this flag allows the script to use pip as a last
|
|
||||||
resort method. NOTE: This only works for functions which actually
|
|
||||||
implement pip based installations.
|
|
||||||
-U If set, fully upgrade the system prior to bootstrapping Salt
|
|
||||||
-I If set, allow insecure connections while downloading any files. For
|
|
||||||
example, pass '--no-check-certificate' to 'wget' or '--insecure' to
|
|
||||||
'curl'. On Debian and Ubuntu, using this option with -U allows one to obtain
|
|
||||||
GnuPG archive keys insecurely if distro has changed release signatures.
|
|
||||||
-F Allow copied files to overwrite existing (config, init.d, etc)
|
|
||||||
-K If set, keep the temporary files in the temporary directories specified
|
|
||||||
with -c and -k
|
|
||||||
-C Only run the configuration function. Implies -F (forced overwrite).
|
|
||||||
To overwrite Master or Syndic configs, -M or -S, respectively, must
|
|
||||||
also be specified. Salt installation will be omitted, but some of the
|
|
||||||
dependencies could be installed to write configuration with -j or -J.
|
|
||||||
-A Pass the salt-master DNS name or IP. This will be stored under
|
|
||||||
${BS_SALT_ETC_DIR}/minion.d/99-master-address.conf
|
|
||||||
-i Pass the salt-minion id. This will be stored under
|
|
||||||
${BS_SALT_ETC_DIR}/minion_id
|
|
||||||
-p Extra-package to install while installing Salt dependencies. One package
|
|
||||||
per -p flag. You're responsible for providing the proper package name.
|
|
||||||
-H Use the specified HTTP proxy for all download URLs (including https://).
|
|
||||||
For example: http://myproxy.example.com:3128
|
|
||||||
-Z Enable additional package repository for newer ZeroMQ
|
|
||||||
(only available for RHEL/CentOS/Fedora/Ubuntu based distributions)
|
|
||||||
-b Assume that dependencies are already installed and software sources are
|
|
||||||
set up. If git is selected, git tree is still checked out as dependency
|
|
||||||
step.
|
|
||||||
-f Force shallow cloning for git installations.
|
|
||||||
This may result in an "n/a" in the version number.
|
|
||||||
-l Disable ssl checks. When passed, switches "https" calls to "http" where
|
|
||||||
possible.
|
|
||||||
-V Install Salt into virtualenv
|
|
||||||
(only available for Ubuntu based distributions)
|
|
||||||
-a Pip install all Python pkg dependencies for Salt. Requires -V to install
|
|
||||||
all pip pkgs into the virtualenv.
|
|
||||||
(Only available for Ubuntu based distributions)
|
|
||||||
-r Disable all repository configuration performed by this script. This
|
|
||||||
option assumes all necessary repository configuration is already present
|
|
||||||
on the system.
|
|
||||||
-R Specify a custom repository URL. Assumes the custom repository URL
|
|
||||||
points to a repository that mirrors Salt packages located at
|
|
||||||
repo.saltstack.com. The option passed with -R replaces the
|
|
||||||
"repo.saltstack.com". If -R is passed, -r is also set. Currently only
|
|
||||||
works on CentOS/RHEL and Debian based distributions.
|
|
||||||
-J Replace the Master config file with data passed in as a JSON string. If
|
|
||||||
a Master config file is found, a reasonable effort will be made to save
|
|
||||||
the file with a ".bak" extension. If used in conjunction with -C or -F,
|
|
||||||
no ".bak" file will be created as either of those options will force
|
|
||||||
a complete overwrite of the file.
|
|
||||||
-j Replace the Minion config file with data passed in as a JSON string. If
|
|
||||||
a Minion config file is found, a reasonable effort will be made to save
|
|
||||||
the file with a ".bak" extension. If used in conjunction with -C or -F,
|
|
||||||
no ".bak" file will be created as either of those options will force
|
|
||||||
a complete overwrite of the file.
|
|
||||||
-q Quiet salt installation from git (setup.py install -q)
|
|
||||||
-x Changes the python version used to install a git version of salt. Currently
|
|
||||||
this is considered experimental and has only been tested on Centos 6. This
|
|
||||||
only works for git installations.
|
|
||||||
-y Installs a different python version on host. Currently this has only been
|
|
||||||
tested with Centos 6 and is considered experimental. This will install the
|
|
||||||
ius repo on the box if disable repo is false. This must be used in conjunction
|
|
||||||
with -x <pythonversion>. For example:
|
|
||||||
sh bootstrap.sh -P -y -x python2.7 git v2016.11.3
|
|
||||||
The above will install python27 and install the git version of salt using the
|
|
||||||
python2.7 executable. This only works for git and pip installations.
|
|
||||||
|
@ -230,81 +230,7 @@ class IPv6InterfaceScoped(ipaddress.IPv6Interface, IPv6AddressScoped):
|
|||||||
self.hostmask = self.network.hostmask
|
self.hostmask = self.network.hostmask
|
||||||
|
|
||||||
|
|
||||||
def ip_address(address):
|
|
||||||
"""Take an IP string/int and return an object of the correct type.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
address: A string or integer, the IP address. Either IPv4 or
|
|
||||||
IPv6 addresses may be supplied; integers less than 2**32 will
|
|
||||||
be considered to be IPv4 by default.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
An IPv4Address or IPv6Address object.
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
ValueError: if the *address* passed isn't either a v4 or a v6
|
|
||||||
address
|
|
||||||
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
return ipaddress.IPv4Address(address)
|
|
||||||
except (ipaddress.AddressValueError, ipaddress.NetmaskValueError) as err:
|
|
||||||
log.debug('Error while parsing IPv4 address: %s', address)
|
|
||||||
log.debug(err)
|
|
||||||
|
|
||||||
try:
|
|
||||||
return IPv6AddressScoped(address)
|
|
||||||
except (ipaddress.AddressValueError, ipaddress.NetmaskValueError) as err:
|
|
||||||
log.debug('Error while parsing IPv6 address: %s', address)
|
|
||||||
log.debug(err)
|
|
||||||
|
|
||||||
if isinstance(address, bytes):
|
|
||||||
raise ipaddress.AddressValueError('{} does not appear to be an IPv4 or IPv6 address. '
|
|
||||||
'Did you pass in a bytes (str in Python 2) instead '
|
|
||||||
'of a unicode object?'.format(repr(address)))
|
|
||||||
|
|
||||||
raise ValueError('{} does not appear to be an IPv4 or IPv6 address'.format(repr(address)))
|
|
||||||
|
|
||||||
|
|
||||||
def ip_interface(address):
|
|
||||||
"""Take an IP string/int and return an object of the correct type.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
address: A string or integer, the IP address. Either IPv4 or
|
|
||||||
IPv6 addresses may be supplied; integers less than 2**32 will
|
|
||||||
be considered to be IPv4 by default.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
An IPv4Interface or IPv6Interface object.
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
ValueError: if the string passed isn't either a v4 or a v6
|
|
||||||
address.
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
The IPv?Interface classes describe an Address on a particular
|
|
||||||
Network, so they're basically a combination of both the Address
|
|
||||||
and Network classes.
|
|
||||||
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
return ipaddress.IPv4Interface(address)
|
|
||||||
except (ipaddress.AddressValueError, ipaddress.NetmaskValueError) as err:
|
|
||||||
log.debug('Error while getting IPv4 interface for address %s', address)
|
|
||||||
log.debug(err)
|
|
||||||
|
|
||||||
try:
|
|
||||||
return ipaddress.IPv6Interface(address)
|
|
||||||
except (ipaddress.AddressValueError, ipaddress.NetmaskValueError) as err:
|
|
||||||
log.debug('Error while getting IPv6 interface for address %s', address)
|
|
||||||
log.debug(err)
|
|
||||||
|
|
||||||
raise ValueError('{} does not appear to be an IPv4 or IPv6 interface'.format(address))
|
|
||||||
|
|
||||||
|
|
||||||
if ipaddress:
|
if ipaddress:
|
||||||
ipaddress.IPv6Address = IPv6AddressScoped
|
ipaddress.IPv6Address = IPv6AddressScoped
|
||||||
if sys.version_info.major == 2:
|
if sys.version_info.major == 2:
|
||||||
ipaddress.IPv6Interface = IPv6InterfaceScoped
|
ipaddress.IPv6Interface = IPv6InterfaceScoped
|
||||||
ipaddress.ip_address = ip_address
|
|
||||||
ipaddress.ip_interface = ip_interface
|
|
||||||
|
@ -283,12 +283,14 @@ def auth(username, password):
|
|||||||
log.error('LDAP authentication requires python-ldap module')
|
log.error('LDAP authentication requires python-ldap module')
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
bind = None
|
||||||
|
|
||||||
# If bind credentials are configured, verify that we receive a valid bind
|
# If bind credentials are configured, verify that we receive a valid bind
|
||||||
if _config('binddn', mandatory=False) and _config('bindpw', mandatory=False):
|
if _config('binddn', mandatory=False) and _config('bindpw', mandatory=False):
|
||||||
bind = _bind_for_search(anonymous=_config('anonymous', mandatory=False))
|
search_bind = _bind_for_search(anonymous=_config('anonymous', mandatory=False))
|
||||||
|
|
||||||
# If username & password are not None, attempt to verify they are valid
|
# If username & password are not None, attempt to verify they are valid
|
||||||
if bind and username and password:
|
if search_bind and username and password:
|
||||||
bind = _bind(username, password,
|
bind = _bind(username, password,
|
||||||
anonymous=_config('auth_by_group_membership_only', mandatory=False)
|
anonymous=_config('auth_by_group_membership_only', mandatory=False)
|
||||||
and _config('anonymous', mandatory=False))
|
and _config('anonymous', mandatory=False))
|
||||||
|
@ -41,6 +41,16 @@ def validate(config):
|
|||||||
_config = {}
|
_config = {}
|
||||||
list(map(_config.update, config))
|
list(map(_config.update, config))
|
||||||
|
|
||||||
|
if 'emitatstartup' in _config:
|
||||||
|
if not isinstance(_config['emitatstartup'], bool):
|
||||||
|
return False, ('Configuration for load beacon option '
|
||||||
|
'emitatstartup must be a boolean.')
|
||||||
|
|
||||||
|
if 'onchangeonly' in _config:
|
||||||
|
if not isinstance(_config['onchangeonly'], bool):
|
||||||
|
return False, ('Configuration for load beacon option '
|
||||||
|
'onchangeonly must be a boolean.')
|
||||||
|
|
||||||
if 'averages' not in _config:
|
if 'averages' not in _config:
|
||||||
return False, ('Averages configuration is required'
|
return False, ('Averages configuration is required'
|
||||||
' for load beacon.')
|
' for load beacon.')
|
||||||
@ -61,6 +71,7 @@ def validate(config):
|
|||||||
return False, ('Configuration for load beacon: '
|
return False, ('Configuration for load beacon: '
|
||||||
'1m, 5m and 15m items must be '
|
'1m, 5m and 15m items must be '
|
||||||
'a list of two items.')
|
'a list of two items.')
|
||||||
|
|
||||||
return True, 'Valid beacon configuration'
|
return True, 'Valid beacon configuration'
|
||||||
|
|
||||||
|
|
||||||
@ -118,7 +129,7 @@ def beacon(config):
|
|||||||
if not LAST_STATUS:
|
if not LAST_STATUS:
|
||||||
for k in ['1m', '5m', '15m']:
|
for k in ['1m', '5m', '15m']:
|
||||||
LAST_STATUS[k] = avg_dict[k]
|
LAST_STATUS[k] = avg_dict[k]
|
||||||
if not config['emitatstartup']:
|
if not _config['emitatstartup']:
|
||||||
log.debug("Don't emit because emitatstartup is False")
|
log.debug("Don't emit because emitatstartup is False")
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
@ -303,19 +303,23 @@ def main(argv): # pylint: disable=W0613
|
|||||||
if OPTIONS.cmd_umask is not None:
|
if OPTIONS.cmd_umask is not None:
|
||||||
old_umask = os.umask(OPTIONS.cmd_umask) # pylint: disable=blacklisted-function
|
old_umask = os.umask(OPTIONS.cmd_umask) # pylint: disable=blacklisted-function
|
||||||
if OPTIONS.tty:
|
if OPTIONS.tty:
|
||||||
|
proc = subprocess.Popen(salt_argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
# Returns bytes instead of string on python 3
|
# Returns bytes instead of string on python 3
|
||||||
stdout, _ = subprocess.Popen(salt_argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
|
stdout, _ = proc.communicate()
|
||||||
sys.stdout.write(stdout.decode(encoding=get_system_encoding(), errors="replace"))
|
sys.stdout.write(stdout.decode(encoding=get_system_encoding(), errors="replace"))
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
|
retcode = proc.returncode
|
||||||
if OPTIONS.wipe:
|
if OPTIONS.wipe:
|
||||||
shutil.rmtree(OPTIONS.saltdir)
|
shutil.rmtree(OPTIONS.saltdir)
|
||||||
elif OPTIONS.wipe:
|
elif OPTIONS.wipe:
|
||||||
subprocess.call(salt_argv)
|
retcode = subprocess.call(salt_argv)
|
||||||
shutil.rmtree(OPTIONS.saltdir)
|
shutil.rmtree(OPTIONS.saltdir)
|
||||||
else:
|
else:
|
||||||
subprocess.call(salt_argv)
|
retcode = subprocess.call(salt_argv)
|
||||||
if OPTIONS.cmd_umask is not None:
|
if OPTIONS.cmd_umask is not None:
|
||||||
os.umask(old_umask) # pylint: disable=blacklisted-function
|
os.umask(old_umask) # pylint: disable=blacklisted-function
|
||||||
|
return retcode
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
sys.exit(main(sys.argv))
|
sys.exit(main(sys.argv))
|
||||||
|
@ -56,7 +56,7 @@ def execute(opts, data, func, args, kwargs):
|
|||||||
'salt-call',
|
'salt-call',
|
||||||
'--out', 'json',
|
'--out', 'json',
|
||||||
'--metadata',
|
'--metadata',
|
||||||
'-c', salt.syspaths.CONFIG_DIR,
|
'-c', opts.get('config_dir'),
|
||||||
'--',
|
'--',
|
||||||
data.get('fun')]
|
data.get('fun')]
|
||||||
if data['fun'] in ('state.sls', 'state.highstate', 'state.apply'):
|
if data['fun'] in ('state.sls', 'state.highstate', 'state.apply'):
|
||||||
|
@ -373,7 +373,7 @@ class Fileserver(object):
|
|||||||
|
|
||||||
# Avoid error logging when performing lookups in the LazyDict by
|
# Avoid error logging when performing lookups in the LazyDict by
|
||||||
# instead doing the membership check on the result of a call to its
|
# instead doing the membership check on the result of a call to its
|
||||||
# .keys() attribute rather than on the LaztDict itself.
|
# .keys() attribute rather than on the LazyDict itself.
|
||||||
server_funcs = self.servers.keys()
|
server_funcs = self.servers.keys()
|
||||||
try:
|
try:
|
||||||
subtract_only = all((x.startswith('-') for x in back))
|
subtract_only = all((x.startswith('-') for x in back))
|
||||||
|
@ -909,7 +909,7 @@ def _virtual(osdata):
|
|||||||
# Tested on CentOS 5.4 / 2.6.18-164.15.1.el5xen
|
# Tested on CentOS 5.4 / 2.6.18-164.15.1.el5xen
|
||||||
grains['virtual_subtype'] = 'Xen Dom0'
|
grains['virtual_subtype'] = 'Xen Dom0'
|
||||||
else:
|
else:
|
||||||
if grains.get('productname', '') == 'HVM domU':
|
if osdata.get('productname', '') == 'HVM domU':
|
||||||
# Requires dmidecode!
|
# Requires dmidecode!
|
||||||
grains['virtual_subtype'] = 'Xen HVM DomU'
|
grains['virtual_subtype'] = 'Xen HVM DomU'
|
||||||
elif os.path.isfile('/proc/xen/capabilities') and \
|
elif os.path.isfile('/proc/xen/capabilities') and \
|
||||||
@ -926,9 +926,8 @@ def _virtual(osdata):
|
|||||||
elif isdir('/sys/bus/xen'):
|
elif isdir('/sys/bus/xen'):
|
||||||
if 'xen:' in __salt__['cmd.run']('dmesg').lower():
|
if 'xen:' in __salt__['cmd.run']('dmesg').lower():
|
||||||
grains['virtual_subtype'] = 'Xen PV DomU'
|
grains['virtual_subtype'] = 'Xen PV DomU'
|
||||||
elif os.listdir('/sys/bus/xen/drivers'):
|
elif os.path.isfile('/sys/bus/xen/drivers/xenconsole'):
|
||||||
# An actual DomU will have several drivers
|
# An actual DomU will have the xenconsole driver
|
||||||
# whereas a paravirt ops kernel will not.
|
|
||||||
grains['virtual_subtype'] = 'Xen PV DomU'
|
grains['virtual_subtype'] = 'Xen PV DomU'
|
||||||
# If a Dom0 or DomU was detected, obviously this is xen
|
# If a Dom0 or DomU was detected, obviously this is xen
|
||||||
if 'dom' in grains.get('virtual_subtype', '').lower():
|
if 'dom' in grains.get('virtual_subtype', '').lower():
|
||||||
@ -1174,25 +1173,34 @@ def _windows_platform_data():
|
|||||||
os_release = platform.release()
|
os_release = platform.release()
|
||||||
kernel_version = platform.version()
|
kernel_version = platform.version()
|
||||||
info = salt.utils.win_osinfo.get_os_version_info()
|
info = salt.utils.win_osinfo.get_os_version_info()
|
||||||
server = {'Vista': '2008Server',
|
|
||||||
'7': '2008ServerR2',
|
|
||||||
'8': '2012Server',
|
|
||||||
'8.1': '2012ServerR2',
|
|
||||||
'10': '2016Server'}
|
|
||||||
|
|
||||||
# Starting with Python 2.7.12 and 3.5.2 the `platform.uname()` function
|
|
||||||
# started reporting the Desktop version instead of the Server version on
|
|
||||||
# Server versions of Windows, so we need to look those up
|
|
||||||
# So, if you find a Server Platform that's a key in the server
|
|
||||||
# dictionary, then lookup the actual Server Release.
|
|
||||||
# (Product Type 1 is Desktop, Everything else is Server)
|
|
||||||
if info['ProductType'] > 1 and os_release in server:
|
|
||||||
os_release = server[os_release]
|
|
||||||
|
|
||||||
service_pack = None
|
service_pack = None
|
||||||
if info['ServicePackMajor'] > 0:
|
if info['ServicePackMajor'] > 0:
|
||||||
service_pack = ''.join(['SP', six.text_type(info['ServicePackMajor'])])
|
service_pack = ''.join(['SP', six.text_type(info['ServicePackMajor'])])
|
||||||
|
|
||||||
|
# This creates the osrelease grain based on the Windows Operating
|
||||||
|
# System Product Name. As long as Microsoft maintains a similar format
|
||||||
|
# this should be future proof
|
||||||
|
version = 'Unknown'
|
||||||
|
release = ''
|
||||||
|
if 'Server' in osinfo.Caption:
|
||||||
|
for item in osinfo.Caption.split(' '):
|
||||||
|
# If it's all digits, then it's version
|
||||||
|
if re.match(r'\d+', item):
|
||||||
|
version = item
|
||||||
|
# If it starts with R and then numbers, it's the release
|
||||||
|
# ie: R2
|
||||||
|
if re.match(r'^R\d+$', item):
|
||||||
|
release = item
|
||||||
|
os_release = '{0}Server{1}'.format(version, release)
|
||||||
|
else:
|
||||||
|
for item in osinfo.Caption.split(' '):
|
||||||
|
# If it's a number, decimal number, Thin or Vista, then it's the
|
||||||
|
# version
|
||||||
|
if re.match(r'^(\d+(\.\d+)?)|Thin|Vista$', item):
|
||||||
|
version = item
|
||||||
|
os_release = version
|
||||||
|
|
||||||
grains = {
|
grains = {
|
||||||
'kernelrelease': _clean_value('kernelrelease', osinfo.Version),
|
'kernelrelease': _clean_value('kernelrelease', osinfo.Version),
|
||||||
'kernelversion': _clean_value('kernelversion', kernel_version),
|
'kernelversion': _clean_value('kernelversion', kernel_version),
|
||||||
@ -1365,6 +1373,7 @@ _OS_FAMILY_MAP = {
|
|||||||
'GCEL': 'Debian',
|
'GCEL': 'Debian',
|
||||||
'Linaro': 'Debian',
|
'Linaro': 'Debian',
|
||||||
'elementary OS': 'Debian',
|
'elementary OS': 'Debian',
|
||||||
|
'elementary': 'Debian',
|
||||||
'Univention': 'Debian',
|
'Univention': 'Debian',
|
||||||
'ScientificLinux': 'RedHat',
|
'ScientificLinux': 'RedHat',
|
||||||
'Raspbian': 'Debian',
|
'Raspbian': 'Debian',
|
||||||
|
@ -139,6 +139,18 @@ def static_loader(
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
def _format_entrypoint_target(ep):
|
||||||
|
'''
|
||||||
|
Makes a string describing the target of an EntryPoint object.
|
||||||
|
|
||||||
|
Base strongly on EntryPoint.__str__().
|
||||||
|
'''
|
||||||
|
s = ep.module_name
|
||||||
|
if ep.attrs:
|
||||||
|
s += ':' + '.'.join(ep.attrs)
|
||||||
|
return s
|
||||||
|
|
||||||
|
|
||||||
def _module_dirs(
|
def _module_dirs(
|
||||||
opts,
|
opts,
|
||||||
ext_type,
|
ext_type,
|
||||||
@ -161,9 +173,13 @@ def _module_dirs(
|
|||||||
ext_type_types.extend(opts[ext_type_dirs])
|
ext_type_types.extend(opts[ext_type_dirs])
|
||||||
if HAS_PKG_RESOURCES and ext_type_dirs:
|
if HAS_PKG_RESOURCES and ext_type_dirs:
|
||||||
for entry_point in pkg_resources.iter_entry_points('salt.loader', ext_type_dirs):
|
for entry_point in pkg_resources.iter_entry_points('salt.loader', ext_type_dirs):
|
||||||
loaded_entry_point = entry_point.load()
|
try:
|
||||||
for path in loaded_entry_point():
|
loaded_entry_point = entry_point.load()
|
||||||
ext_type_types.append(path)
|
for path in loaded_entry_point():
|
||||||
|
ext_type_types.append(path)
|
||||||
|
except Exception as exc:
|
||||||
|
log.error("Error getting module directories from %s: %s", _format_entrypoint_target(entry_point), exc)
|
||||||
|
log.debug("Full backtrace for module directories error", exc_info=True)
|
||||||
|
|
||||||
cli_module_dirs = []
|
cli_module_dirs = []
|
||||||
# The dirs can be any module dir, or a in-tree _{ext_type} dir
|
# The dirs can be any module dir, or a in-tree _{ext_type} dir
|
||||||
|
@ -250,8 +250,12 @@ class Maintenance(salt.utils.process.SignalHandlingMultiprocessingProcess):
|
|||||||
keys.append(fn_)
|
keys.append(fn_)
|
||||||
log.debug('Writing master key cache')
|
log.debug('Writing master key cache')
|
||||||
# Write a temporary file securely
|
# Write a temporary file securely
|
||||||
with salt.utils.atomicfile.atomic_open(os.path.join(self.opts['pki_dir'], acc, '.key_cache')) as cache_file:
|
if six.PY2:
|
||||||
self.serial.dump(keys, cache_file)
|
with salt.utils.atomicfile.atomic_open(os.path.join(self.opts['pki_dir'], acc, '.key_cache')) as cache_file:
|
||||||
|
self.serial.dump(keys, cache_file)
|
||||||
|
else:
|
||||||
|
with salt.utils.atomicfile.atomic_open(os.path.join(self.opts['pki_dir'], acc, '.key_cache'), mode='wb') as cache_file:
|
||||||
|
self.serial.dump(keys, cache_file)
|
||||||
|
|
||||||
def handle_key_rotate(self, now):
|
def handle_key_rotate(self, now):
|
||||||
'''
|
'''
|
||||||
|
@ -169,7 +169,13 @@ def cert(name,
|
|||||||
res = __salt__['cmd.run_all'](' '.join(cmd))
|
res = __salt__['cmd.run_all'](' '.join(cmd))
|
||||||
|
|
||||||
if res['retcode'] != 0:
|
if res['retcode'] != 0:
|
||||||
return {'result': False, 'comment': 'Certificate {0} renewal failed with:\n{1}'.format(name, res['stderr'])}
|
if 'expand' in res['stderr']:
|
||||||
|
cmd.append('--expand')
|
||||||
|
res = __salt__['cmd.run_all'](' '.join(cmd))
|
||||||
|
if res['retcode'] != 0:
|
||||||
|
return {'result': False, 'comment': 'Certificate {0} renewal failed with:\n{1}'.format(name, res['stderr'])}
|
||||||
|
else:
|
||||||
|
return {'result': False, 'comment': 'Certificate {0} renewal failed with:\n{1}'.format(name, res['stderr'])}
|
||||||
|
|
||||||
if 'no action taken' in res['stdout']:
|
if 'no action taken' in res['stdout']:
|
||||||
comment = 'Certificate {0} unchanged'.format(cert_file)
|
comment = 'Certificate {0} unchanged'.format(cert_file)
|
||||||
|
@ -42,6 +42,8 @@ except ImportError:
|
|||||||
|
|
||||||
# Import salt libs
|
# Import salt libs
|
||||||
import salt.utils.args
|
import salt.utils.args
|
||||||
|
import salt.utils.data
|
||||||
|
import salt.utils.stringutils
|
||||||
from salt.exceptions import SaltInvocationError
|
from salt.exceptions import SaltInvocationError
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
@ -239,6 +241,7 @@ def execute(context=None, lens=None, commands=(), load_path=None):
|
|||||||
'see debug log for details: {0}'.format(arg)
|
'see debug log for details: {0}'.format(arg)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
args = salt.utils.data.decode(args, to_str=True)
|
||||||
log.debug('%s: %s', method, args)
|
log.debug('%s: %s', method, args)
|
||||||
|
|
||||||
func = getattr(aug, method)
|
func = getattr(aug, method)
|
||||||
@ -492,7 +495,7 @@ def ls(path, load_path=None): # pylint: disable=C0103
|
|||||||
def _match(path):
|
def _match(path):
|
||||||
''' Internal match function '''
|
''' Internal match function '''
|
||||||
try:
|
try:
|
||||||
matches = aug.match(path)
|
matches = aug.match(salt.utils.stringutils.to_str(path))
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
@ -393,7 +393,7 @@ def __within(within=None, errmsg=None, dtype=None):
|
|||||||
|
|
||||||
def __space_delimited_list(value):
|
def __space_delimited_list(value):
|
||||||
'''validate that a value contains one or more space-delimited values'''
|
'''validate that a value contains one or more space-delimited values'''
|
||||||
if isinstance(value, str):
|
if isinstance(value, six.string_types):
|
||||||
value = value.strip().split()
|
value = value.strip().split()
|
||||||
|
|
||||||
if hasattr(value, '__iter__') and value != []:
|
if hasattr(value, '__iter__') and value != []:
|
||||||
@ -407,6 +407,7 @@ SALT_ATTR_TO_DEBIAN_ATTR_MAP = {
|
|||||||
'search': 'dns-search',
|
'search': 'dns-search',
|
||||||
'hwaddr': 'hwaddress', # TODO: this limits bootp functionality
|
'hwaddr': 'hwaddress', # TODO: this limits bootp functionality
|
||||||
'ipaddr': 'address',
|
'ipaddr': 'address',
|
||||||
|
'ipaddrs': 'addresses',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -423,6 +424,7 @@ IPV4_ATTR_MAP = {
|
|||||||
'proto': __within(IPV4_VALID_PROTO, dtype=six.text_type),
|
'proto': __within(IPV4_VALID_PROTO, dtype=six.text_type),
|
||||||
# ipv4 static & manual
|
# ipv4 static & manual
|
||||||
'address': __ipv4_quad,
|
'address': __ipv4_quad,
|
||||||
|
'addresses': __anything,
|
||||||
'netmask': __ipv4_netmask,
|
'netmask': __ipv4_netmask,
|
||||||
'broadcast': __ipv4_quad,
|
'broadcast': __ipv4_quad,
|
||||||
'metric': __int,
|
'metric': __int,
|
||||||
@ -473,6 +475,7 @@ IPV6_ATTR_MAP = {
|
|||||||
'proto': __within(IPV6_VALID_PROTO),
|
'proto': __within(IPV6_VALID_PROTO),
|
||||||
# ipv6 static & manual
|
# ipv6 static & manual
|
||||||
'address': __ipv6,
|
'address': __ipv6,
|
||||||
|
'addresses': __anything,
|
||||||
'netmask': __ipv6_netmask,
|
'netmask': __ipv6_netmask,
|
||||||
'broadcast': __ipv6,
|
'broadcast': __ipv6,
|
||||||
'gateway': __ipv6, # supports a colon-delimited list
|
'gateway': __ipv6, # supports a colon-delimited list
|
||||||
@ -626,7 +629,12 @@ def _parse_interfaces(interface_files=None):
|
|||||||
attrname = attr
|
attrname = attr
|
||||||
(valid, value, errmsg) = _validate_interface_option(
|
(valid, value, errmsg) = _validate_interface_option(
|
||||||
attr, valuestr, addrfam)
|
attr, valuestr, addrfam)
|
||||||
iface_dict[attrname] = value
|
if attrname == 'address' and 'address' in iface_dict:
|
||||||
|
if 'addresses' not in iface_dict:
|
||||||
|
iface_dict['addresses'] = []
|
||||||
|
iface_dict['addresses'].append(value)
|
||||||
|
else:
|
||||||
|
iface_dict[attrname] = value
|
||||||
|
|
||||||
elif attr in _REV_ETHTOOL_CONFIG_OPTS:
|
elif attr in _REV_ETHTOOL_CONFIG_OPTS:
|
||||||
if 'ethtool' not in iface_dict:
|
if 'ethtool' not in iface_dict:
|
||||||
|
@ -939,6 +939,9 @@ def compare_containers(first, second, ignore=None):
|
|||||||
if item == 'Ulimits':
|
if item == 'Ulimits':
|
||||||
val1 = _ulimit_sort(val1)
|
val1 = _ulimit_sort(val1)
|
||||||
val2 = _ulimit_sort(val2)
|
val2 = _ulimit_sort(val2)
|
||||||
|
if item == 'Env':
|
||||||
|
val1 = sorted(val1)
|
||||||
|
val2 = sorted(val2)
|
||||||
if val1 != val2:
|
if val1 != val2:
|
||||||
ret.setdefault(conf_dict, {})[item] = {'old': val1, 'new': val2}
|
ret.setdefault(conf_dict, {})[item] = {'old': val1, 'new': val2}
|
||||||
# Check for optionally-present items that were in the second container
|
# Check for optionally-present items that were in the second container
|
||||||
@ -965,6 +968,9 @@ def compare_containers(first, second, ignore=None):
|
|||||||
if item == 'Ulimits':
|
if item == 'Ulimits':
|
||||||
val1 = _ulimit_sort(val1)
|
val1 = _ulimit_sort(val1)
|
||||||
val2 = _ulimit_sort(val2)
|
val2 = _ulimit_sort(val2)
|
||||||
|
if item == 'Env':
|
||||||
|
val1 = sorted(val1)
|
||||||
|
val2 = sorted(val2)
|
||||||
if val1 != val2:
|
if val1 != val2:
|
||||||
ret.setdefault(conf_dict, {})[item] = {'old': val1, 'new': val2}
|
ret.setdefault(conf_dict, {})[item] = {'old': val1, 'new': val2}
|
||||||
return ret
|
return ret
|
||||||
@ -5617,6 +5623,7 @@ def pause(name):
|
|||||||
.format(name))}
|
.format(name))}
|
||||||
return _change_state(name, 'pause', 'paused')
|
return _change_state(name, 'pause', 'paused')
|
||||||
|
|
||||||
|
|
||||||
freeze = salt.utils.functools.alias_function(pause, 'freeze')
|
freeze = salt.utils.functools.alias_function(pause, 'freeze')
|
||||||
|
|
||||||
|
|
||||||
@ -5819,6 +5826,7 @@ def unpause(name):
|
|||||||
.format(name))}
|
.format(name))}
|
||||||
return _change_state(name, 'unpause', 'running')
|
return _change_state(name, 'unpause', 'running')
|
||||||
|
|
||||||
|
|
||||||
unfreeze = salt.utils.functools.alias_function(unpause, 'unfreeze')
|
unfreeze = salt.utils.functools.alias_function(unpause, 'unfreeze')
|
||||||
|
|
||||||
|
|
||||||
|
@ -793,6 +793,7 @@ def get_source_sum(file_name='',
|
|||||||
ret = extract_hash(hash_fn, '', file_name, source, source_hash_name)
|
ret = extract_hash(hash_fn, '', file_name, source, source_hash_name)
|
||||||
if ret is None:
|
if ret is None:
|
||||||
_invalid_source_hash_format()
|
_invalid_source_hash_format()
|
||||||
|
ret['hsum'] = ret['hsum'].lower()
|
||||||
return ret
|
return ret
|
||||||
else:
|
else:
|
||||||
# The source_hash is a hash expression
|
# The source_hash is a hash expression
|
||||||
@ -836,6 +837,7 @@ def get_source_sum(file_name='',
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ret['hsum'] = ret['hsum'].lower()
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
@ -1694,6 +1696,8 @@ def _starts_till(src, probe, strip_comments=True):
|
|||||||
if not src or not probe:
|
if not src or not probe:
|
||||||
return no_match
|
return no_match
|
||||||
|
|
||||||
|
src = src.rstrip('\n\r')
|
||||||
|
probe = probe.rstrip('\n\r')
|
||||||
if src == probe:
|
if src == probe:
|
||||||
return equal
|
return equal
|
||||||
|
|
||||||
@ -2275,6 +2279,8 @@ def replace(path,
|
|||||||
# Just search; bail as early as a match is found
|
# Just search; bail as early as a match is found
|
||||||
if re.search(cpattern, r_data):
|
if re.search(cpattern, r_data):
|
||||||
return True # `with` block handles file closure
|
return True # `with` block handles file closure
|
||||||
|
else:
|
||||||
|
return False
|
||||||
else:
|
else:
|
||||||
result, nrepl = re.subn(cpattern,
|
result, nrepl = re.subn(cpattern,
|
||||||
repl.replace('\\', '\\\\') if backslash_literal else repl,
|
repl.replace('\\', '\\\\') if backslash_literal else repl,
|
||||||
@ -6001,6 +6007,7 @@ def list_backups(path, limit=None):
|
|||||||
[files[x] for x in sorted(files, reverse=True)[:limit]]
|
[files[x] for x in sorted(files, reverse=True)[:limit]]
|
||||||
)))
|
)))
|
||||||
|
|
||||||
|
|
||||||
list_backup = salt.utils.functools.alias_function(list_backups, 'list_backup')
|
list_backup = salt.utils.functools.alias_function(list_backups, 'list_backup')
|
||||||
|
|
||||||
|
|
||||||
@ -6173,6 +6180,7 @@ def delete_backup(path, backup_id):
|
|||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
remove_backup = salt.utils.functools.alias_function(delete_backup, 'remove_backup')
|
remove_backup = salt.utils.functools.alias_function(delete_backup, 'remove_backup')
|
||||||
|
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ Manage the information in the hosts file
|
|||||||
|
|
||||||
# Import Python libs
|
# Import Python libs
|
||||||
from __future__ import absolute_import, print_function, unicode_literals
|
from __future__ import absolute_import, print_function, unicode_literals
|
||||||
|
import errno
|
||||||
import os
|
import os
|
||||||
|
|
||||||
# Import salt libs
|
# Import salt libs
|
||||||
@ -22,7 +23,12 @@ def __get_hosts_filename():
|
|||||||
'''
|
'''
|
||||||
Return the path to the appropriate hosts file
|
Return the path to the appropriate hosts file
|
||||||
'''
|
'''
|
||||||
return __salt__['config.option']('hosts.file')
|
try:
|
||||||
|
return __context__['hosts.__get_hosts_filename']
|
||||||
|
except KeyError:
|
||||||
|
__context__['hosts.__get_hosts_filename'] = \
|
||||||
|
__salt__['config.option']('hosts.file')
|
||||||
|
return __context__['hosts.__get_hosts_filename']
|
||||||
|
|
||||||
|
|
||||||
def _get_or_create_hostfile():
|
def _get_or_create_hostfile():
|
||||||
@ -43,26 +49,35 @@ def _list_hosts():
|
|||||||
'''
|
'''
|
||||||
Return the hosts found in the hosts file in as an OrderedDict
|
Return the hosts found in the hosts file in as an OrderedDict
|
||||||
'''
|
'''
|
||||||
count = 0
|
try:
|
||||||
hfn = __get_hosts_filename()
|
return __context__['hosts._list_hosts']
|
||||||
ret = odict.OrderedDict()
|
except KeyError:
|
||||||
if not os.path.isfile(hfn):
|
count = 0
|
||||||
|
hfn = __get_hosts_filename()
|
||||||
|
ret = odict.OrderedDict()
|
||||||
|
try:
|
||||||
|
with salt.utils.files.fopen(hfn) as ifile:
|
||||||
|
for line in ifile:
|
||||||
|
line = salt.utils.stringutils.to_unicode(line).strip()
|
||||||
|
if not line:
|
||||||
|
continue
|
||||||
|
if line.startswith('#'):
|
||||||
|
ret.setdefault('comment-{0}'.format(count), []).append(line)
|
||||||
|
count += 1
|
||||||
|
continue
|
||||||
|
if '#' in line:
|
||||||
|
line = line[:line.index('#')].strip()
|
||||||
|
comps = line.split()
|
||||||
|
ip = comps.pop(0)
|
||||||
|
ret.setdefault(ip, []).extend(comps)
|
||||||
|
except (IOError, OSError) as exc:
|
||||||
|
salt.utils.files.process_read_exception(exc, hfn, ignore=errno.ENOENT)
|
||||||
|
# Don't set __context__ since we weren't able to read from the
|
||||||
|
# hosts file.
|
||||||
|
return ret
|
||||||
|
|
||||||
|
__context__['hosts._list_hosts'] = ret
|
||||||
return ret
|
return ret
|
||||||
with salt.utils.files.fopen(hfn) as ifile:
|
|
||||||
for line in ifile:
|
|
||||||
line = salt.utils.stringutils.to_unicode(line).strip()
|
|
||||||
if not line:
|
|
||||||
continue
|
|
||||||
if line.startswith('#'):
|
|
||||||
ret.setdefault('comment-{0}'.format(count), []).append(line)
|
|
||||||
count += 1
|
|
||||||
continue
|
|
||||||
if '#' in line:
|
|
||||||
line = line[:line.index('#')].strip()
|
|
||||||
comps = line.split()
|
|
||||||
ip = comps.pop(0)
|
|
||||||
ret.setdefault(ip, []).extend(comps)
|
|
||||||
return ret
|
|
||||||
|
|
||||||
|
|
||||||
def list_hosts():
|
def list_hosts():
|
||||||
@ -133,7 +148,10 @@ def has_pair(ip, alias):
|
|||||||
salt '*' hosts.has_pair <ip> <alias>
|
salt '*' hosts.has_pair <ip> <alias>
|
||||||
'''
|
'''
|
||||||
hosts = _list_hosts()
|
hosts = _list_hosts()
|
||||||
return ip in hosts and alias in hosts[ip]
|
try:
|
||||||
|
return alias in hosts[ip]
|
||||||
|
except KeyError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def set_host(ip, alias):
|
def set_host(ip, alias):
|
||||||
@ -157,6 +175,9 @@ def set_host(ip, alias):
|
|||||||
if not os.path.isfile(hfn):
|
if not os.path.isfile(hfn):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# Make sure future calls to _list_hosts() will re-read the file
|
||||||
|
__context__.pop('hosts._list_hosts', None)
|
||||||
|
|
||||||
line_to_add = salt.utils.stringutils.to_bytes(
|
line_to_add = salt.utils.stringutils.to_bytes(
|
||||||
ip + '\t\t' + alias + os.linesep
|
ip + '\t\t' + alias + os.linesep
|
||||||
)
|
)
|
||||||
@ -203,6 +224,8 @@ def rm_host(ip, alias):
|
|||||||
'''
|
'''
|
||||||
if not has_pair(ip, alias):
|
if not has_pair(ip, alias):
|
||||||
return True
|
return True
|
||||||
|
# Make sure future calls to _list_hosts() will re-read the file
|
||||||
|
__context__.pop('hosts._list_hosts', None)
|
||||||
hfn = _get_or_create_hostfile()
|
hfn = _get_or_create_hostfile()
|
||||||
with salt.utils.files.fopen(hfn, 'rb') as fp_:
|
with salt.utils.files.fopen(hfn, 'rb') as fp_:
|
||||||
lines = fp_.readlines()
|
lines = fp_.readlines()
|
||||||
@ -251,6 +274,10 @@ def add_host(ip, alias):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
hosts = _list_hosts()
|
hosts = _list_hosts()
|
||||||
|
|
||||||
|
# Make sure future calls to _list_hosts() will re-read the file
|
||||||
|
__context__.pop('hosts._list_hosts', None)
|
||||||
|
|
||||||
inserted = False
|
inserted = False
|
||||||
for i, h in six.iteritems(hosts):
|
for i, h in six.iteritems(hosts):
|
||||||
for j in range(len(h)):
|
for j in range(len(h)):
|
||||||
|
@ -10,11 +10,11 @@ Module to provide icinga2 compatibility to salt.
|
|||||||
# Import python libs
|
# Import python libs
|
||||||
from __future__ import absolute_import, print_function, unicode_literals
|
from __future__ import absolute_import, print_function, unicode_literals
|
||||||
import logging
|
import logging
|
||||||
import subprocess
|
|
||||||
|
|
||||||
# Import Salt libs
|
# Import Salt libs
|
||||||
import salt.utils.path
|
import salt.utils.path
|
||||||
import salt.utils.platform
|
import salt.utils.platform
|
||||||
|
from salt.utils.icinga2 import get_certs_path
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -32,18 +32,6 @@ def __virtual__():
|
|||||||
return (False, 'Icinga2 not installed.')
|
return (False, 'Icinga2 not installed.')
|
||||||
|
|
||||||
|
|
||||||
def _execute(cmd, ret_code=False):
|
|
||||||
process = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
|
||||||
if ret_code:
|
|
||||||
return process.wait()
|
|
||||||
output, error = process.communicate()
|
|
||||||
if output:
|
|
||||||
log.debug(output)
|
|
||||||
return output
|
|
||||||
log.debug(error)
|
|
||||||
return error
|
|
||||||
|
|
||||||
|
|
||||||
def generate_ticket(domain):
|
def generate_ticket(domain):
|
||||||
'''
|
'''
|
||||||
Generate and save an icinga2 ticket.
|
Generate and save an icinga2 ticket.
|
||||||
@ -58,7 +46,7 @@ def generate_ticket(domain):
|
|||||||
salt '*' icinga2.generate_ticket domain.tld
|
salt '*' icinga2.generate_ticket domain.tld
|
||||||
|
|
||||||
'''
|
'''
|
||||||
result = _execute(["icinga2", "pki", "ticket", "--cn", domain])
|
result = __salt__['cmd.run_all'](["icinga2", "pki", "ticket", "--cn", domain], python_shell=False)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
@ -76,7 +64,7 @@ def generate_cert(domain):
|
|||||||
salt '*' icinga2.generate_cert domain.tld
|
salt '*' icinga2.generate_cert domain.tld
|
||||||
|
|
||||||
'''
|
'''
|
||||||
result = _execute(["icinga2", "pki", "new-cert", "--cn", domain, "--key", "/etc/icinga2/pki/{0}.key".format(domain), "--cert", "/etc/icinga2/pki/{0}.crt".format(domain)], ret_code=True)
|
result = __salt__['cmd.run_all'](["icinga2", "pki", "new-cert", "--cn", domain, "--key", "{0}{1}.key".format(get_certs_path(), domain), "--cert", "{0}{1}.crt".format(get_certs_path(), domain)], python_shell=False)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
@ -94,8 +82,8 @@ def save_cert(domain, master):
|
|||||||
salt '*' icinga2.save_cert domain.tld master.domain.tld
|
salt '*' icinga2.save_cert domain.tld master.domain.tld
|
||||||
|
|
||||||
'''
|
'''
|
||||||
result = _execute(["icinga2", "pki", "save-cert", "--key", "/etc/icinga2/pki/{0}.key".format(domain), "--cert", "/etc/icinga2/pki/{0}.cert".format(domain), "--trustedcert",
|
result = __salt__['cmd.run_all'](["icinga2", "pki", "save-cert", "--key", "{0}{1}.key".format(get_certs_path(), domain), "--cert", "{0}{1}.cert".format(get_certs_path(), domain), "--trustedcert",
|
||||||
"/etc/icinga2/pki/trusted-master.crt", "--host", master], ret_code=True)
|
"{0}trusted-master.crt".format(get_certs_path()), "--host", master], python_shell=False)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
@ -114,8 +102,8 @@ def request_cert(domain, master, ticket, port):
|
|||||||
salt '*' icinga2.request_cert domain.tld master.domain.tld TICKET_ID
|
salt '*' icinga2.request_cert domain.tld master.domain.tld TICKET_ID
|
||||||
|
|
||||||
'''
|
'''
|
||||||
result = _execute(["icinga2", "pki", "request", "--host", master, "--port", port, "--ticket", ticket, "--key", "/etc/icinga2/pki/{0}.key".format(domain), "--cert",
|
result = __salt__['cmd.run_all'](["icinga2", "pki", "request", "--host", master, "--port", port, "--ticket", ticket, "--key", "{0}{1}.key".format(get_certs_path(), domain), "--cert",
|
||||||
"/etc/icinga2/pki/{0}.crt".format(domain), "--trustedcert", "/etc/icinga2/pki/trusted-master.crt", "--ca", "/etc/icinga2/pki/ca.crt"], ret_code=True)
|
"{0}{1}.crt".format(get_certs_path(), domain), "--trustedcert", "{0}trusted-master.crt".format(get_certs_path()), "--ca", "{0}ca.crt".format(get_certs_path())], python_shell=False)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
@ -134,6 +122,6 @@ def node_setup(domain, master, ticket):
|
|||||||
salt '*' icinga2.node_setup domain.tld master.domain.tld TICKET_ID
|
salt '*' icinga2.node_setup domain.tld master.domain.tld TICKET_ID
|
||||||
|
|
||||||
'''
|
'''
|
||||||
result = _execute(["icinga2", "node", "setup", "--ticket", ticket, "--endpoint", master, "--zone", domain, "--master_host", master, "--trustedcert", "/etc/icinga2/pki/trusted-master.crt"],
|
result = __salt__['cmd.run_all'](["icinga2", "node", "setup", "--ticket", ticket, "--endpoint", master, "--zone", domain, "--master_host", master, "--trustedcert", "{0}trusted-master.crt".format(get_certs_path())],
|
||||||
ret_code=True)
|
python_shell=False)
|
||||||
return result
|
return result
|
||||||
|
@ -332,7 +332,7 @@ def _connect(**kwargs):
|
|||||||
try:
|
try:
|
||||||
dbc = MySQLdb.connect(**connargs)
|
dbc = MySQLdb.connect(**connargs)
|
||||||
except MySQLdb.OperationalError as exc:
|
except MySQLdb.OperationalError as exc:
|
||||||
err = 'MySQL Error {0}: {1}'.format(*exc)
|
err = 'MySQL Error {0}: {1}'.format(*exc.args)
|
||||||
__context__['mysql.error'] = err
|
__context__['mysql.error'] = err
|
||||||
log.error(err)
|
log.error(err)
|
||||||
return None
|
return None
|
||||||
@ -648,7 +648,7 @@ def query(database, query, **connection_args):
|
|||||||
try:
|
try:
|
||||||
affected = _execute(cur, query)
|
affected = _execute(cur, query)
|
||||||
except MySQLdb.OperationalError as exc:
|
except MySQLdb.OperationalError as exc:
|
||||||
err = 'MySQL Error {0}: {1}'.format(*exc)
|
err = 'MySQL Error {0}: {1}'.format(*exc.args)
|
||||||
__context__['mysql.error'] = err
|
__context__['mysql.error'] = err
|
||||||
log.error(err)
|
log.error(err)
|
||||||
return False
|
return False
|
||||||
@ -773,7 +773,7 @@ def status(**connection_args):
|
|||||||
try:
|
try:
|
||||||
_execute(cur, qry)
|
_execute(cur, qry)
|
||||||
except MySQLdb.OperationalError as exc:
|
except MySQLdb.OperationalError as exc:
|
||||||
err = 'MySQL Error {0}: {1}'.format(*exc)
|
err = 'MySQL Error {0}: {1}'.format(*exc.args)
|
||||||
__context__['mysql.error'] = err
|
__context__['mysql.error'] = err
|
||||||
log.error(err)
|
log.error(err)
|
||||||
return {}
|
return {}
|
||||||
@ -804,7 +804,7 @@ def version(**connection_args):
|
|||||||
try:
|
try:
|
||||||
_execute(cur, qry)
|
_execute(cur, qry)
|
||||||
except MySQLdb.OperationalError as exc:
|
except MySQLdb.OperationalError as exc:
|
||||||
err = 'MySQL Error {0}: {1}'.format(*exc)
|
err = 'MySQL Error {0}: {1}'.format(*exc.args)
|
||||||
__context__['mysql.error'] = err
|
__context__['mysql.error'] = err
|
||||||
log.error(err)
|
log.error(err)
|
||||||
return ''
|
return ''
|
||||||
@ -837,7 +837,7 @@ def slave_lag(**connection_args):
|
|||||||
try:
|
try:
|
||||||
_execute(cur, qry)
|
_execute(cur, qry)
|
||||||
except MySQLdb.OperationalError as exc:
|
except MySQLdb.OperationalError as exc:
|
||||||
err = 'MySQL Error {0}: {1}'.format(*exc)
|
err = 'MySQL Error {0}: {1}'.format(*exc.args)
|
||||||
__context__['mysql.error'] = err
|
__context__['mysql.error'] = err
|
||||||
log.error(err)
|
log.error(err)
|
||||||
return -3
|
return -3
|
||||||
@ -922,7 +922,7 @@ def db_list(**connection_args):
|
|||||||
try:
|
try:
|
||||||
_execute(cur, qry)
|
_execute(cur, qry)
|
||||||
except MySQLdb.OperationalError as exc:
|
except MySQLdb.OperationalError as exc:
|
||||||
err = 'MySQL Error {0}: {1}'.format(*exc)
|
err = 'MySQL Error {0}: {1}'.format(*exc.args)
|
||||||
__context__['mysql.error'] = err
|
__context__['mysql.error'] = err
|
||||||
log.error(err)
|
log.error(err)
|
||||||
return []
|
return []
|
||||||
@ -1011,7 +1011,7 @@ def db_tables(name, **connection_args):
|
|||||||
try:
|
try:
|
||||||
_execute(cur, qry)
|
_execute(cur, qry)
|
||||||
except MySQLdb.OperationalError as exc:
|
except MySQLdb.OperationalError as exc:
|
||||||
err = 'MySQL Error {0}: {1}'.format(*exc)
|
err = 'MySQL Error {0}: {1}'.format(*exc.args)
|
||||||
__context__['mysql.error'] = err
|
__context__['mysql.error'] = err
|
||||||
log.error(err)
|
log.error(err)
|
||||||
return []
|
return []
|
||||||
@ -1046,7 +1046,7 @@ def db_exists(name, **connection_args):
|
|||||||
try:
|
try:
|
||||||
_execute(cur, qry, args)
|
_execute(cur, qry, args)
|
||||||
except MySQLdb.OperationalError as exc:
|
except MySQLdb.OperationalError as exc:
|
||||||
err = 'MySQL Error {0}: {1}'.format(*exc)
|
err = 'MySQL Error {0}: {1}'.format(*exc.args)
|
||||||
__context__['mysql.error'] = err
|
__context__['mysql.error'] = err
|
||||||
log.error(err)
|
log.error(err)
|
||||||
return False
|
return False
|
||||||
@ -1101,7 +1101,7 @@ def db_create(name, character_set=None, collate=None, **connection_args):
|
|||||||
log.info('DB \'%s\' created', name)
|
log.info('DB \'%s\' created', name)
|
||||||
return True
|
return True
|
||||||
except MySQLdb.OperationalError as exc:
|
except MySQLdb.OperationalError as exc:
|
||||||
err = 'MySQL Error {0}: {1}'.format(*exc)
|
err = 'MySQL Error {0}: {1}'.format(*exc.args)
|
||||||
__context__['mysql.error'] = err
|
__context__['mysql.error'] = err
|
||||||
log.error(err)
|
log.error(err)
|
||||||
return False
|
return False
|
||||||
@ -1137,7 +1137,7 @@ def db_remove(name, **connection_args):
|
|||||||
try:
|
try:
|
||||||
_execute(cur, qry)
|
_execute(cur, qry)
|
||||||
except MySQLdb.OperationalError as exc:
|
except MySQLdb.OperationalError as exc:
|
||||||
err = 'MySQL Error {0}: {1}'.format(*exc)
|
err = 'MySQL Error {0}: {1}'.format(*exc.args)
|
||||||
__context__['mysql.error'] = err
|
__context__['mysql.error'] = err
|
||||||
log.error(err)
|
log.error(err)
|
||||||
return False
|
return False
|
||||||
@ -1169,7 +1169,7 @@ def user_list(**connection_args):
|
|||||||
qry = 'SELECT User,Host FROM mysql.user'
|
qry = 'SELECT User,Host FROM mysql.user'
|
||||||
_execute(cur, qry)
|
_execute(cur, qry)
|
||||||
except MySQLdb.OperationalError as exc:
|
except MySQLdb.OperationalError as exc:
|
||||||
err = 'MySQL Error {0}: {1}'.format(*exc)
|
err = 'MySQL Error {0}: {1}'.format(*exc.args)
|
||||||
__context__['mysql.error'] = err
|
__context__['mysql.error'] = err
|
||||||
log.error(err)
|
log.error(err)
|
||||||
return []
|
return []
|
||||||
@ -1247,12 +1247,12 @@ def user_exists(user,
|
|||||||
args['password'] = password_hash
|
args['password'] = password_hash
|
||||||
|
|
||||||
if run_verify:
|
if run_verify:
|
||||||
if not verify_login(user, host, password):
|
if not verify_login(user, password, **connection_args):
|
||||||
return False
|
return False
|
||||||
try:
|
try:
|
||||||
_execute(cur, qry, args)
|
_execute(cur, qry, args)
|
||||||
except MySQLdb.OperationalError as exc:
|
except MySQLdb.OperationalError as exc:
|
||||||
err = 'MySQL Error {0}: {1}'.format(*exc)
|
err = 'MySQL Error {0}: {1}'.format(*exc.args)
|
||||||
__context__['mysql.error'] = err
|
__context__['mysql.error'] = err
|
||||||
log.error(err)
|
log.error(err)
|
||||||
return False
|
return False
|
||||||
@ -1284,7 +1284,7 @@ def user_info(user, host='localhost', **connection_args):
|
|||||||
try:
|
try:
|
||||||
_execute(cur, qry, args)
|
_execute(cur, qry, args)
|
||||||
except MySQLdb.OperationalError as exc:
|
except MySQLdb.OperationalError as exc:
|
||||||
err = 'MySQL Error {0}: {1}'.format(*exc)
|
err = 'MySQL Error {0}: {1}'.format(*exc.args)
|
||||||
__context__['mysql.error'] = err
|
__context__['mysql.error'] = err
|
||||||
log.error(err)
|
log.error(err)
|
||||||
return False
|
return False
|
||||||
@ -1384,7 +1384,7 @@ def user_create(user,
|
|||||||
try:
|
try:
|
||||||
_execute(cur, qry, args)
|
_execute(cur, qry, args)
|
||||||
except MySQLdb.OperationalError as exc:
|
except MySQLdb.OperationalError as exc:
|
||||||
err = 'MySQL Error {0}: {1}'.format(*exc)
|
err = 'MySQL Error {0}: {1}'.format(*exc.args)
|
||||||
__context__['mysql.error'] = err
|
__context__['mysql.error'] = err
|
||||||
log.error(err)
|
log.error(err)
|
||||||
return False
|
return False
|
||||||
@ -1499,7 +1499,7 @@ def user_chpass(user,
|
|||||||
try:
|
try:
|
||||||
result = _execute(cur, qry, args)
|
result = _execute(cur, qry, args)
|
||||||
except MySQLdb.OperationalError as exc:
|
except MySQLdb.OperationalError as exc:
|
||||||
err = 'MySQL Error {0}: {1}'.format(*exc)
|
err = 'MySQL Error {0}: {1}'.format(*exc.args)
|
||||||
__context__['mysql.error'] = err
|
__context__['mysql.error'] = err
|
||||||
log.error(err)
|
log.error(err)
|
||||||
return False
|
return False
|
||||||
@ -1554,7 +1554,7 @@ def user_remove(user,
|
|||||||
try:
|
try:
|
||||||
_execute(cur, qry, args)
|
_execute(cur, qry, args)
|
||||||
except MySQLdb.OperationalError as exc:
|
except MySQLdb.OperationalError as exc:
|
||||||
err = 'MySQL Error {0}: {1}'.format(*exc)
|
err = 'MySQL Error {0}: {1}'.format(*exc.args)
|
||||||
__context__['mysql.error'] = err
|
__context__['mysql.error'] = err
|
||||||
log.error(err)
|
log.error(err)
|
||||||
return False
|
return False
|
||||||
@ -1772,13 +1772,13 @@ def user_grants(user,
|
|||||||
try:
|
try:
|
||||||
_execute(cur, qry, args)
|
_execute(cur, qry, args)
|
||||||
except MySQLdb.OperationalError as exc:
|
except MySQLdb.OperationalError as exc:
|
||||||
err = 'MySQL Error {0}: {1}'.format(*exc)
|
err = 'MySQL Error {0}: {1}'.format(*exc.args)
|
||||||
__context__['mysql.error'] = err
|
__context__['mysql.error'] = err
|
||||||
log.error(err)
|
log.error(err)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
ret = []
|
ret = []
|
||||||
results = cur.fetchall()
|
results = salt.utils.data.decode(cur.fetchall())
|
||||||
for grant in results:
|
for grant in results:
|
||||||
tmp = grant[0].split(' IDENTIFIED BY')[0]
|
tmp = grant[0].split(' IDENTIFIED BY')[0]
|
||||||
if 'WITH GRANT OPTION' in grant[0] and 'WITH GRANT OPTION' not in tmp:
|
if 'WITH GRANT OPTION' in grant[0] and 'WITH GRANT OPTION' not in tmp:
|
||||||
@ -1886,7 +1886,7 @@ def grant_add(grant,
|
|||||||
try:
|
try:
|
||||||
_execute(cur, qry['qry'], qry['args'])
|
_execute(cur, qry['qry'], qry['args'])
|
||||||
except (MySQLdb.OperationalError, MySQLdb.ProgrammingError) as exc:
|
except (MySQLdb.OperationalError, MySQLdb.ProgrammingError) as exc:
|
||||||
err = 'MySQL Error {0}: {1}'.format(*exc)
|
err = 'MySQL Error {0}: {1}'.format(*exc.args)
|
||||||
__context__['mysql.error'] = err
|
__context__['mysql.error'] = err
|
||||||
log.error(err)
|
log.error(err)
|
||||||
return False
|
return False
|
||||||
@ -1960,7 +1960,7 @@ def grant_revoke(grant,
|
|||||||
try:
|
try:
|
||||||
_execute(cur, qry, args)
|
_execute(cur, qry, args)
|
||||||
except MySQLdb.OperationalError as exc:
|
except MySQLdb.OperationalError as exc:
|
||||||
err = 'MySQL Error {0}: {1}'.format(*exc)
|
err = 'MySQL Error {0}: {1}'.format(*exc.args)
|
||||||
__context__['mysql.error'] = err
|
__context__['mysql.error'] = err
|
||||||
log.error(err)
|
log.error(err)
|
||||||
return False
|
return False
|
||||||
@ -2235,7 +2235,7 @@ def showglobal(**connection_args):
|
|||||||
return rtnv
|
return rtnv
|
||||||
|
|
||||||
|
|
||||||
def verify_login(user, host='localhost', password=None, **connection_args):
|
def verify_login(user, password=None, **connection_args):
|
||||||
'''
|
'''
|
||||||
Attempt to login using the provided credentials.
|
Attempt to login using the provided credentials.
|
||||||
If successful, return true. Otherwise, return False.
|
If successful, return true. Otherwise, return False.
|
||||||
@ -2244,11 +2244,10 @@ def verify_login(user, host='localhost', password=None, **connection_args):
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
salt '*' mysql.verify_login root localhost password
|
salt '*' mysql.verify_login root password
|
||||||
'''
|
'''
|
||||||
# Override the connection args
|
# Override the connection args for username and password
|
||||||
connection_args['connection_user'] = user
|
connection_args['connection_user'] = user
|
||||||
connection_args['connection_host'] = host
|
|
||||||
connection_args['connection_pass'] = password
|
connection_args['connection_pass'] = password
|
||||||
|
|
||||||
dbc = _connect(**connection_args)
|
dbc = _connect(**connection_args)
|
||||||
|
@ -14,6 +14,7 @@ import datetime
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
# Import salt libs
|
# Import salt libs
|
||||||
|
import salt.utils.data
|
||||||
from salt.exceptions import SaltInvocationError, CommandExecutionError
|
from salt.exceptions import SaltInvocationError, CommandExecutionError
|
||||||
|
|
||||||
# Import third party libs
|
# Import third party libs
|
||||||
@ -53,9 +54,9 @@ def _get_proc_cmdline(proc):
|
|||||||
It's backward compatible with < 2.0 versions of psutil.
|
It's backward compatible with < 2.0 versions of psutil.
|
||||||
'''
|
'''
|
||||||
try:
|
try:
|
||||||
return proc.cmdline() if PSUTIL2 else proc.cmdline
|
return salt.utils.data.decode(proc.cmdline() if PSUTIL2 else proc.cmdline)
|
||||||
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
||||||
return ''
|
return []
|
||||||
|
|
||||||
|
|
||||||
def _get_proc_create_time(proc):
|
def _get_proc_create_time(proc):
|
||||||
@ -65,7 +66,7 @@ def _get_proc_create_time(proc):
|
|||||||
It's backward compatible with < 2.0 versions of psutil.
|
It's backward compatible with < 2.0 versions of psutil.
|
||||||
'''
|
'''
|
||||||
try:
|
try:
|
||||||
return proc.create_time() if PSUTIL2 else proc.create_time
|
return salt.utils.data.decode(proc.create_time() if PSUTIL2 else proc.create_time)
|
||||||
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -77,7 +78,7 @@ def _get_proc_name(proc):
|
|||||||
It's backward compatible with < 2.0 versions of psutil.
|
It's backward compatible with < 2.0 versions of psutil.
|
||||||
'''
|
'''
|
||||||
try:
|
try:
|
||||||
return proc.name() if PSUTIL2 else proc.name
|
return salt.utils.data.decode(proc.name() if PSUTIL2 else proc.name)
|
||||||
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
@ -89,7 +90,7 @@ def _get_proc_status(proc):
|
|||||||
It's backward compatible with < 2.0 versions of psutil.
|
It's backward compatible with < 2.0 versions of psutil.
|
||||||
'''
|
'''
|
||||||
try:
|
try:
|
||||||
return proc.status() if PSUTIL2 else proc.status
|
return salt.utils.data.decode(proc.status() if PSUTIL2 else proc.status)
|
||||||
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -101,7 +102,7 @@ def _get_proc_username(proc):
|
|||||||
It's backward compatible with < 2.0 versions of psutil.
|
It's backward compatible with < 2.0 versions of psutil.
|
||||||
'''
|
'''
|
||||||
try:
|
try:
|
||||||
return proc.username() if PSUTIL2 else proc.username
|
return salt.utils.data.decode(proc.username() if PSUTIL2 else proc.username)
|
||||||
except (psutil.NoSuchProcess, psutil.AccessDenied, KeyError):
|
except (psutil.NoSuchProcess, psutil.AccessDenied, KeyError):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -637,7 +637,7 @@ def apply_(mods=None, **kwargs):
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
salt '*' state.apply test pillar='{"foo": "bar"}'
|
salt '*' state.apply stuff pillar='{"foo": "bar"}'
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
Values passed this way will override Pillar values set via
|
Values passed this way will override Pillar values set via
|
||||||
@ -680,11 +680,11 @@ def apply_(mods=None, **kwargs):
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
# Run the states configured in salt://test.sls (or salt://test/init.sls)
|
# Run the states configured in salt://stuff.sls (or salt://stuff/init.sls)
|
||||||
salt '*' state.apply test
|
salt '*' state.apply stuff
|
||||||
# Run the states configured in salt://test.sls (or salt://test/init.sls)
|
# Run the states configured in salt://stuff.sls (or salt://stuff/init.sls)
|
||||||
# and salt://pkgs.sls (or salt://pkgs/init.sls).
|
# and salt://pkgs.sls (or salt://pkgs/init.sls).
|
||||||
salt '*' state.apply test,pkgs
|
salt '*' state.apply stuff,pkgs
|
||||||
|
|
||||||
The following additional arguments are also accepted when applying
|
The following additional arguments are also accepted when applying
|
||||||
individual SLS files:
|
individual SLS files:
|
||||||
@ -704,7 +704,7 @@ def apply_(mods=None, **kwargs):
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
salt '*' state.apply test pillar='{"foo": "bar"}'
|
salt '*' state.apply stuff pillar='{"foo": "bar"}'
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
Values passed this way will override Pillar values set via
|
Values passed this way will override Pillar values set via
|
||||||
@ -755,7 +755,7 @@ def apply_(mods=None, **kwargs):
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
salt '*' state.apply test localconfig=/path/to/minion.yml
|
salt '*' state.apply stuff localconfig=/path/to/minion.yml
|
||||||
|
|
||||||
sync_mods
|
sync_mods
|
||||||
If specified, the desired custom module types will be synced prior to
|
If specified, the desired custom module types will be synced prior to
|
||||||
@ -763,8 +763,8 @@ def apply_(mods=None, **kwargs):
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
salt '*' state.apply test sync_mods=states,modules
|
salt '*' state.apply stuff sync_mods=states,modules
|
||||||
salt '*' state.apply test sync_mods=all
|
salt '*' state.apply stuff sync_mods=all
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
This option is ignored when no SLS files are specified, as a
|
This option is ignored when no SLS files are specified, as a
|
||||||
@ -792,8 +792,8 @@ def request(mods=None,
|
|||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
salt '*' state.request
|
salt '*' state.request
|
||||||
salt '*' state.request test
|
salt '*' state.request stuff
|
||||||
salt '*' state.request test,pkgs
|
salt '*' state.request stuff,pkgs
|
||||||
'''
|
'''
|
||||||
kwargs['test'] = True
|
kwargs['test'] = True
|
||||||
ret = apply_(mods, **kwargs)
|
ret = apply_(mods, **kwargs)
|
||||||
@ -929,7 +929,7 @@ def highstate(test=None, queue=False, **kwargs):
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
salt '*' state.apply test pillar='{"foo": "bar"}'
|
salt '*' state.apply stuff pillar='{"foo": "bar"}'
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
Values passed this way will override Pillar values set via
|
Values passed this way will override Pillar values set via
|
||||||
@ -1109,7 +1109,7 @@ def sls(mods, test=None, exclude=None, queue=False, sync_mods=None, **kwargs):
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
salt '*' state.apply test pillar='{"foo": "bar"}'
|
salt '*' state.apply stuff pillar='{"foo": "bar"}'
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
Values passed this way will override existing Pillar values set via
|
Values passed this way will override existing Pillar values set via
|
||||||
@ -1195,8 +1195,8 @@ def sls(mods, test=None, exclude=None, queue=False, sync_mods=None, **kwargs):
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
salt '*' state.sls test sync_mods=states,modules
|
salt '*' state.sls stuff sync_mods=states,modules
|
||||||
salt '*' state.sls test sync_mods=all
|
salt '*' state.sls stuff sync_mods=all
|
||||||
|
|
||||||
.. versionadded:: 2017.7.8,2018.3.3,Fluorine
|
.. versionadded:: 2017.7.8,2018.3.3,Fluorine
|
||||||
|
|
||||||
@ -1727,7 +1727,7 @@ def show_low_sls(mods, test=None, queue=False, **kwargs):
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
salt '*' state.show_low_sls test pillar='{"foo": "bar"}'
|
salt '*' state.show_low_sls stuff pillar='{"foo": "bar"}'
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
Values passed this way will override Pillar values set via
|
Values passed this way will override Pillar values set via
|
||||||
|
@ -25,7 +25,9 @@ from salt.ext import six
|
|||||||
HAS_LIBS = False
|
HAS_LIBS = False
|
||||||
try:
|
try:
|
||||||
import twilio
|
import twilio
|
||||||
if twilio.__version__ > 5:
|
# Grab version, ensure elements are ints
|
||||||
|
twilio_version = tuple([int(x) for x in twilio.__version_info__])
|
||||||
|
if twilio_version > (5, ):
|
||||||
TWILIO_5 = False
|
TWILIO_5 = False
|
||||||
from twilio.rest import Client as TwilioRestClient
|
from twilio.rest import Client as TwilioRestClient
|
||||||
from twilio.rest import TwilioException as TwilioRestException
|
from twilio.rest import TwilioException as TwilioRestException
|
||||||
|
@ -10,6 +10,7 @@ import re
|
|||||||
# Import Salt libs
|
# Import Salt libs
|
||||||
import salt.utils.platform
|
import salt.utils.platform
|
||||||
from salt.exceptions import CommandExecutionError
|
from salt.exceptions import CommandExecutionError
|
||||||
|
import salt.utils.win_lgpo_netsh
|
||||||
|
|
||||||
# Define the module's virtual name
|
# Define the module's virtual name
|
||||||
__virtualname__ = 'firewall'
|
__virtualname__ = 'firewall'
|
||||||
@ -285,7 +286,7 @@ def delete_rule(name=None,
|
|||||||
salt '*' firewall.delete_rule 'test' '8080' 'tcp' 'in'
|
salt '*' firewall.delete_rule 'test' '8080' 'tcp' 'in'
|
||||||
|
|
||||||
# Delete the incoming tcp port 8000 from 192.168.0.1 in the rule named
|
# Delete the incoming tcp port 8000 from 192.168.0.1 in the rule named
|
||||||
# 'test_remote_ip`
|
# 'test_remote_ip'
|
||||||
salt '*' firewall.delete_rule 'test_remote_ip' '8000' 'tcp' 'in' '192.168.0.1'
|
salt '*' firewall.delete_rule 'test_remote_ip' '8000' 'tcp' 'in' '192.168.0.1'
|
||||||
|
|
||||||
# Delete all rules for local port 80:
|
# Delete all rules for local port 80:
|
||||||
@ -342,3 +343,436 @@ def rule_exists(name):
|
|||||||
return True
|
return True
|
||||||
except CommandExecutionError:
|
except CommandExecutionError:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def get_settings(profile, section, store='local'):
|
||||||
|
'''
|
||||||
|
Get the firewall property from the specified profile in the specified store
|
||||||
|
as returned by ``netsh advfirewall``.
|
||||||
|
|
||||||
|
.. versionadded:: 2018.3.4
|
||||||
|
.. versionadded:: Fluorine
|
||||||
|
|
||||||
|
Args:
|
||||||
|
|
||||||
|
profile (str):
|
||||||
|
The firewall profile to query. Valid options are:
|
||||||
|
|
||||||
|
- domain
|
||||||
|
- public
|
||||||
|
- private
|
||||||
|
|
||||||
|
section (str):
|
||||||
|
The property to query within the selected profile. Valid options
|
||||||
|
are:
|
||||||
|
|
||||||
|
- firewallpolicy : inbound/outbound behavior
|
||||||
|
- logging : firewall logging settings
|
||||||
|
- settings : firewall properties
|
||||||
|
- state : firewalls state (on | off)
|
||||||
|
|
||||||
|
store (str):
|
||||||
|
The store to use. This is either the local firewall policy or the
|
||||||
|
policy defined by local group policy. Valid options are:
|
||||||
|
|
||||||
|
- lgpo
|
||||||
|
- local
|
||||||
|
|
||||||
|
Default is ``local``
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: A dictionary containing the properties for the specified profile
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
CommandExecutionError: If an error occurs
|
||||||
|
ValueError: If the parameters are incorrect
|
||||||
|
|
||||||
|
CLI Example:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
# Get the inbound/outbound firewall settings for connections on the
|
||||||
|
# local domain profile
|
||||||
|
salt * win_firewall.get_settings domain firewallpolicy
|
||||||
|
|
||||||
|
# Get the inbound/outbound firewall settings for connections on the
|
||||||
|
# domain profile as defined by local group policy
|
||||||
|
salt * win_firewall.get_settings domain firewallpolicy lgpo
|
||||||
|
'''
|
||||||
|
return salt.utils.win_lgpo_netsh.get_settings(profile=profile,
|
||||||
|
section=section,
|
||||||
|
store=store)
|
||||||
|
|
||||||
|
|
||||||
|
def get_all_settings(domain, store='local'):
|
||||||
|
'''
|
||||||
|
Gets all the properties for the specified profile in the specified store
|
||||||
|
|
||||||
|
.. versionadded:: 2018.3.4
|
||||||
|
.. versionadded:: Fluorine
|
||||||
|
|
||||||
|
Args:
|
||||||
|
|
||||||
|
profile (str):
|
||||||
|
The firewall profile to query. Valid options are:
|
||||||
|
|
||||||
|
- domain
|
||||||
|
- public
|
||||||
|
- private
|
||||||
|
|
||||||
|
store (str):
|
||||||
|
The store to use. This is either the local firewall policy or the
|
||||||
|
policy defined by local group policy. Valid options are:
|
||||||
|
|
||||||
|
- lgpo
|
||||||
|
- local
|
||||||
|
|
||||||
|
Default is ``local``
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: A dictionary containing the specified settings
|
||||||
|
|
||||||
|
CLI Example:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
# Get all firewall settings for connections on the domain profile
|
||||||
|
salt * win_firewall.get_all_settings domain
|
||||||
|
|
||||||
|
# Get all firewall settings for connections on the domain profile as
|
||||||
|
# defined by local group policy
|
||||||
|
salt * win_firewall.get_all_settings domain lgpo
|
||||||
|
'''
|
||||||
|
return salt.utils.win_lgpo_netsh.get_all_settings(profile=domain,
|
||||||
|
store=store)
|
||||||
|
|
||||||
|
|
||||||
|
def get_all_profiles(store='local'):
|
||||||
|
'''
|
||||||
|
Gets all properties for all profiles in the specified store
|
||||||
|
|
||||||
|
.. versionadded:: 2018.3.4
|
||||||
|
.. versionadded:: Fluorine
|
||||||
|
|
||||||
|
Args:
|
||||||
|
|
||||||
|
store (str):
|
||||||
|
The store to use. This is either the local firewall policy or the
|
||||||
|
policy defined by local group policy. Valid options are:
|
||||||
|
|
||||||
|
- lgpo
|
||||||
|
- local
|
||||||
|
|
||||||
|
Default is ``local``
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: A dictionary containing the specified settings for each profile
|
||||||
|
|
||||||
|
CLI Example:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
# Get all firewall settings for all profiles
|
||||||
|
salt * firewall.get_all_settings
|
||||||
|
|
||||||
|
# Get all firewall settings for all profiles as defined by local group
|
||||||
|
# policy
|
||||||
|
|
||||||
|
salt * firewall.get_all_settings lgpo
|
||||||
|
'''
|
||||||
|
return salt.utils.win_lgpo_netsh.get_all_profiles(store=store)
|
||||||
|
|
||||||
|
|
||||||
|
def set_firewall_settings(profile, inbound=None, outbound=None, store='local'):
|
||||||
|
'''
|
||||||
|
Set the firewall inbound/outbound settings for the specified profile and
|
||||||
|
store
|
||||||
|
|
||||||
|
.. versionadded:: 2018.3.4
|
||||||
|
.. versionadded:: Fluorine
|
||||||
|
|
||||||
|
Args:
|
||||||
|
|
||||||
|
profile (str):
|
||||||
|
The firewall profile to query. Valid options are:
|
||||||
|
|
||||||
|
- domain
|
||||||
|
- public
|
||||||
|
- private
|
||||||
|
|
||||||
|
inbound (str):
|
||||||
|
The inbound setting. If ``None`` is passed, the setting will remain
|
||||||
|
unchanged. Valid values are:
|
||||||
|
|
||||||
|
- blockinbound
|
||||||
|
- blockinboundalways
|
||||||
|
- allowinbound
|
||||||
|
- notconfigured
|
||||||
|
|
||||||
|
Default is ``None``
|
||||||
|
|
||||||
|
outbound (str):
|
||||||
|
The outbound setting. If ``None`` is passed, the setting will remain
|
||||||
|
unchanged. Valid values are:
|
||||||
|
|
||||||
|
- allowoutbound
|
||||||
|
- blockoutbound
|
||||||
|
- notconfigured
|
||||||
|
|
||||||
|
Default is ``None``
|
||||||
|
|
||||||
|
store (str):
|
||||||
|
The store to use. This is either the local firewall policy or the
|
||||||
|
policy defined by local group policy. Valid options are:
|
||||||
|
|
||||||
|
- lgpo
|
||||||
|
- local
|
||||||
|
|
||||||
|
Default is ``local``
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: ``True`` if successful
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
CommandExecutionError: If an error occurs
|
||||||
|
ValueError: If the parameters are incorrect
|
||||||
|
|
||||||
|
CLI Example:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
# Set the inbound setting for the domain profile to block inbound
|
||||||
|
# connections
|
||||||
|
salt * firewall.set_firewall_settings domain='domain' inbound='blockinbound'
|
||||||
|
|
||||||
|
# Set the outbound setting for the domain profile to allow outbound
|
||||||
|
# connections
|
||||||
|
salt * firewall.set_firewall_settings domain='domain' outbound='allowoutbound'
|
||||||
|
|
||||||
|
# Set inbound/outbound settings for the domain profile in the group
|
||||||
|
# policy to block inbound and allow outbound
|
||||||
|
salt * firewall.set_firewall_settings domain='domain' inbound='blockinbound' outbound='allowoutbound' store='lgpo'
|
||||||
|
'''
|
||||||
|
return salt.utils.win_lgpo_netsh.set_firewall_settings(profile=profile,
|
||||||
|
inbound=inbound,
|
||||||
|
outbound=outbound,
|
||||||
|
store=store)
|
||||||
|
|
||||||
|
|
||||||
|
def set_logging_settings(profile, setting, value, store='local'):
|
||||||
|
r'''
|
||||||
|
Configure logging settings for the Windows firewall.
|
||||||
|
|
||||||
|
.. versionadded:: 2018.3.4
|
||||||
|
.. versionadded:: Fluorine
|
||||||
|
|
||||||
|
Args:
|
||||||
|
|
||||||
|
profile (str):
|
||||||
|
The firewall profile to configure. Valid options are:
|
||||||
|
|
||||||
|
- domain
|
||||||
|
- public
|
||||||
|
- private
|
||||||
|
|
||||||
|
setting (str):
|
||||||
|
The logging setting to configure. Valid options are:
|
||||||
|
|
||||||
|
- allowedconnections
|
||||||
|
- droppedconnections
|
||||||
|
- filename
|
||||||
|
- maxfilesize
|
||||||
|
|
||||||
|
value (str):
|
||||||
|
The value to apply to the setting. Valid values are dependent upon
|
||||||
|
the setting being configured. Valid options are:
|
||||||
|
|
||||||
|
allowedconnections:
|
||||||
|
|
||||||
|
- enable
|
||||||
|
- disable
|
||||||
|
- notconfigured
|
||||||
|
|
||||||
|
droppedconnections:
|
||||||
|
|
||||||
|
- enable
|
||||||
|
- disable
|
||||||
|
- notconfigured
|
||||||
|
|
||||||
|
filename:
|
||||||
|
|
||||||
|
- Full path and name of the firewall log file
|
||||||
|
- notconfigured
|
||||||
|
|
||||||
|
maxfilesize:
|
||||||
|
|
||||||
|
- 1 - 32767
|
||||||
|
- notconfigured
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
``notconfigured`` can only be used when using the lgpo store
|
||||||
|
|
||||||
|
store (str):
|
||||||
|
The store to use. This is either the local firewall policy or the
|
||||||
|
policy defined by local group policy. Valid options are:
|
||||||
|
|
||||||
|
- lgpo
|
||||||
|
- local
|
||||||
|
|
||||||
|
Default is ``local``
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: ``True`` if successful
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
CommandExecutionError: If an error occurs
|
||||||
|
ValueError: If the parameters are incorrect
|
||||||
|
|
||||||
|
CLI Example:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
# Log allowed connections and set that in local group policy
|
||||||
|
salt * firewall.set_logging_settings domain allowedconnections enable lgpo
|
||||||
|
|
||||||
|
# Don't log dropped connections
|
||||||
|
salt * firewall.set_logging_settings profile=private setting=droppedconnections value=disable
|
||||||
|
|
||||||
|
# Set the location of the log file
|
||||||
|
salt * firewall.set_logging_settings domain filename C:\windows\logs\firewall.log
|
||||||
|
|
||||||
|
# You can also use environment variables
|
||||||
|
salt * firewall.set_logging_settings domain filename %systemroot%\system32\LogFiles\Firewall\pfirewall.log
|
||||||
|
|
||||||
|
# Set the max file size of the log to 2048 Kb
|
||||||
|
salt * firewall.set_logging_settings domain maxfilesize 2048
|
||||||
|
'''
|
||||||
|
return salt.utils.win_lgpo_netsh.set_logging_settings(profile=profile,
|
||||||
|
setting=setting,
|
||||||
|
value=value,
|
||||||
|
store=store)
|
||||||
|
|
||||||
|
|
||||||
|
def set_settings(profile, setting, value, store='local'):
|
||||||
|
'''
|
||||||
|
Configure firewall settings.
|
||||||
|
|
||||||
|
.. versionadded:: 2018.3.4
|
||||||
|
.. versionadded:: Fluorine
|
||||||
|
|
||||||
|
Args:
|
||||||
|
|
||||||
|
profile (str):
|
||||||
|
The firewall profile to configure. Valid options are:
|
||||||
|
|
||||||
|
- domain
|
||||||
|
- public
|
||||||
|
- private
|
||||||
|
|
||||||
|
setting (str):
|
||||||
|
The firewall setting to configure. Valid options are:
|
||||||
|
|
||||||
|
- localfirewallrules
|
||||||
|
- localconsecrules
|
||||||
|
- inboundusernotification
|
||||||
|
- remotemanagement
|
||||||
|
- unicastresponsetomulticast
|
||||||
|
|
||||||
|
value (str):
|
||||||
|
The value to apply to the setting. Valid options are
|
||||||
|
|
||||||
|
- enable
|
||||||
|
- disable
|
||||||
|
- notconfigured
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
``notconfigured`` can only be used when using the lgpo store
|
||||||
|
|
||||||
|
store (str):
|
||||||
|
The store to use. This is either the local firewall policy or the
|
||||||
|
policy defined by local group policy. Valid options are:
|
||||||
|
|
||||||
|
- lgpo
|
||||||
|
- local
|
||||||
|
|
||||||
|
Default is ``local``
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: ``True`` if successful
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
CommandExecutionError: If an error occurs
|
||||||
|
ValueError: If the parameters are incorrect
|
||||||
|
|
||||||
|
CLI Example:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
# Merge local rules with those distributed through group policy
|
||||||
|
salt * firewall.set_settings domain localfirewallrules enable
|
||||||
|
|
||||||
|
# Allow remote management of Windows Firewall
|
||||||
|
salt * firewall.set_settings domain remotemanagement enable
|
||||||
|
'''
|
||||||
|
return salt.utils.win_lgpo_netsh.set_settings(profile=profile,
|
||||||
|
setting=setting,
|
||||||
|
value=value,
|
||||||
|
store=store)
|
||||||
|
|
||||||
|
|
||||||
|
def set_state(profile, state, store='local'):
|
||||||
|
'''
|
||||||
|
Configure the firewall state.
|
||||||
|
|
||||||
|
.. versionadded:: 2018.3.4
|
||||||
|
.. versionadded:: Fluorine
|
||||||
|
|
||||||
|
Args:
|
||||||
|
|
||||||
|
profile (str):
|
||||||
|
The firewall profile to configure. Valid options are:
|
||||||
|
|
||||||
|
- domain
|
||||||
|
- public
|
||||||
|
- private
|
||||||
|
|
||||||
|
state (str):
|
||||||
|
The firewall state. Valid options are:
|
||||||
|
|
||||||
|
- on
|
||||||
|
- off
|
||||||
|
- notconfigured
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
``notconfigured`` can only be used when using the lgpo store
|
||||||
|
|
||||||
|
store (str):
|
||||||
|
The store to use. This is either the local firewall policy or the
|
||||||
|
policy defined by local group policy. Valid options are:
|
||||||
|
|
||||||
|
- lgpo
|
||||||
|
- local
|
||||||
|
|
||||||
|
Default is ``local``
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: ``True`` if successful
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
CommandExecutionError: If an error occurs
|
||||||
|
ValueError: If the parameters are incorrect
|
||||||
|
|
||||||
|
CLI Example:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
# Turn the firewall off when the domain profile is active
|
||||||
|
salt * firewall.set_state domain off
|
||||||
|
|
||||||
|
# Turn the firewall on when the public profile is active and set that in
|
||||||
|
# the local group policy
|
||||||
|
salt * firewall.set_state public on lgpo
|
||||||
|
'''
|
||||||
|
return salt.utils.win_lgpo_netsh.set_state(profile=profile,
|
||||||
|
state=state,
|
||||||
|
store=store)
|
||||||
|
File diff suppressed because it is too large
Load Diff
93
salt/modules/win_wusa.py
Normal file
93
salt/modules/win_wusa.py
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
'''
|
||||||
|
Microsoft Update files management via wusa.exe
|
||||||
|
|
||||||
|
:maintainer: Thomas Lemarchand
|
||||||
|
:platform: Windows
|
||||||
|
:depends: PowerShell
|
||||||
|
|
||||||
|
.. versionadded:: Neon
|
||||||
|
'''
|
||||||
|
|
||||||
|
# Import python libs
|
||||||
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
import logging
|
||||||
|
|
||||||
|
# Import salt libs
|
||||||
|
import salt.utils.platform
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Define the module's virtual name
|
||||||
|
__virtualname__ = 'win_wusa'
|
||||||
|
|
||||||
|
|
||||||
|
def __virtual__():
|
||||||
|
'''
|
||||||
|
Load only on Windows
|
||||||
|
'''
|
||||||
|
if not salt.utils.platform.is_windows():
|
||||||
|
return False, 'Only available on Windows systems'
|
||||||
|
|
||||||
|
powershell_info = __salt__['cmd.shell_info'](shell='powershell', list_modules=False)
|
||||||
|
if not powershell_info['installed']:
|
||||||
|
return False, 'PowerShell not available'
|
||||||
|
|
||||||
|
return __virtualname__
|
||||||
|
|
||||||
|
|
||||||
|
def is_installed(kb):
|
||||||
|
'''
|
||||||
|
Check if a specific KB is installed.
|
||||||
|
|
||||||
|
CLI Example:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
salt '*' win_wusa.is_installed KB123456
|
||||||
|
'''
|
||||||
|
get_hotfix_result = __salt__['cmd.powershell_all']('Get-HotFix -Id {0}'.format(kb), ignore_retcode=True)
|
||||||
|
|
||||||
|
return get_hotfix_result['retcode'] == 0
|
||||||
|
|
||||||
|
|
||||||
|
def install(path):
|
||||||
|
'''
|
||||||
|
Install a KB from a .msu file.
|
||||||
|
Some KBs will need a reboot, but this function does not manage it.
|
||||||
|
You may have to manage reboot yourself after installation.
|
||||||
|
|
||||||
|
CLI Example:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
salt '*' win_wusa.install C:/temp/KB123456.msu
|
||||||
|
'''
|
||||||
|
return __salt__['cmd.run_all']('wusa.exe {0} /quiet /norestart'.format(path), ignore_retcode=True)
|
||||||
|
|
||||||
|
|
||||||
|
def uninstall(kb):
|
||||||
|
'''
|
||||||
|
Uninstall a specific KB.
|
||||||
|
|
||||||
|
CLI Example:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
salt '*' win_wusa.uninstall KB123456
|
||||||
|
'''
|
||||||
|
return __salt__['cmd.run_all']('wusa.exe /uninstall /kb:{0} /quiet /norestart'.format(kb[2:]), ignore_retcode=True)
|
||||||
|
|
||||||
|
|
||||||
|
def list_kbs():
|
||||||
|
'''
|
||||||
|
Return a list of dictionaries, one dictionary for each installed KB.
|
||||||
|
The HotFixID key contains the ID of the KB.
|
||||||
|
|
||||||
|
CLI Example:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
salt '*' win_wusa.list_kbs
|
||||||
|
'''
|
||||||
|
return __salt__['cmd.powershell']('Get-HotFix')
|
@ -502,6 +502,8 @@ class BaseSaltAPIHandler(tornado.web.RequestHandler): # pylint: disable=W0223
|
|||||||
'''
|
'''
|
||||||
# timeout all the futures
|
# timeout all the futures
|
||||||
self.timeout_futures()
|
self.timeout_futures()
|
||||||
|
# clear local_client objects to disconnect event publisher's IOStream connections
|
||||||
|
del self.saltclients
|
||||||
|
|
||||||
def on_connection_close(self):
|
def on_connection_close(self):
|
||||||
'''
|
'''
|
||||||
@ -931,14 +933,27 @@ class SaltAPIHandler(BaseSaltAPIHandler): # pylint: disable=W0223
|
|||||||
'''
|
'''
|
||||||
Dispatch local client commands
|
Dispatch local client commands
|
||||||
'''
|
'''
|
||||||
# Generate jid before triggering a job to subscribe all returns from minions
|
# Generate jid and find all minions before triggering a job to subscribe all returns from minions
|
||||||
chunk['jid'] = salt.utils.jid.gen_jid(self.application.opts)
|
chunk['jid'] = salt.utils.jid.gen_jid(self.application.opts) if not chunk.get('jid', None) else chunk['jid']
|
||||||
|
minions = set(self.ckminions.check_minions(chunk['tgt'], chunk.get('tgt_type', 'glob')))
|
||||||
|
|
||||||
|
def subscribe_minion(minion):
|
||||||
|
salt_evt = self.application.event_listener.get_event(
|
||||||
|
self,
|
||||||
|
tag='salt/job/{}/ret/{}'.format(chunk['jid'], minion),
|
||||||
|
matcher=EventListener.exact_matcher)
|
||||||
|
syndic_evt = self.application.event_listener.get_event(
|
||||||
|
self,
|
||||||
|
tag='syndic/job/{}/ret/{}'.format(chunk['jid'], minion),
|
||||||
|
matcher=EventListener.exact_matcher)
|
||||||
|
return salt_evt, syndic_evt
|
||||||
|
|
||||||
# start listening for the event before we fire the job to avoid races
|
# start listening for the event before we fire the job to avoid races
|
||||||
events = [
|
events = []
|
||||||
self.application.event_listener.get_event(self, tag='salt/job/'+chunk['jid']),
|
for minion in minions:
|
||||||
self.application.event_listener.get_event(self, tag='syndic/job/'+chunk['jid']),
|
salt_evt, syndic_evt = subscribe_minion(minion)
|
||||||
]
|
events.append(salt_evt)
|
||||||
|
events.append(syndic_evt)
|
||||||
|
|
||||||
f_call = self._format_call_run_job_async(chunk)
|
f_call = self._format_call_run_job_async(chunk)
|
||||||
# fire a job off
|
# fire a job off
|
||||||
@ -954,6 +969,12 @@ class SaltAPIHandler(BaseSaltAPIHandler): # pylint: disable=W0223
|
|||||||
pass
|
pass
|
||||||
raise tornado.gen.Return('No minions matched the target. No command was sent, no jid was assigned.')
|
raise tornado.gen.Return('No minions matched the target. No command was sent, no jid was assigned.')
|
||||||
|
|
||||||
|
# get_event for missing minion
|
||||||
|
for minion in list(set(pub_data['minions']) - set(minions)):
|
||||||
|
salt_evt, syndic_evt = subscribe_minion(minion)
|
||||||
|
events.append(salt_evt)
|
||||||
|
events.append(syndic_evt)
|
||||||
|
|
||||||
# Map of minion_id -> returned for all minions we think we need to wait on
|
# Map of minion_id -> returned for all minions we think we need to wait on
|
||||||
minions = {m: False for m in pub_data['minions']}
|
minions = {m: False for m in pub_data['minions']}
|
||||||
|
|
||||||
@ -1008,7 +1029,10 @@ class SaltAPIHandler(BaseSaltAPIHandler): # pylint: disable=W0223
|
|||||||
cancel_inflight_futures()
|
cancel_inflight_futures()
|
||||||
raise tornado.gen.Return(chunk_ret)
|
raise tornado.gen.Return(chunk_ret)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
f_result = f.result()
|
f_result = f.result()
|
||||||
|
if f in events:
|
||||||
|
events.remove(f)
|
||||||
# if this is a start, then we need to add it to the pile
|
# if this is a start, then we need to add it to the pile
|
||||||
if f_result['tag'].endswith('/new'):
|
if f_result['tag'].endswith('/new'):
|
||||||
for minion_id in f_result['data']['minions']:
|
for minion_id in f_result['data']['minions']:
|
||||||
@ -1018,7 +1042,6 @@ class SaltAPIHandler(BaseSaltAPIHandler): # pylint: disable=W0223
|
|||||||
chunk_ret[f_result['data']['id']] = f_result['data']['return']
|
chunk_ret[f_result['data']['id']] = f_result['data']['return']
|
||||||
# clear finished event future
|
# clear finished event future
|
||||||
minions[f_result['data']['id']] = True
|
minions[f_result['data']['id']] = True
|
||||||
|
|
||||||
# if there are no more minions to wait for, then we are done
|
# if there are no more minions to wait for, then we are done
|
||||||
if not more_todo() and min_wait_time.done():
|
if not more_todo() and min_wait_time.done():
|
||||||
cancel_inflight_futures()
|
cancel_inflight_futures()
|
||||||
@ -1027,11 +1050,6 @@ class SaltAPIHandler(BaseSaltAPIHandler): # pylint: disable=W0223
|
|||||||
except TimeoutException:
|
except TimeoutException:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if f == events[0]:
|
|
||||||
events[0] = self.application.event_listener.get_event(self, tag='salt/job/'+chunk['jid'])
|
|
||||||
else:
|
|
||||||
events[1] = self.application.event_listener.get_event(self, tag='syndic/job/'+chunk['jid'])
|
|
||||||
|
|
||||||
@tornado.gen.coroutine
|
@tornado.gen.coroutine
|
||||||
def job_not_running(self, jid, tgt, tgt_type, minions, is_finished):
|
def job_not_running(self, jid, tgt, tgt_type, minions, is_finished):
|
||||||
'''
|
'''
|
||||||
|
@ -327,6 +327,29 @@ mountpoint to ``web/`` (and restart the ``salt-master`` daemon).
|
|||||||
:conf_master:`git_pillar_includes` is not disabled.
|
:conf_master:`git_pillar_includes` is not disabled.
|
||||||
- Content from mounted git_pillar repos can only be referenced by a top
|
- Content from mounted git_pillar repos can only be referenced by a top
|
||||||
file in the same pillar environment.
|
file in the same pillar environment.
|
||||||
|
- Salt versions prior to 2018.3.4 ignore the ``root`` parameter when
|
||||||
|
``mountpoint`` is set.
|
||||||
|
|
||||||
|
.. _git-pillar-all_saltenvs:
|
||||||
|
|
||||||
|
all_saltenvs
|
||||||
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. versionadded:: 2018.3.4
|
||||||
|
|
||||||
|
When ``__env__`` is specified as the branch name, ``all_saltenvs`` per-remote configuration parameter overrides the logic Salt uses to map branches/tags to pillar environments (i.e. pillarenvs). This allows a single branch/tag to appear in all saltenvs. Example:
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
ext_pillar:
|
||||||
|
- git:
|
||||||
|
- __env__ https://mydomain.tld/top.git
|
||||||
|
- all_saltenvs: master
|
||||||
|
- __env__ https://mydomain.tld/pillar-nginx.git:
|
||||||
|
- mountpoint: web/server/
|
||||||
|
- __env__ https://mydomain.tld/pillar-appdata.git:
|
||||||
|
- mountpoint: web/server/
|
||||||
|
|
||||||
'''
|
'''
|
||||||
from __future__ import absolute_import, print_function, unicode_literals
|
from __future__ import absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
@ -346,7 +369,7 @@ from salt.pillar import Pillar
|
|||||||
from salt.ext import six
|
from salt.ext import six
|
||||||
|
|
||||||
PER_REMOTE_OVERRIDES = ('env', 'root', 'ssl_verify', 'refspecs')
|
PER_REMOTE_OVERRIDES = ('env', 'root', 'ssl_verify', 'refspecs')
|
||||||
PER_REMOTE_ONLY = ('name', 'mountpoint')
|
PER_REMOTE_ONLY = ('name', 'mountpoint', 'all_saltenvs')
|
||||||
GLOBAL_ONLY = ('base', 'branch')
|
GLOBAL_ONLY = ('base', 'branch')
|
||||||
|
|
||||||
# Set up logging
|
# Set up logging
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
'''
|
'''
|
||||||
=================
|
|
||||||
Nodegroups Pillar
|
Nodegroups Pillar
|
||||||
=================
|
=================
|
||||||
|
|
||||||
|
@ -364,8 +364,8 @@ def key_value_to_tree(data):
|
|||||||
for flatkey, value in six.iteritems(data):
|
for flatkey, value in six.iteritems(data):
|
||||||
t = tree
|
t = tree
|
||||||
keys = flatkey.split(__opts__['pepa_delimiter'])
|
keys = flatkey.split(__opts__['pepa_delimiter'])
|
||||||
for key in keys:
|
for i, key in enumerate(keys, 1):
|
||||||
if key == keys[-1]:
|
if i == len(keys):
|
||||||
t[key] = value
|
t[key] = value
|
||||||
else:
|
else:
|
||||||
t = t.setdefault(key, {})
|
t = t.setdefault(key, {})
|
||||||
|
@ -29,19 +29,20 @@ def __virtual__():
|
|||||||
|
|
||||||
def ext_pillar(minion_id, pillar, *args, **kwargs):
|
def ext_pillar(minion_id, pillar, *args, **kwargs):
|
||||||
'''
|
'''
|
||||||
Node definitions path will be retrieved from args - or set to default -
|
Compile pillar data
|
||||||
then added to 'salt_data' dict that is passed to the 'get_pillars' function.
|
|
||||||
'salt_data' dict is a convenient way to pass all the required datas to the function
|
|
||||||
It contains:
|
|
||||||
- __opts__
|
|
||||||
- __salt__
|
|
||||||
- __grains__
|
|
||||||
- __pillar__
|
|
||||||
- minion_id
|
|
||||||
- path
|
|
||||||
|
|
||||||
If successfull the function will return a pillar dict for minion_id
|
|
||||||
'''
|
'''
|
||||||
|
# Node definitions path will be retrieved from args (or set to default),
|
||||||
|
# then added to 'salt_data' dict that is passed to the 'get_pillars'
|
||||||
|
# function. The dictionary contains:
|
||||||
|
# - __opts__
|
||||||
|
# - __salt__
|
||||||
|
# - __grains__
|
||||||
|
# - __pillar__
|
||||||
|
# - minion_id
|
||||||
|
# - path
|
||||||
|
#
|
||||||
|
# If successful, the function will return a pillar dict for minion_id.
|
||||||
|
|
||||||
# If path has not been set, make a default
|
# If path has not been set, make a default
|
||||||
for i in args:
|
for i in args:
|
||||||
if 'path' not in i:
|
if 'path' not in i:
|
||||||
|
@ -47,8 +47,8 @@ execution functions, grains, pillar, etc. They are:
|
|||||||
``/srv/salt/foo/bar/baz.sls``, then ``__sls__`` in that file will be
|
``/srv/salt/foo/bar/baz.sls``, then ``__sls__`` in that file will be
|
||||||
``foo.bar.baz``.
|
``foo.bar.baz``.
|
||||||
|
|
||||||
The global context ``data`` (same as context ``{{ data }}`` for states written
|
When writing a reactor SLS file the global context ``data`` (same as context ``{{ data }}``
|
||||||
with Jinja + YAML). The following YAML + Jinja state declaration:
|
for states written with Jinja + YAML) is available. The following YAML + Jinja state declaration:
|
||||||
|
|
||||||
.. code-block:: jinja
|
.. code-block:: jinja
|
||||||
|
|
||||||
|
@ -137,7 +137,10 @@ def _fetch_option(cfg, ret_config, virtualname, attr_name):
|
|||||||
if not ret_config:
|
if not ret_config:
|
||||||
# Using the default configuration key
|
# Using the default configuration key
|
||||||
if isinstance(cfg, dict):
|
if isinstance(cfg, dict):
|
||||||
return c_cfg.get(attr_name, cfg.get(default_cfg_key))
|
if default_cfg_key in cfg:
|
||||||
|
return cfg[default_cfg_key]
|
||||||
|
else:
|
||||||
|
return c_cfg.get(attr_name)
|
||||||
else:
|
else:
|
||||||
return c_cfg.get(attr_name, cfg(default_cfg_key))
|
return c_cfg.get(attr_name, cfg(default_cfg_key))
|
||||||
|
|
||||||
|
@ -1736,14 +1736,15 @@ class State(object):
|
|||||||
try:
|
try:
|
||||||
ret = self.states[cdata['full']](*cdata['args'],
|
ret = self.states[cdata['full']](*cdata['args'],
|
||||||
**cdata['kwargs'])
|
**cdata['kwargs'])
|
||||||
except Exception:
|
except Exception as exc:
|
||||||
|
log.debug('An exception occurred in this state: %s', exc,
|
||||||
|
exc_info_on_loglevel=logging.DEBUG)
|
||||||
trb = traceback.format_exc()
|
trb = traceback.format_exc()
|
||||||
ret = {
|
ret = {
|
||||||
'result': False,
|
'result': False,
|
||||||
'name': name,
|
'name': name,
|
||||||
'changes': {},
|
'changes': {},
|
||||||
'comment': 'An exception occurred in this state: {0}'.format(
|
'comment': 'An exception occurred in this state: {0}'.format(trb)
|
||||||
trb)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
utc_finish_time = datetime.datetime.utcnow()
|
utc_finish_time = datetime.datetime.utcnow()
|
||||||
@ -1919,7 +1920,9 @@ class State(object):
|
|||||||
self.states.inject_globals = {}
|
self.states.inject_globals = {}
|
||||||
if 'check_cmd' in low and '{0[state]}.mod_run_check_cmd'.format(low) not in self.states:
|
if 'check_cmd' in low and '{0[state]}.mod_run_check_cmd'.format(low) not in self.states:
|
||||||
ret.update(self._run_check_cmd(low))
|
ret.update(self._run_check_cmd(low))
|
||||||
except Exception:
|
except Exception as exc:
|
||||||
|
log.debug('An exception occurred in this state: %s', exc,
|
||||||
|
exc_info_on_loglevel=logging.DEBUG)
|
||||||
trb = traceback.format_exc()
|
trb = traceback.format_exc()
|
||||||
# There are a number of possibilities to not have the cdata
|
# There are a number of possibilities to not have the cdata
|
||||||
# populated with what we might have expected, so just be smart
|
# populated with what we might have expected, so just be smart
|
||||||
@ -1934,8 +1937,7 @@ class State(object):
|
|||||||
'result': False,
|
'result': False,
|
||||||
'name': name,
|
'name': name,
|
||||||
'changes': {},
|
'changes': {},
|
||||||
'comment': 'An exception occurred in this state: {0}'.format(
|
'comment': 'An exception occurred in this state: {0}'.format(trb)
|
||||||
trb)
|
|
||||||
}
|
}
|
||||||
finally:
|
finally:
|
||||||
if low.get('__prereq__'):
|
if low.get('__prereq__'):
|
||||||
|
@ -202,6 +202,12 @@ def set_(name, path):
|
|||||||
path
|
path
|
||||||
is the location of one of the alternative target files.
|
is the location of one of the alternative target files.
|
||||||
(e.g. /usr/bin/less)
|
(e.g. /usr/bin/less)
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
foo:
|
||||||
|
alternatives.set:
|
||||||
|
- path: /usr/bin/foo-2.0
|
||||||
'''
|
'''
|
||||||
ret = {'name': name,
|
ret = {'name': name,
|
||||||
'path': path,
|
'path': path,
|
||||||
|
@ -16,8 +16,6 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
r'''
|
r'''
|
||||||
:codeauthor: :email:`Bo Maryniuk <bo@suse.de>`
|
|
||||||
|
|
||||||
Execution of Ansible modules from within states
|
Execution of Ansible modules from within states
|
||||||
===============================================
|
===============================================
|
||||||
|
|
||||||
|
@ -84,12 +84,18 @@ def downloaded(name, artifact, target_dir='/tmp', target_file=None, use_literal_
|
|||||||
'changes': {},
|
'changes': {},
|
||||||
'comment': ''}
|
'comment': ''}
|
||||||
|
|
||||||
try:
|
if 'test' in __opts__ and __opts__['test'] is True:
|
||||||
fetch_result = __fetch_from_artifactory(artifact, target_dir, target_file, use_literal_group_id)
|
fetch_result = {}
|
||||||
except Exception as exc:
|
fetch_result['status'] = True
|
||||||
ret['result'] = False
|
fetch_result['comment'] = 'Artifact would be downloaded from URL: {0}'.format(artifact['artifactory_url'])
|
||||||
ret['comment'] = six.text_type(exc)
|
fetch_result['changes'] = {}
|
||||||
return ret
|
else:
|
||||||
|
try:
|
||||||
|
fetch_result = __fetch_from_artifactory(artifact, target_dir, target_file, use_literal_group_id)
|
||||||
|
except Exception as exc:
|
||||||
|
ret['result'] = False
|
||||||
|
ret['comment'] = six.text_type(exc)
|
||||||
|
return ret
|
||||||
|
|
||||||
log.debug('fetch_result = %s', fetch_result)
|
log.debug('fetch_result = %s', fetch_result)
|
||||||
|
|
||||||
|
@ -5,11 +5,6 @@ Management of Gentoo configuration using eselect
|
|||||||
|
|
||||||
A state module to manage Gentoo configuration via eselect
|
A state module to manage Gentoo configuration via eselect
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
profile:
|
|
||||||
eselect.set:
|
|
||||||
target: hardened/linux/amd64
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
# Import Python libs
|
# Import Python libs
|
||||||
@ -44,6 +39,11 @@ def set_(name, target, module_parameter=None, action_parameter=None):
|
|||||||
action_parameter
|
action_parameter
|
||||||
additional params passed to the defined action
|
additional params passed to the defined action
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
profile:
|
||||||
|
eselect.set:
|
||||||
|
- target: hardened/linux/amd64
|
||||||
'''
|
'''
|
||||||
ret = {'changes': {},
|
ret = {'changes': {},
|
||||||
'comment': '',
|
'comment': '',
|
||||||
|
@ -126,7 +126,6 @@ __virtualname__ = 'etcd'
|
|||||||
# Function aliases
|
# Function aliases
|
||||||
__func_alias__ = {
|
__func_alias__ = {
|
||||||
'set_': 'set',
|
'set_': 'set',
|
||||||
'rm_': 'rm'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Import third party libs
|
# Import third party libs
|
||||||
@ -147,12 +146,13 @@ def __virtual__():
|
|||||||
|
|
||||||
def set_(name, value, profile=None, **kwargs):
|
def set_(name, value, profile=None, **kwargs):
|
||||||
'''
|
'''
|
||||||
Set a key in etcd and can be called as ``set``.
|
Set a key in etcd
|
||||||
|
|
||||||
name
|
name
|
||||||
The etcd key name, for example: ``/foo/bar/baz``.
|
The etcd key name, for example: ``/foo/bar/baz``.
|
||||||
value
|
value
|
||||||
The value the key should contain.
|
The value the key should contain.
|
||||||
|
|
||||||
profile
|
profile
|
||||||
Optional, defaults to ``None``. Sets the etcd profile to use which has
|
Optional, defaults to ``None``. Sets the etcd profile to use which has
|
||||||
been defined in the Salt Master config.
|
been defined in the Salt Master config.
|
||||||
@ -263,14 +263,16 @@ def directory(name, profile=None, **kwargs):
|
|||||||
return rtn
|
return rtn
|
||||||
|
|
||||||
|
|
||||||
def rm_(name, recurse=False, profile=None, **kwargs):
|
def rm(name, recurse=False, profile=None, **kwargs):
|
||||||
'''
|
'''
|
||||||
Deletes a key from etcd. This function is also aliased as ``rm``.
|
Deletes a key from etcd
|
||||||
|
|
||||||
name
|
name
|
||||||
The etcd key name to remove, for example ``/foo/bar/baz``.
|
The etcd key name to remove, for example ``/foo/bar/baz``.
|
||||||
|
|
||||||
recurse
|
recurse
|
||||||
Optional, defaults to ``False``. If ``True`` performs a recursive delete.
|
Optional, defaults to ``False``. If ``True`` performs a recursive delete.
|
||||||
|
|
||||||
profile
|
profile
|
||||||
Optional, defaults to ``None``. Sets the etcd profile to use which has
|
Optional, defaults to ``None``. Sets the etcd profile to use which has
|
||||||
been defined in the Salt Master config.
|
been defined in the Salt Master config.
|
||||||
@ -353,7 +355,7 @@ def mod_watch(name, **kwargs):
|
|||||||
|
|
||||||
# Watch to rm etcd key
|
# Watch to rm etcd key
|
||||||
if kwargs.get('sfun') in ['wait_rm_key', 'wait_rm']:
|
if kwargs.get('sfun') in ['wait_rm_key', 'wait_rm']:
|
||||||
return rm_(
|
return rm(
|
||||||
name,
|
name,
|
||||||
kwargs.get('profile'))
|
kwargs.get('profile'))
|
||||||
|
|
||||||
|
@ -677,6 +677,7 @@ def _check_directory(name,
|
|||||||
group=None,
|
group=None,
|
||||||
recurse=False,
|
recurse=False,
|
||||||
mode=None,
|
mode=None,
|
||||||
|
file_mode=None,
|
||||||
clean=False,
|
clean=False,
|
||||||
require=False,
|
require=False,
|
||||||
exclude_pat=None,
|
exclude_pat=None,
|
||||||
@ -712,6 +713,7 @@ def _check_directory(name,
|
|||||||
if check_files:
|
if check_files:
|
||||||
for fname in files:
|
for fname in files:
|
||||||
fchange = {}
|
fchange = {}
|
||||||
|
mode = file_mode
|
||||||
path = os.path.join(root, fname)
|
path = os.path.join(root, fname)
|
||||||
stats = __salt__['file.stats'](
|
stats = __salt__['file.stats'](
|
||||||
path, None, follow_symlinks
|
path, None, follow_symlinks
|
||||||
@ -720,6 +722,8 @@ def _check_directory(name,
|
|||||||
fchange['user'] = user
|
fchange['user'] = user
|
||||||
if group is not None and group != stats.get('group'):
|
if group is not None and group != stats.get('group'):
|
||||||
fchange['group'] = group
|
fchange['group'] = group
|
||||||
|
if mode is not None and mode != stats.get('mode'):
|
||||||
|
fchange['mode'] = mode
|
||||||
if fchange:
|
if fchange:
|
||||||
changes[path] = fchange
|
changes[path] = fchange
|
||||||
if check_dirs:
|
if check_dirs:
|
||||||
@ -3119,8 +3123,8 @@ def directory(name,
|
|||||||
win_perms_reset=win_perms_reset)
|
win_perms_reset=win_perms_reset)
|
||||||
else:
|
else:
|
||||||
presult, pcomment, pchanges = _check_directory(
|
presult, pcomment, pchanges = _check_directory(
|
||||||
name, user, group, recurse or [], dir_mode, clean, require,
|
name, user, group, recurse or [], dir_mode, file_mode, clean,
|
||||||
exclude_pat, max_depth, follow_symlinks)
|
require, exclude_pat, max_depth, follow_symlinks)
|
||||||
|
|
||||||
if pchanges:
|
if pchanges:
|
||||||
ret['pchanges'].update(pchanges)
|
ret['pchanges'].update(pchanges)
|
||||||
|
@ -16,6 +16,9 @@ you can specify what ruby version and gemset to target.
|
|||||||
'''
|
'''
|
||||||
from __future__ import absolute_import, unicode_literals, print_function
|
from __future__ import absolute_import, unicode_literals, print_function
|
||||||
|
|
||||||
|
import salt.utils
|
||||||
|
|
||||||
|
import re
|
||||||
import logging
|
import logging
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -84,10 +87,29 @@ def installed(name, # pylint: disable=C0103
|
|||||||
'Use of argument ruby found, but neither rvm or rbenv is installed'
|
'Use of argument ruby found, but neither rvm or rbenv is installed'
|
||||||
)
|
)
|
||||||
gems = __salt__['gem.list'](name, ruby, gem_bin=gem_bin, runas=user)
|
gems = __salt__['gem.list'](name, ruby, gem_bin=gem_bin, runas=user)
|
||||||
if name in gems and version is not None and str(version) in gems[name]:
|
if name in gems and version is not None:
|
||||||
ret['result'] = True
|
match = re.match(r'(>=|>|<|<=)', version)
|
||||||
ret['comment'] = 'Gem is already installed.'
|
if match:
|
||||||
return ret
|
# Grab the comparison
|
||||||
|
cmpr = match.group()
|
||||||
|
|
||||||
|
# Clear out 'default:' and any whitespace
|
||||||
|
installed_version = re.sub('default: ', '', gems[name][0]).strip()
|
||||||
|
|
||||||
|
# Clear out comparison from version and whitespace
|
||||||
|
desired_version = re.sub(cmpr, '', version).strip()
|
||||||
|
|
||||||
|
if salt.utils.compare_versions(installed_version,
|
||||||
|
cmpr,
|
||||||
|
desired_version):
|
||||||
|
ret['result'] = True
|
||||||
|
ret['comment'] = 'Installed Gem meets version requirements.'
|
||||||
|
return ret
|
||||||
|
else:
|
||||||
|
if str(version) in gems[name]:
|
||||||
|
ret['result'] = True
|
||||||
|
ret['comment'] = 'Gem is already installed.'
|
||||||
|
return ret
|
||||||
elif name in gems and version is None:
|
elif name in gems and version is None:
|
||||||
ret['result'] = True
|
ret['result'] = True
|
||||||
ret['comment'] = 'Gem is already installed.'
|
ret['comment'] = 'Gem is already installed.'
|
||||||
|
@ -281,8 +281,8 @@ def latest(name,
|
|||||||
identity=None,
|
identity=None,
|
||||||
https_user=None,
|
https_user=None,
|
||||||
https_pass=None,
|
https_pass=None,
|
||||||
onlyif=False,
|
onlyif=None,
|
||||||
unless=False,
|
unless=None,
|
||||||
refspec_branch='*',
|
refspec_branch='*',
|
||||||
refspec_tag='*',
|
refspec_tag='*',
|
||||||
output_encoding=None,
|
output_encoding=None,
|
||||||
@ -2197,8 +2197,8 @@ def detached(name,
|
|||||||
identity=None,
|
identity=None,
|
||||||
https_user=None,
|
https_user=None,
|
||||||
https_pass=None,
|
https_pass=None,
|
||||||
onlyif=False,
|
onlyif=None,
|
||||||
unless=False,
|
unless=None,
|
||||||
output_encoding=None,
|
output_encoding=None,
|
||||||
**kwargs):
|
**kwargs):
|
||||||
'''
|
'''
|
||||||
@ -3409,18 +3409,65 @@ def mod_run_check(cmd_kwargs, onlyif, unless):
|
|||||||
Otherwise, returns ``True``
|
Otherwise, returns ``True``
|
||||||
'''
|
'''
|
||||||
cmd_kwargs = copy.deepcopy(cmd_kwargs)
|
cmd_kwargs = copy.deepcopy(cmd_kwargs)
|
||||||
cmd_kwargs['python_shell'] = True
|
cmd_kwargs.update({
|
||||||
if onlyif:
|
'use_vt': False,
|
||||||
if __salt__['cmd.retcode'](onlyif, **cmd_kwargs) != 0:
|
'bg': False,
|
||||||
|
'ignore_retcode': True,
|
||||||
|
'python_shell': True,
|
||||||
|
})
|
||||||
|
|
||||||
|
if onlyif is not None:
|
||||||
|
if not isinstance(onlyif, list):
|
||||||
|
onlyif = [onlyif]
|
||||||
|
|
||||||
|
for command in onlyif:
|
||||||
|
if not isinstance(command, six.string_types) and command:
|
||||||
|
# Boolean or some other non-string which resolves to True
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
if __salt__['cmd.retcode'](command, **cmd_kwargs) == 0:
|
||||||
|
# Command exited with a zero retcode
|
||||||
|
continue
|
||||||
|
except Exception as exc:
|
||||||
|
log.exception(
|
||||||
|
'The following onlyif command raised an error: %s',
|
||||||
|
command
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
'comment': 'onlyif raised error ({0}), see log for '
|
||||||
|
'more details'.format(exc),
|
||||||
|
'result': False
|
||||||
|
}
|
||||||
|
|
||||||
return {'comment': 'onlyif condition is false',
|
return {'comment': 'onlyif condition is false',
|
||||||
'skip_watch': True,
|
'skip_watch': True,
|
||||||
'result': True}
|
'result': True}
|
||||||
|
|
||||||
if unless:
|
if unless is not None:
|
||||||
if __salt__['cmd.retcode'](unless, **cmd_kwargs) == 0:
|
if not isinstance(unless, list):
|
||||||
|
unless = [unless]
|
||||||
|
|
||||||
|
for command in unless:
|
||||||
|
if not isinstance(command, six.string_types) and not command:
|
||||||
|
# Boolean or some other non-string which resolves to False
|
||||||
|
break
|
||||||
|
try:
|
||||||
|
if __salt__['cmd.retcode'](command, **cmd_kwargs) != 0:
|
||||||
|
# Command exited with a non-zero retcode
|
||||||
|
break
|
||||||
|
except Exception as exc:
|
||||||
|
log.exception(
|
||||||
|
'The following unless command raised an error: %s',
|
||||||
|
command
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
'comment': 'unless raised error ({0}), see log for '
|
||||||
|
'more details'.format(exc),
|
||||||
|
'result': False
|
||||||
|
}
|
||||||
|
else:
|
||||||
return {'comment': 'unless condition is true',
|
return {'comment': 'unless condition is true',
|
||||||
'skip_watch': True,
|
'skip_watch': True,
|
||||||
'result': True}
|
'result': True}
|
||||||
|
|
||||||
# No reason to stop, return True
|
|
||||||
return True
|
return True
|
||||||
|
@ -247,6 +247,7 @@ def absent(name, orgname=None, profile='grafana'):
|
|||||||
|
|
||||||
_IGNORED_DASHBOARD_FIELDS = [
|
_IGNORED_DASHBOARD_FIELDS = [
|
||||||
'id',
|
'id',
|
||||||
|
'uid',
|
||||||
'originalTitle',
|
'originalTitle',
|
||||||
'version',
|
'version',
|
||||||
]
|
]
|
||||||
|
@ -67,7 +67,7 @@ from salt.ext import six
|
|||||||
import salt.utils.validate.net
|
import salt.utils.validate.net
|
||||||
|
|
||||||
|
|
||||||
def present(name, ip): # pylint: disable=C0103
|
def present(name, ip, clean=False): # pylint: disable=C0103
|
||||||
'''
|
'''
|
||||||
Ensures that the named host is present with the given ip
|
Ensures that the named host is present with the given ip
|
||||||
|
|
||||||
@ -75,36 +75,92 @@ def present(name, ip): # pylint: disable=C0103
|
|||||||
The host to assign an ip to
|
The host to assign an ip to
|
||||||
|
|
||||||
ip
|
ip
|
||||||
The ip addr(s) to apply to the host
|
The ip addr(s) to apply to the host. Can be a single IP or a list of IP
|
||||||
|
addresses.
|
||||||
|
|
||||||
|
clean : False
|
||||||
|
Remove any entries which don't match those configured in the ``ip``
|
||||||
|
option.
|
||||||
|
|
||||||
|
.. versionadded:: 2018.3.4
|
||||||
'''
|
'''
|
||||||
ret = {'name': name,
|
ret = {'name': name,
|
||||||
'changes': {},
|
'changes': {},
|
||||||
'result': None,
|
'result': None if __opts__['test'] else True,
|
||||||
'comment': ''}
|
'comment': ''}
|
||||||
|
|
||||||
if not isinstance(ip, list):
|
if not isinstance(ip, list):
|
||||||
ip = [ip]
|
ip = [ip]
|
||||||
|
|
||||||
|
all_hosts = __salt__['hosts.list_hosts']()
|
||||||
comments = []
|
comments = []
|
||||||
for _ip in ip:
|
to_add = set()
|
||||||
if __salt__['hosts.has_pair'](_ip, name):
|
to_remove = set()
|
||||||
ret['result'] = True
|
|
||||||
comments.append('Host {0} ({1}) already present'.format(name, _ip))
|
# First check for IPs not currently in the hosts file
|
||||||
|
to_add.update([(addr, name) for addr in ip if addr not in all_hosts])
|
||||||
|
|
||||||
|
# Now sweep through the hosts file and look for entries matching either the
|
||||||
|
# IP address(es) or hostname.
|
||||||
|
for addr, aliases in six.iteritems(all_hosts):
|
||||||
|
if addr not in ip:
|
||||||
|
if name in aliases:
|
||||||
|
# Found match for hostname, but the corresponding IP is not in
|
||||||
|
# our list, so we need to remove it.
|
||||||
|
if clean:
|
||||||
|
to_remove.add((addr, name))
|
||||||
|
else:
|
||||||
|
ret.setdefault('warnings', []).append(
|
||||||
|
'Host {0} present for IP address {1}. To get rid of '
|
||||||
|
'this warning, either run this state with \'clean\' '
|
||||||
|
'set to True to remove {0} from {1}, or add {1} to '
|
||||||
|
'the \'ip\' argument.'.format(name, addr)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
if __opts__['test']:
|
if name in aliases:
|
||||||
comments.append('Host {0} ({1}) needs to be added/updated'.format(name, _ip))
|
# No changes needed for this IP address and hostname
|
||||||
|
comments.append(
|
||||||
|
'Host {0} ({1}) already present'.format(name, addr)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
if salt.utils.validate.net.ipv4_addr(_ip) or salt.utils.validate.net.ipv6_addr(_ip):
|
# IP address listed in hosts file, but hostname is not present.
|
||||||
if __salt__['hosts.add_host'](_ip, name):
|
# We will need to add it.
|
||||||
ret['changes'] = {'host': name}
|
if salt.utils.validate.net.ip_addr(addr):
|
||||||
ret['result'] = True
|
to_add.add((addr, name))
|
||||||
comments.append('Added host {0} ({1})'.format(name, _ip))
|
|
||||||
else:
|
|
||||||
ret['result'] = False
|
|
||||||
comments.append('Failed to set host')
|
|
||||||
else:
|
else:
|
||||||
ret['result'] = False
|
ret['result'] = False
|
||||||
comments.append('Invalid IP Address for {0} ({1})'.format(name, _ip))
|
comments.append(
|
||||||
|
'Invalid IP Address for {0} ({1})'.format(name, addr)
|
||||||
|
)
|
||||||
|
|
||||||
|
for addr, name in to_add:
|
||||||
|
if __opts__['test']:
|
||||||
|
comments.append(
|
||||||
|
'Host {0} ({1}) would be added'.format(name, addr)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
if __salt__['hosts.add_host'](addr, name):
|
||||||
|
comments.append('Added host {0} ({1})'.format(name, addr))
|
||||||
|
else:
|
||||||
|
ret['result'] = False
|
||||||
|
comments.append('Failed to add host {0} ({1})'.format(name, addr))
|
||||||
|
continue
|
||||||
|
ret['changes'].setdefault('added', {}).setdefault(addr, []).append(name)
|
||||||
|
|
||||||
|
for addr, name in to_remove:
|
||||||
|
if __opts__['test']:
|
||||||
|
comments.append(
|
||||||
|
'Host {0} ({1}) would be removed'.format(name, addr)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
if __salt__['hosts.rm_host'](addr, name):
|
||||||
|
comments.append('Removed host {0} ({1})'.format(name, addr))
|
||||||
|
else:
|
||||||
|
ret['result'] = False
|
||||||
|
comments.append('Failed to remove host {0} ({1})'.format(name, addr))
|
||||||
|
continue
|
||||||
|
ret['changes'].setdefault('removed', {}).setdefault(addr, []).append(name)
|
||||||
|
|
||||||
ret['comment'] = '\n'.join(comments)
|
ret['comment'] = '\n'.join(comments)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ import os.path
|
|||||||
from salt.ext import six
|
from salt.ext import six
|
||||||
import salt.utils.files
|
import salt.utils.files
|
||||||
import salt.utils.stringutils
|
import salt.utils.stringutils
|
||||||
|
from salt.utils.icinga2 import get_certs_path
|
||||||
|
|
||||||
|
|
||||||
def __virtual__():
|
def __virtual__():
|
||||||
@ -103,8 +104,9 @@ def generate_ticket(name, output=None, grain=None, key=None, overwrite=True):
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
# Executing the command.
|
# Executing the command.
|
||||||
ticket = __salt__['icinga2.generate_ticket'](name).strip()
|
ticket_res = __salt__['icinga2.generate_ticket'](name)
|
||||||
if ticket:
|
ticket = ticket_res['stdout']
|
||||||
|
if not ticket_res['retcode']:
|
||||||
ret['comment'] = six.text_type(ticket)
|
ret['comment'] = six.text_type(ticket)
|
||||||
|
|
||||||
if output == 'grain':
|
if output == 'grain':
|
||||||
@ -140,8 +142,8 @@ def generate_cert(name):
|
|||||||
'changes': {},
|
'changes': {},
|
||||||
'result': True,
|
'result': True,
|
||||||
'comment': ''}
|
'comment': ''}
|
||||||
cert = "/etc/icinga2/pki/{0}.crt".format(name)
|
cert = "{0}{1}.crt".format(get_certs_path(), name)
|
||||||
key = "/etc/icinga2/pki/{0}.key".format(name)
|
key = "{0}{1}.key".format(get_certs_path(), name)
|
||||||
|
|
||||||
# Checking if execution is needed.
|
# Checking if execution is needed.
|
||||||
if os.path.isfile(cert) and os.path.isfile(key):
|
if os.path.isfile(cert) and os.path.isfile(key):
|
||||||
@ -154,7 +156,7 @@ def generate_cert(name):
|
|||||||
|
|
||||||
# Executing the command.
|
# Executing the command.
|
||||||
cert_save = __salt__['icinga2.generate_cert'](name)
|
cert_save = __salt__['icinga2.generate_cert'](name)
|
||||||
if not cert_save:
|
if not cert_save['retcode']:
|
||||||
ret['comment'] = "Certificate and key generated"
|
ret['comment'] = "Certificate and key generated"
|
||||||
ret['changes']['cert'] = "Executed. Certificate saved: {0}".format(cert)
|
ret['changes']['cert'] = "Executed. Certificate saved: {0}".format(cert)
|
||||||
ret['changes']['key'] = "Executed. Key saved: {0}".format(key)
|
ret['changes']['key'] = "Executed. Key saved: {0}".format(key)
|
||||||
@ -175,7 +177,7 @@ def save_cert(name, master):
|
|||||||
'changes': {},
|
'changes': {},
|
||||||
'result': True,
|
'result': True,
|
||||||
'comment': ''}
|
'comment': ''}
|
||||||
cert = "/etc/icinga2/pki/trusted-master.crt"
|
cert = "{0}trusted-master.crt".format(get_certs_path())
|
||||||
|
|
||||||
# Checking if execution is needed.
|
# Checking if execution is needed.
|
||||||
if os.path.isfile(cert):
|
if os.path.isfile(cert):
|
||||||
@ -188,7 +190,7 @@ def save_cert(name, master):
|
|||||||
|
|
||||||
# Executing the command.
|
# Executing the command.
|
||||||
cert_save = __salt__['icinga2.save_cert'](name, master)
|
cert_save = __salt__['icinga2.save_cert'](name, master)
|
||||||
if not cert_save:
|
if not cert_save['retcode']:
|
||||||
ret['comment'] = "Certificate for icinga2 master saved"
|
ret['comment'] = "Certificate for icinga2 master saved"
|
||||||
ret['changes']['cert'] = "Executed. Certificate saved: {0}".format(cert)
|
ret['changes']['cert'] = "Executed. Certificate saved: {0}".format(cert)
|
||||||
return ret
|
return ret
|
||||||
@ -214,7 +216,7 @@ def request_cert(name, master, ticket, port="5665"):
|
|||||||
'changes': {},
|
'changes': {},
|
||||||
'result': True,
|
'result': True,
|
||||||
'comment': ''}
|
'comment': ''}
|
||||||
cert = "/etc/icinga2/pki/ca.crt"
|
cert = "{0}ca.crt".format(get_certs_path())
|
||||||
|
|
||||||
# Checking if execution is needed.
|
# Checking if execution is needed.
|
||||||
if os.path.isfile(cert):
|
if os.path.isfile(cert):
|
||||||
@ -227,12 +229,12 @@ def request_cert(name, master, ticket, port="5665"):
|
|||||||
|
|
||||||
# Executing the command.
|
# Executing the command.
|
||||||
cert_request = __salt__['icinga2.request_cert'](name, master, ticket, port)
|
cert_request = __salt__['icinga2.request_cert'](name, master, ticket, port)
|
||||||
if not cert_request:
|
if not cert_request['retcode']:
|
||||||
ret['comment'] = "Certificate request from icinga2 master executed"
|
ret['comment'] = "Certificate request from icinga2 master executed"
|
||||||
ret['changes']['cert'] = "Executed. Certificate requested: {0}".format(cert)
|
ret['changes']['cert'] = "Executed. Certificate requested: {0}".format(cert)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
ret['comment'] = "FAILED. Certificate requested failed with exit code: {0}".format(cert_request)
|
ret['comment'] = "FAILED. Certificate requested failed with output: {0}".format(cert_request['stdout'])
|
||||||
ret['result'] = False
|
ret['result'] = False
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
@ -254,8 +256,8 @@ def node_setup(name, master, ticket):
|
|||||||
'changes': {},
|
'changes': {},
|
||||||
'result': True,
|
'result': True,
|
||||||
'comment': ''}
|
'comment': ''}
|
||||||
cert = "/etc/icinga2/pki/{0}.crt.orig".format(name)
|
cert = "{0}{1}.crt.orig".format(get_certs_path(), name)
|
||||||
key = "/etc/icinga2/pki/{0}.key.orig".format(name)
|
key = "{0}{1}.key.orig".format(get_certs_path(), name)
|
||||||
|
|
||||||
# Checking if execution is needed.
|
# Checking if execution is needed.
|
||||||
if os.path.isfile(cert) and os.path.isfile(cert):
|
if os.path.isfile(cert) and os.path.isfile(cert):
|
||||||
@ -268,11 +270,11 @@ def node_setup(name, master, ticket):
|
|||||||
|
|
||||||
# Executing the command.
|
# Executing the command.
|
||||||
node_setup = __salt__['icinga2.node_setup'](name, master, ticket)
|
node_setup = __salt__['icinga2.node_setup'](name, master, ticket)
|
||||||
if not node_setup:
|
if not node_setup['retcode']:
|
||||||
ret['comment'] = "Node setup executed."
|
ret['comment'] = "Node setup executed."
|
||||||
ret['changes']['cert'] = "Node setup finished successfully."
|
ret['changes']['cert'] = "Node setup finished successfully."
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
ret['comment'] = "FAILED. Node setup failed with exit code: {0}".format(node_setup)
|
ret['comment'] = "FAILED. Node setup failed with outpu: {0}".format(node_setup['stdout'])
|
||||||
ret['result'] = False
|
ret['result'] = False
|
||||||
return ret
|
return ret
|
||||||
|
@ -62,20 +62,20 @@ def set_(name, key, value, setting=None, conf_file=_DEFAULT_CONF):
|
|||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
logrotate-rotate:
|
logrotate-rotate:
|
||||||
logrotate.set:
|
logrotate.set:
|
||||||
- key: rotate
|
- key: rotate
|
||||||
- value: 2
|
- value: 2
|
||||||
|
|
||||||
Example of usage specifying all available arguments:
|
Example of usage specifying all available arguments:
|
||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
logrotate-wtmp-rotate:
|
logrotate-wtmp-rotate:
|
||||||
logrotate.set:
|
logrotate.set:
|
||||||
- key: /var/log/wtmp
|
- key: /var/log/wtmp
|
||||||
- value: rotate
|
- value: rotate
|
||||||
- setting: 2
|
- setting: 2
|
||||||
- conf_file: /etc/logrotate.conf
|
- conf_file: /etc/logrotate.conf
|
||||||
'''
|
'''
|
||||||
ret = {'name': name,
|
ret = {'name': name,
|
||||||
'changes': dict(),
|
'changes': dict(),
|
||||||
|
@ -9,12 +9,30 @@ all interfaces are ignored unless specified.
|
|||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
Prior to version 2014.1.0, only RedHat-based systems (RHEL,
|
RedHat-based systems (RHEL, CentOS, Scientific, etc.)
|
||||||
CentOS, Scientific Linux, etc.) are supported. Support for Debian/Ubuntu is
|
have been supported since version 2014.1.0.
|
||||||
new in 2014.1.0 and should be considered experimental.
|
|
||||||
|
Debian-based systems (Debian, Ubuntu, etc.) have been
|
||||||
|
supported since version 2017.7.0. The following options
|
||||||
|
are not supported: ipaddr_start, and ipaddr_end.
|
||||||
|
|
||||||
Other platforms are not yet supported.
|
Other platforms are not yet supported.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
On Debian-based systems, networking configuration can be specified
|
||||||
|
in `/etc/network/interfaces` or via included files such as (by default)
|
||||||
|
`/etc/network/interfaces.d/*`. This can be problematic for configuration
|
||||||
|
management. It is recommended to use either `file.managed` *or*
|
||||||
|
`network.managed`.
|
||||||
|
|
||||||
|
If using `network.managed`, it can be useful to ensure `interfaces.d/`
|
||||||
|
is empty. This can be done using:
|
||||||
|
|
||||||
|
/etc/network/interfaces.d:
|
||||||
|
file.directory:
|
||||||
|
- clean: True
|
||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
system:
|
system:
|
||||||
@ -31,9 +49,17 @@ all interfaces are ignored unless specified.
|
|||||||
network.managed:
|
network.managed:
|
||||||
- enabled: True
|
- enabled: True
|
||||||
- type: eth
|
- type: eth
|
||||||
- proto: none
|
- proto: static
|
||||||
- ipaddr: 10.1.0.1
|
- ipaddr: 10.1.0.7
|
||||||
- netmask: 255.255.255.0
|
- netmask: 255.255.255.0
|
||||||
|
- gateway: 10.1.0.1
|
||||||
|
- enable_ipv6: true
|
||||||
|
- ipv6proto: static
|
||||||
|
- ipv6ipaddrs:
|
||||||
|
- 2001:db8:dead:beef::3/64
|
||||||
|
- 2001:db8:dead:beef::7/64
|
||||||
|
- ipv6gateway: 2001:db8:dead:beef::1
|
||||||
|
- ipv6netmask: 64
|
||||||
- dns:
|
- dns:
|
||||||
- 8.8.8.8
|
- 8.8.8.8
|
||||||
- 8.8.4.4
|
- 8.8.4.4
|
||||||
@ -121,12 +147,11 @@ all interfaces are ignored unless specified.
|
|||||||
- type: bond
|
- type: bond
|
||||||
- ipaddr: 10.1.0.1
|
- ipaddr: 10.1.0.1
|
||||||
- netmask: 255.255.255.0
|
- netmask: 255.255.255.0
|
||||||
- mode: active-backup
|
- mode: gre
|
||||||
- proto: static
|
- proto: static
|
||||||
- dns:
|
- dns:
|
||||||
- 8.8.8.8
|
- 8.8.8.8
|
||||||
- 8.8.4.4
|
- 8.8.4.4
|
||||||
- ipv6:
|
|
||||||
- enabled: False
|
- enabled: False
|
||||||
- slaves: eth2 eth3
|
- slaves: eth2 eth3
|
||||||
- require:
|
- require:
|
||||||
@ -202,6 +227,62 @@ all interfaces are ignored unless specified.
|
|||||||
- require:
|
- require:
|
||||||
- network: eth4
|
- network: eth4
|
||||||
|
|
||||||
|
eth6:
|
||||||
|
network.managed:
|
||||||
|
- type: eth
|
||||||
|
- noifupdown: True
|
||||||
|
|
||||||
|
# IPv4
|
||||||
|
- proto: static
|
||||||
|
- ipaddr: 192.168.4.9
|
||||||
|
- netmask: 255.255.255.0
|
||||||
|
- gateway: 192.168.4.1
|
||||||
|
- enable_ipv6: True
|
||||||
|
|
||||||
|
# IPv6
|
||||||
|
- ipv6proto: static
|
||||||
|
- ipv6ipaddr: 2001:db8:dead:c0::3
|
||||||
|
- ipv6netmask: 64
|
||||||
|
- ipv6gateway: 2001:db8:dead:c0::1
|
||||||
|
# override shared; makes those options v4-only
|
||||||
|
- ipv6ttl: 15
|
||||||
|
|
||||||
|
# Shared
|
||||||
|
- mtu: 1480
|
||||||
|
- ttl: 18
|
||||||
|
- dns:
|
||||||
|
- 8.8.8.8
|
||||||
|
- 8.8.4.4
|
||||||
|
|
||||||
|
eth7:
|
||||||
|
- type: eth
|
||||||
|
- proto: static
|
||||||
|
- ipaddr: 10.1.0.7
|
||||||
|
- netmask: 255.255.255.0
|
||||||
|
- gateway: 10.1.0.1
|
||||||
|
- enable_ipv6: True
|
||||||
|
- ipv6proto: static
|
||||||
|
- ipv6ipaddr: 2001:db8:dead:beef::3
|
||||||
|
- ipv6netmask: 64
|
||||||
|
- ipv6gateway: 2001:db8:dead:beef::1
|
||||||
|
- noifupdown: True
|
||||||
|
|
||||||
|
eth8:
|
||||||
|
network.managed:
|
||||||
|
- enabled: True
|
||||||
|
- type: eth
|
||||||
|
- proto: static
|
||||||
|
- enable_ipv6: true
|
||||||
|
- ipv6proto: static
|
||||||
|
- ipv6ipaddrs:
|
||||||
|
- 2001:db8:dead:beef::3/64
|
||||||
|
- 2001:db8:dead:beef::7/64
|
||||||
|
- ipv6gateway: 2001:db8:dead:beef::1
|
||||||
|
- ipv6netmask: 64
|
||||||
|
- dns:
|
||||||
|
- 8.8.8.8
|
||||||
|
- 8.8.4.4
|
||||||
|
|
||||||
system:
|
system:
|
||||||
network.system:
|
network.system:
|
||||||
- enabled: True
|
- enabled: True
|
||||||
@ -217,17 +298,11 @@ all interfaces are ignored unless specified.
|
|||||||
network.managed:
|
network.managed:
|
||||||
- name: lo
|
- name: lo
|
||||||
- type: eth
|
- type: eth
|
||||||
|
- proto: loopback
|
||||||
- onboot: yes
|
- onboot: yes
|
||||||
- userctl: no
|
- userctl: no
|
||||||
- ipv6_autoconf: no
|
- ipv6_autoconf: no
|
||||||
- enable_ipv6: true
|
- enable_ipv6: true
|
||||||
- ipaddrs:
|
|
||||||
- 127.0.0.1/8
|
|
||||||
- 10.1.0.4/32
|
|
||||||
- 10.1.0.12/32
|
|
||||||
- ipv6addrs:
|
|
||||||
- fc00::1/128
|
|
||||||
- fc00::100/128
|
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
Apply changes to hostname immediately.
|
Apply changes to hostname immediately.
|
||||||
|
@ -422,9 +422,16 @@ def running(name,
|
|||||||
else:
|
else:
|
||||||
before_toggle_enable_status = True
|
before_toggle_enable_status = True
|
||||||
|
|
||||||
|
unmask_ret = {'comment': ''}
|
||||||
|
if unmask:
|
||||||
|
unmask_ret = unmasked(name, unmask_runtime)
|
||||||
|
|
||||||
# See if the service is already running
|
# See if the service is already running
|
||||||
if before_toggle_status:
|
if before_toggle_status:
|
||||||
ret['comment'] = 'The service {0} is already running'.format(name)
|
ret['comment'] = '\n'.join(
|
||||||
|
[_f for _f in ['The service {0} is already running'.format(name),
|
||||||
|
unmask_ret['comment']] if _f]
|
||||||
|
)
|
||||||
if enable is True and not before_toggle_enable_status:
|
if enable is True and not before_toggle_enable_status:
|
||||||
ret.update(_enable(name, None, **kwargs))
|
ret.update(_enable(name, None, **kwargs))
|
||||||
elif enable is False and before_toggle_enable_status:
|
elif enable is False and before_toggle_enable_status:
|
||||||
@ -434,7 +441,9 @@ def running(name,
|
|||||||
# Run the tests
|
# Run the tests
|
||||||
if __opts__['test']:
|
if __opts__['test']:
|
||||||
ret['result'] = None
|
ret['result'] = None
|
||||||
ret['comment'] = 'Service {0} is set to start'.format(name)
|
ret['comment'] = '\n'.join(
|
||||||
|
[_f for _f in ['Service {0} is set to start'.format(name),
|
||||||
|
unmask_ret['comment']] if _f])
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
# Conditionally add systemd-specific args to call to service.start
|
# Conditionally add systemd-specific args to call to service.start
|
||||||
@ -494,6 +503,9 @@ def running(name,
|
|||||||
.format(ret['comment'], init_delay)
|
.format(ret['comment'], init_delay)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if unmask:
|
||||||
|
ret['comment'] = '\n'.join([ret['comment'], unmask_ret['comment']])
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
@ -338,6 +338,7 @@ def dead(name,
|
|||||||
else:
|
else:
|
||||||
# process name doesn't exist
|
# process name doesn't exist
|
||||||
ret['comment'] = "Service {0} doesn't exist".format(name)
|
ret['comment'] = "Service {0} doesn't exist".format(name)
|
||||||
|
return ret
|
||||||
|
|
||||||
if is_stopped is True:
|
if is_stopped is True:
|
||||||
ret['comment'] = "Service {0} is not running".format(name)
|
ret['comment'] = "Service {0} is not running".format(name)
|
||||||
|
@ -141,13 +141,13 @@ def _changes(name,
|
|||||||
change['empty_password'] = True
|
change['empty_password'] = True
|
||||||
if date is not None and lshad['lstchg'] != date:
|
if date is not None and lshad['lstchg'] != date:
|
||||||
change['date'] = date
|
change['date'] = date
|
||||||
if mindays and mindays is not 0 and lshad['min'] != mindays:
|
if mindays is not None and lshad['min'] != mindays:
|
||||||
change['mindays'] = mindays
|
change['mindays'] = mindays
|
||||||
if maxdays and maxdays is not 999999 and lshad['max'] != maxdays:
|
if maxdays is not None and lshad['max'] != maxdays:
|
||||||
change['maxdays'] = maxdays
|
change['maxdays'] = maxdays
|
||||||
if inactdays and inactdays is not 0 and lshad['inact'] != inactdays:
|
if inactdays is not None and lshad['inact'] != inactdays:
|
||||||
change['inactdays'] = inactdays
|
change['inactdays'] = inactdays
|
||||||
if warndays and warndays is not 7 and lshad['warn'] != warndays:
|
if warndays is not None and lshad['warn'] != warndays:
|
||||||
change['warndays'] = warndays
|
change['warndays'] = warndays
|
||||||
if expire and lshad['expire'] != expire:
|
if expire and lshad['expire'] != expire:
|
||||||
change['expire'] = expire
|
change['expire'] = expire
|
||||||
|
@ -252,9 +252,18 @@ def set_(name,
|
|||||||
if policy_data and policy_data['output_section'] in current_policy:
|
if policy_data and policy_data['output_section'] in current_policy:
|
||||||
for policy_name, policy_setting in six.iteritems(policy_data['requested_policy']):
|
for policy_name, policy_setting in six.iteritems(policy_data['requested_policy']):
|
||||||
currently_set = False
|
currently_set = False
|
||||||
|
# Check Case sensitive first (faster)
|
||||||
if policy_name in current_policy[policy_data['output_section']]:
|
if policy_name in current_policy[policy_data['output_section']]:
|
||||||
currently_set = True
|
currently_set = True
|
||||||
pol_id = policy_name
|
pol_id = policy_name
|
||||||
|
# Check case insensitive
|
||||||
|
elif policy_name.lower() in (k.lower() for k in current_policy[policy_data['output_section']]):
|
||||||
|
for p_name in current_policy[policy_data['output_section']]:
|
||||||
|
if policy_name.lower() == p_name.lower():
|
||||||
|
currently_set = True
|
||||||
|
pol_id = policy_name
|
||||||
|
break
|
||||||
|
# Check aliases
|
||||||
else:
|
else:
|
||||||
for alias in policy_data['policy_lookup'][policy_name]['policy_aliases']:
|
for alias in policy_data['policy_lookup'][policy_name]['policy_aliases']:
|
||||||
log.debug('checking alias %s', alias)
|
log.debug('checking alias %s', alias)
|
||||||
@ -308,13 +317,13 @@ def set_(name,
|
|||||||
policy_changes.append(policy_name)
|
policy_changes.append(policy_name)
|
||||||
else:
|
else:
|
||||||
if additional_policy_comments:
|
if additional_policy_comments:
|
||||||
ret['comment'] = '"{0}" is already set ({1}).\n'.format(policy_name, ', '.join(additional_policy_comments))
|
ret['comment'] = '"{0}" is already set ({1})\n'.format(policy_name, ', '.join(additional_policy_comments))
|
||||||
else:
|
else:
|
||||||
ret['comment'] = '"{0}" is already set.\n'.format(policy_name) + ret['comment']
|
ret['comment'] = '"{0}" is already set\n'.format(policy_name) + ret['comment']
|
||||||
else:
|
else:
|
||||||
log.debug('%s current setting matches '
|
log.debug('%s current setting matches '
|
||||||
'the requested setting', policy_name)
|
'the requested setting', policy_name)
|
||||||
ret['comment'] = '"{0}" is already set.\n'.format(policy_name) + ret['comment']
|
ret['comment'] = '"{0}" is already set\n'.format(policy_name) + ret['comment']
|
||||||
else:
|
else:
|
||||||
policy_changes.append(policy_name)
|
policy_changes.append(policy_name)
|
||||||
log.debug('policy %s is not set, we will configure it',
|
log.debug('policy %s is not set, we will configure it',
|
||||||
@ -322,7 +331,7 @@ def set_(name,
|
|||||||
if __opts__['test']:
|
if __opts__['test']:
|
||||||
if policy_changes:
|
if policy_changes:
|
||||||
ret['result'] = None
|
ret['result'] = None
|
||||||
ret['comment'] = 'The following policies are set to change:\n{0}.'.format(
|
ret['comment'] = 'The following policies are set to change:\n{0}'.format(
|
||||||
'\n'.join(policy_changes))
|
'\n'.join(policy_changes))
|
||||||
else:
|
else:
|
||||||
ret['comment'] = 'All specified policies are properly configured'
|
ret['comment'] = 'All specified policies are properly configured'
|
||||||
@ -334,13 +343,17 @@ def set_(name,
|
|||||||
adml_language=adml_language)
|
adml_language=adml_language)
|
||||||
if _ret:
|
if _ret:
|
||||||
ret['result'] = _ret
|
ret['result'] = _ret
|
||||||
ret['comment'] = 'The following policies changed:\n{0}.'.format(
|
|
||||||
'\n'.join(policy_changes))
|
|
||||||
ret['changes'] = salt.utils.dictdiffer.deep_diff(
|
ret['changes'] = salt.utils.dictdiffer.deep_diff(
|
||||||
current_policy,
|
current_policy,
|
||||||
__salt__['lgpo.get'](policy_class=policy_class,
|
__salt__['lgpo.get'](policy_class=policy_class,
|
||||||
adml_language=adml_language,
|
adml_language=adml_language,
|
||||||
hierarchical_return=False))
|
hierarchical_return=False))
|
||||||
|
if ret['changes']:
|
||||||
|
ret['comment'] = 'The following policies changed:\n{0}' \
|
||||||
|
''.format('\n'.join(policy_changes))
|
||||||
|
else:
|
||||||
|
ret['comment'] = 'The following policies are in the correct state:\n{0}' \
|
||||||
|
''.format('\n'.join(policy_changes))
|
||||||
else:
|
else:
|
||||||
ret['result'] = False
|
ret['result'] = False
|
||||||
ret['comment'] = 'Errors occurred while attempting to configure policies: {0}'.format(_ret)
|
ret['comment'] = 'Errors occurred while attempting to configure policies: {0}'.format(_ret)
|
||||||
|
115
salt/states/win_wusa.py
Normal file
115
salt/states/win_wusa.py
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
'''
|
||||||
|
Microsoft Updates (KB) Management
|
||||||
|
|
||||||
|
This module provides the ability to enforce KB installations
|
||||||
|
from files (.msu), without WSUS.
|
||||||
|
|
||||||
|
.. versionadded:: Neon
|
||||||
|
'''
|
||||||
|
|
||||||
|
# Import python libs
|
||||||
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
import logging
|
||||||
|
|
||||||
|
# Import salt libs
|
||||||
|
import salt.utils.platform
|
||||||
|
import salt.exceptions
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
# Define the module's virtual name
|
||||||
|
__virtualname__ = 'win_wusa'
|
||||||
|
|
||||||
|
|
||||||
|
def __virtual__():
|
||||||
|
'''
|
||||||
|
Load only on Windows
|
||||||
|
'''
|
||||||
|
if not salt.utils.platform.is_windows():
|
||||||
|
return False, 'Only available on Windows systems'
|
||||||
|
|
||||||
|
return __virtualname__
|
||||||
|
|
||||||
|
|
||||||
|
def installed(name, source):
|
||||||
|
'''
|
||||||
|
Enforce the installed state of a KB
|
||||||
|
|
||||||
|
name
|
||||||
|
Name of the Windows KB ("KB123456")
|
||||||
|
source
|
||||||
|
Source of .msu file corresponding to the KB
|
||||||
|
|
||||||
|
'''
|
||||||
|
ret = {
|
||||||
|
'name': name,
|
||||||
|
'changes': {},
|
||||||
|
'result': False,
|
||||||
|
'comment': '',
|
||||||
|
}
|
||||||
|
|
||||||
|
# Start with basic error-checking. Do all the passed parameters make sense
|
||||||
|
# and agree with each-other?
|
||||||
|
if not name or not source:
|
||||||
|
raise salt.exceptions.SaltInvocationError(
|
||||||
|
'Arguments "name" and "source" are mandatory.')
|
||||||
|
|
||||||
|
# Check the current state of the system. Does anything need to change?
|
||||||
|
current_state = __salt__['win_wusa.is_installed'](name)
|
||||||
|
|
||||||
|
if current_state:
|
||||||
|
ret['result'] = True
|
||||||
|
ret['comment'] = 'KB already installed'
|
||||||
|
return ret
|
||||||
|
|
||||||
|
# The state of the system does need to be changed. Check if we're running
|
||||||
|
# in ``test=true`` mode.
|
||||||
|
if __opts__['test'] is True:
|
||||||
|
ret['comment'] = 'The KB "{0}" will be installed.'.format(name)
|
||||||
|
ret['changes'] = {
|
||||||
|
'old': current_state,
|
||||||
|
'new': True,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Return ``None`` when running with ``test=true``.
|
||||||
|
ret['result'] = None
|
||||||
|
|
||||||
|
return ret
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = __states__['file.cached'](source,
|
||||||
|
skip_verify=True,
|
||||||
|
saltenv=__env__)
|
||||||
|
except Exception as exc:
|
||||||
|
msg = 'Failed to cache {0}: {1}'.format(
|
||||||
|
salt.utils.url.redact_http_basic_auth(source),
|
||||||
|
exc.__str__())
|
||||||
|
log.exception(msg)
|
||||||
|
ret['comment'] = msg
|
||||||
|
return ret
|
||||||
|
|
||||||
|
if result['result']:
|
||||||
|
# Get the path of the file in the minion cache
|
||||||
|
cached = __salt__['cp.is_cached'](source, saltenv=__env__)
|
||||||
|
else:
|
||||||
|
log.debug(
|
||||||
|
'failed to download %s',
|
||||||
|
salt.utils.url.redact_http_basic_auth(source)
|
||||||
|
)
|
||||||
|
return result
|
||||||
|
|
||||||
|
# Finally, make the actual change and return the result.
|
||||||
|
new_state = __salt__['win_wusa.install'](cached)
|
||||||
|
|
||||||
|
ret['comment'] = 'The KB "{0}" was installed!'.format(name)
|
||||||
|
|
||||||
|
ret['changes'] = {
|
||||||
|
'old': current_state,
|
||||||
|
'new': new_state,
|
||||||
|
}
|
||||||
|
|
||||||
|
ret['result'] = True
|
||||||
|
|
||||||
|
return ret
|
@ -127,7 +127,6 @@ log = logging.getLogger(__name__)
|
|||||||
|
|
||||||
__func_alias__ = {
|
__func_alias__ = {
|
||||||
'import_': 'import',
|
'import_': 'import',
|
||||||
'export_': 'export',
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Define the state's virtual name
|
# Define the state's virtual name
|
||||||
@ -623,7 +622,7 @@ def halted(name, graceful=True):
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def export_(name, path, replace=False):
|
def export(name, path, replace=False):
|
||||||
'''
|
'''
|
||||||
Export a zones configuration
|
Export a zones configuration
|
||||||
|
|
||||||
@ -760,6 +759,11 @@ def import_(name, path, mode='import', nodataset=False, brand_opts=None):
|
|||||||
``install``: will import and then try to install the zone
|
``install``: will import and then try to install the zone
|
||||||
``attach``: will import and then try to attach of the zone
|
``attach``: will import and then try to attach of the zone
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
omipkg1:
|
||||||
|
zone.import:
|
||||||
|
- path: /foo/bar/baz
|
||||||
'''
|
'''
|
||||||
ret = {'name': name,
|
ret = {'name': name,
|
||||||
'changes': {},
|
'changes': {},
|
||||||
|
@ -5,7 +5,8 @@
|
|||||||
{% if interface.hwaddress %} hwaddress {{interface.hwaddress}}
|
{% if interface.hwaddress %} hwaddress {{interface.hwaddress}}
|
||||||
{%endif%}{% if interface.vlan_raw_device %} vlan-raw-device {{interface.vlan_raw_device}}
|
{%endif%}{% if interface.vlan_raw_device %} vlan-raw-device {{interface.vlan_raw_device}}
|
||||||
{%endif%}{% if interface.address %} address {{interface.address}}
|
{%endif%}{% if interface.address %} address {{interface.address}}
|
||||||
{%endif%}{% if interface.netmask %} netmask {{interface.netmask}}
|
{%endif%}{% if interface.addresses %}{%for addr in interface.addresses %} address {{addr}}
|
||||||
|
{%endfor%}{%endif%}{% if interface.netmask %} netmask {{interface.netmask}}
|
||||||
{%endif%}{% if interface.broadcast %} broadcast {{interface.broadcast}}
|
{%endif%}{% if interface.broadcast %} broadcast {{interface.broadcast}}
|
||||||
{%endif%}{% if interface.metric %} metric {{interface.metric}}
|
{%endif%}{% if interface.metric %} metric {{interface.metric}}
|
||||||
{%endif%}{% if interface.gateway %} gateway {{interface.gateway}}
|
{%endif%}{% if interface.gateway %} gateway {{interface.gateway}}
|
||||||
@ -30,8 +31,12 @@
|
|||||||
{%endif%}{% if interface.unit %} unit {{interface.unit}}
|
{%endif%}{% if interface.unit %} unit {{interface.unit}}
|
||||||
{%endif%}{% if interface.options %} options {{interface.options}}
|
{%endif%}{% if interface.options %} options {{interface.options}}
|
||||||
{%endif%}{% if interface.master %} bond-master {{interface.master}}
|
{%endif%}{% if interface.master %} bond-master {{interface.master}}
|
||||||
{%endif%}{% if interface.dns_nameservers %} dns-nameservers {%for item in interface.dns_nameservers %}{{item}} {%endfor%}
|
{%endif%}{% if interface.dns_nameservers %} dns-nameservers {%
|
||||||
{%endif%}{% if interface.dns_search %} dns-search {% for item in interface.dns_search %}{{item}} {%endfor%}
|
if interface.dns_nameservers is string %}{{ interface.dns_nameservers }}{%
|
||||||
|
else %}{{ interface.dns_nameservers|join(" ") }}{% endif %}
|
||||||
|
{%endif%}{% if interface.dns_search %} dns-search {%
|
||||||
|
if interface.dns_search is string %}{{interface.dns_search }}{%
|
||||||
|
else %}{{ interface.dns_search|join(" ") }}{% endif %}
|
||||||
{%endif%}{% if interface.ethtool %}{%for item in interface.ethtool_keys %} {{item}} {{interface.ethtool[item]}}
|
{%endif%}{% if interface.ethtool %}{%for item in interface.ethtool_keys %} {{item}} {{interface.ethtool[item]}}
|
||||||
{%endfor%}{%endif%}{% if interface.bonding %}{%for item in interface.bonding_keys %} bond-{{item}} {{interface.bonding[item]}}
|
{%endfor%}{%endif%}{% if interface.bonding %}{%for item in interface.bonding_keys %} bond-{{item}} {{interface.bonding[item]}}
|
||||||
{%endfor%}{%endif%}{% if interface.bridging %}{%for item in interface.bridging_keys %} bridge_{{item}} {{interface.bridging[item]}}
|
{%endfor%}{%endif%}{% if interface.bridging %}{%for item in interface.bridging_keys %} bridge_{{item}} {{interface.bridging[item]}}
|
||||||
@ -69,7 +74,8 @@
|
|||||||
{%endif%}{% if interface.dhcp %} dhcp {{interface.dhcp}}{# END V6ONLOPTS #}
|
{%endif%}{% if interface.dhcp %} dhcp {{interface.dhcp}}{# END V6ONLOPTS #}
|
||||||
{%endif%}{% if interface.vlan_raw_device %} vlan-raw-device {{interface.vlan_raw_device}}
|
{%endif%}{% if interface.vlan_raw_device %} vlan-raw-device {{interface.vlan_raw_device}}
|
||||||
{%endif%}{% if interface.address %} address {{interface.address}}
|
{%endif%}{% if interface.address %} address {{interface.address}}
|
||||||
{%endif%}{% if interface.netmask %} netmask {{interface.netmask}}
|
{%endif%}{% if interface.addresses %}{% for addr in interface.addresses %} address {{addr}}
|
||||||
|
{%endfor%}{%endif%}{% if interface.netmask %} netmask {{interface.netmask}}
|
||||||
{%endif%}{% if interface.broadcast %} broadcast {{interface.broadcast}}
|
{%endif%}{% if interface.broadcast %} broadcast {{interface.broadcast}}
|
||||||
{%endif%}{% if interface.metric %} metric {{interface.metric}}
|
{%endif%}{% if interface.metric %} metric {{interface.metric}}
|
||||||
{%endif%}{% if interface.gateway %} gateway {{interface.gateway}}
|
{%endif%}{% if interface.gateway %} gateway {{interface.gateway}}
|
||||||
@ -94,8 +100,12 @@
|
|||||||
{%endif%}{% if interface.unit %} unit {{interface.unit}}
|
{%endif%}{% if interface.unit %} unit {{interface.unit}}
|
||||||
{%endif%}{% if interface.options %} options {{interface.options}}
|
{%endif%}{% if interface.options %} options {{interface.options}}
|
||||||
{%endif%}{% if interface.master %} bond-master {{interface.master}}
|
{%endif%}{% if interface.master %} bond-master {{interface.master}}
|
||||||
{%endif%}{% if interface.dns_nameservers %} dns-nameservers {%for item in interface.dns_nameservers %}{{item}} {%endfor%}
|
{%endif%}{% if interface.dns_nameservers %} dns-nameservers {%
|
||||||
{%endif%}{% if interface.dns_search %} dns-search {% for item in interface.dns_search %}{{item}} {%endfor%}
|
if interface.dns_nameservers is string %}{{ interface.dns_nameservers }}{%
|
||||||
|
else %}{{ interface.dns_nameservers|join(" ") }}{% endif %}
|
||||||
|
{%endif%}{% if interface.dns_search %} dns-search {%
|
||||||
|
if interface.dns_search is string %}{{interface.dns_search }}{%
|
||||||
|
else %}{{ interface.dns_search|join(" ") }}{% endif %}
|
||||||
{%endif%}{% if interface.ethtool %}{%for item in interface.ethtool_keys %} {{item}} {{interface.ethtool[item]}}
|
{%endif%}{% if interface.ethtool %}{%for item in interface.ethtool_keys %} {{item}} {{interface.ethtool[item]}}
|
||||||
{%endfor%}{%endif%}{% if interface.bonding %}{%for item in interface.bonding_keys %} bond-{{item}} {{interface.bonding[item]}}
|
{%endfor%}{%endif%}{% if interface.bonding %}{%for item in interface.bonding_keys %} bond-{{item}} {{interface.bonding[item]}}
|
||||||
{%endfor%}{%endif%}{% if interface.bridging %}{%for item in interface.bridging_keys %} bridge_{{item}} {{interface.bridging[item]}}
|
{%endfor%}{%endif%}{% if interface.bridging %}{%for item in interface.bridging_keys %} bridge_{{item}} {{interface.bridging[item]}}
|
||||||
|
@ -108,7 +108,7 @@ Saltclass Examples
|
|||||||
|
|
||||||
``<saltclass_path>/nodes/lausanne/qls.node1.yml``
|
``<saltclass_path>/nodes/lausanne/qls.node1.yml``
|
||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: jinja
|
||||||
|
|
||||||
environment: base
|
environment: base
|
||||||
|
|
||||||
@ -228,19 +228,20 @@ def __virtual__():
|
|||||||
|
|
||||||
def top(**kwargs):
|
def top(**kwargs):
|
||||||
'''
|
'''
|
||||||
Node definitions path will be retrieved from __opts__ - or set to default -
|
Compile tops
|
||||||
then added to 'salt_data' dict that is passed to the 'get_tops' function.
|
|
||||||
'salt_data' dict is a convenient way to pass all the required datas to the function
|
|
||||||
It contains:
|
|
||||||
- __opts__
|
|
||||||
- empty __salt__
|
|
||||||
- __grains__
|
|
||||||
- empty __pillar__
|
|
||||||
- minion_id
|
|
||||||
- path
|
|
||||||
|
|
||||||
If successfull the function will return a top dict for minion_id
|
|
||||||
'''
|
'''
|
||||||
|
# Node definitions path will be retrieved from args (or set to default),
|
||||||
|
# then added to 'salt_data' dict that is passed to the 'get_pillars'
|
||||||
|
# function. The dictionary contains:
|
||||||
|
# - __opts__
|
||||||
|
# - __salt__
|
||||||
|
# - __grains__
|
||||||
|
# - __pillar__
|
||||||
|
# - minion_id
|
||||||
|
# - path
|
||||||
|
#
|
||||||
|
# If successful, the function will return a pillar dict for minion_id.
|
||||||
|
|
||||||
# If path has not been set, make a default
|
# If path has not been set, make a default
|
||||||
_opts = __opts__['master_tops']['saltclass']
|
_opts = __opts__['master_tops']['saltclass']
|
||||||
if 'path' not in _opts:
|
if 'path' not in _opts:
|
||||||
|
@ -13,6 +13,7 @@ import signal
|
|||||||
import hashlib
|
import hashlib
|
||||||
import logging
|
import logging
|
||||||
import weakref
|
import weakref
|
||||||
|
import threading
|
||||||
from random import randint
|
from random import randint
|
||||||
|
|
||||||
# Import Salt Libs
|
# Import Salt Libs
|
||||||
@ -760,6 +761,9 @@ class ZeroMQPubServerChannel(salt.transport.server.PubServerChannel):
|
|||||||
'''
|
'''
|
||||||
Encapsulate synchronous operations for a publisher channel
|
Encapsulate synchronous operations for a publisher channel
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
_sock_data = threading.local()
|
||||||
|
|
||||||
def __init__(self, opts):
|
def __init__(self, opts):
|
||||||
self.opts = opts
|
self.opts = opts
|
||||||
self.serial = salt.payload.Serial(self.opts) # TODO: in init?
|
self.serial = salt.payload.Serial(self.opts) # TODO: in init?
|
||||||
@ -795,9 +799,11 @@ class ZeroMQPubServerChannel(salt.transport.server.PubServerChannel):
|
|||||||
# IPv6 sockets work for both IPv6 and IPv4 addresses
|
# IPv6 sockets work for both IPv6 and IPv4 addresses
|
||||||
pub_sock.setsockopt(zmq.IPV4ONLY, 0)
|
pub_sock.setsockopt(zmq.IPV4ONLY, 0)
|
||||||
pub_sock.setsockopt(zmq.BACKLOG, self.opts.get('zmq_backlog', 1000))
|
pub_sock.setsockopt(zmq.BACKLOG, self.opts.get('zmq_backlog', 1000))
|
||||||
|
pub_sock.setsockopt(zmq.LINGER, -1)
|
||||||
pub_uri = 'tcp://{interface}:{publish_port}'.format(**self.opts)
|
pub_uri = 'tcp://{interface}:{publish_port}'.format(**self.opts)
|
||||||
# Prepare minion pull socket
|
# Prepare minion pull socket
|
||||||
pull_sock = context.socket(zmq.PULL)
|
pull_sock = context.socket(zmq.PULL)
|
||||||
|
pull_sock.setsockopt(zmq.LINGER, -1)
|
||||||
|
|
||||||
if self.opts.get('ipc_mode', '') == 'tcp':
|
if self.opts.get('ipc_mode', '') == 'tcp':
|
||||||
pull_uri = 'tcp://127.0.0.1:{0}'.format(
|
pull_uri = 'tcp://127.0.0.1:{0}'.format(
|
||||||
@ -860,15 +866,14 @@ class ZeroMQPubServerChannel(salt.transport.server.PubServerChannel):
|
|||||||
raise exc
|
raise exc
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
# Cleanly close the sockets if we're shutting down
|
log.trace('Publish daemon caught Keyboard interupt, tearing down')
|
||||||
if pub_sock.closed is False:
|
# Cleanly close the sockets if we're shutting down
|
||||||
pub_sock.setsockopt(zmq.LINGER, 1)
|
if pub_sock.closed is False:
|
||||||
pub_sock.close()
|
pub_sock.close()
|
||||||
if pull_sock.closed is False:
|
if pull_sock.closed is False:
|
||||||
pull_sock.setsockopt(zmq.LINGER, 1)
|
pull_sock.close()
|
||||||
pull_sock.close()
|
if context.closed is False:
|
||||||
if context.closed is False:
|
context.term()
|
||||||
context.term()
|
|
||||||
|
|
||||||
def pre_fork(self, process_manager, kwargs=None):
|
def pre_fork(self, process_manager, kwargs=None):
|
||||||
'''
|
'''
|
||||||
@ -880,23 +885,29 @@ class ZeroMQPubServerChannel(salt.transport.server.PubServerChannel):
|
|||||||
'''
|
'''
|
||||||
process_manager.add_process(self._publish_daemon, kwargs=kwargs)
|
process_manager.add_process(self._publish_daemon, kwargs=kwargs)
|
||||||
|
|
||||||
def publish(self, load):
|
@property
|
||||||
|
def pub_sock(self):
|
||||||
'''
|
'''
|
||||||
Publish "load" to minions
|
This thread's zmq publisher socket. This socket is stored on the class
|
||||||
|
so that multiple instantiations in the same thread will re-use a single
|
||||||
:param dict load: A load to be sent across the wire to minions
|
zmq socket.
|
||||||
'''
|
'''
|
||||||
payload = {'enc': 'aes'}
|
try:
|
||||||
|
return self._sock_data.sock
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
crypticle = salt.crypt.Crypticle(self.opts, salt.master.SMaster.secrets['aes']['secret'].value)
|
def pub_connect(self):
|
||||||
payload['load'] = crypticle.dumps(load)
|
'''
|
||||||
if self.opts['sign_pub_messages']:
|
Create and connect this thread's zmq socket. If a publisher socket
|
||||||
master_pem_path = os.path.join(self.opts['pki_dir'], 'master.pem')
|
already exists "pub_close" is called before creating and connecting a
|
||||||
log.debug("Signing data packet")
|
new socket.
|
||||||
payload['sig'] = salt.crypt.sign_message(master_pem_path, payload['load'])
|
'''
|
||||||
# Send 0MQ to the publisher
|
if self.pub_sock:
|
||||||
context = zmq.Context(1)
|
self.pub_close()
|
||||||
pub_sock = context.socket(zmq.PUSH)
|
ctx = zmq.Context.instance()
|
||||||
|
self._sock_data.sock = ctx.socket(zmq.PUSH)
|
||||||
|
self.pub_sock.setsockopt(zmq.LINGER, -1)
|
||||||
if self.opts.get('ipc_mode', '') == 'tcp':
|
if self.opts.get('ipc_mode', '') == 'tcp':
|
||||||
pull_uri = 'tcp://127.0.0.1:{0}'.format(
|
pull_uri = 'tcp://127.0.0.1:{0}'.format(
|
||||||
self.opts.get('tcp_master_publish_pull', 4514)
|
self.opts.get('tcp_master_publish_pull', 4514)
|
||||||
@ -905,7 +916,33 @@ class ZeroMQPubServerChannel(salt.transport.server.PubServerChannel):
|
|||||||
pull_uri = 'ipc://{0}'.format(
|
pull_uri = 'ipc://{0}'.format(
|
||||||
os.path.join(self.opts['sock_dir'], 'publish_pull.ipc')
|
os.path.join(self.opts['sock_dir'], 'publish_pull.ipc')
|
||||||
)
|
)
|
||||||
pub_sock.connect(pull_uri)
|
log.debug("Connecting to pub server: %s", pull_uri)
|
||||||
|
self.pub_sock.connect(pull_uri)
|
||||||
|
return self._sock_data.sock
|
||||||
|
|
||||||
|
def pub_close(self):
|
||||||
|
'''
|
||||||
|
Disconnect an existing publisher socket and remove it from the local
|
||||||
|
thread's cache.
|
||||||
|
'''
|
||||||
|
if hasattr(self._sock_data, 'sock'):
|
||||||
|
self._sock_data.sock.close()
|
||||||
|
delattr(self._sock_data, 'sock')
|
||||||
|
|
||||||
|
def publish(self, load):
|
||||||
|
'''
|
||||||
|
Publish "load" to minions. This send the load to the publisher daemon
|
||||||
|
process with does the actual sending to minions.
|
||||||
|
|
||||||
|
:param dict load: A load to be sent across the wire to minions
|
||||||
|
'''
|
||||||
|
payload = {'enc': 'aes'}
|
||||||
|
crypticle = salt.crypt.Crypticle(self.opts, salt.master.SMaster.secrets['aes']['secret'].value)
|
||||||
|
payload['load'] = crypticle.dumps(load)
|
||||||
|
if self.opts['sign_pub_messages']:
|
||||||
|
master_pem_path = os.path.join(self.opts['pki_dir'], 'master.pem')
|
||||||
|
log.debug("Signing data packet")
|
||||||
|
payload['sig'] = salt.crypt.sign_message(master_pem_path, payload['load'])
|
||||||
int_payload = {'payload': self.serial.dumps(payload)}
|
int_payload = {'payload': self.serial.dumps(payload)}
|
||||||
|
|
||||||
# add some targeting stuff for lists only (for now)
|
# add some targeting stuff for lists only (for now)
|
||||||
@ -928,12 +965,11 @@ class ZeroMQPubServerChannel(salt.transport.server.PubServerChannel):
|
|||||||
'Sending payload to publish daemon. jid=%s size=%d',
|
'Sending payload to publish daemon. jid=%s size=%d',
|
||||||
load.get('jid', None), len(payload),
|
load.get('jid', None), len(payload),
|
||||||
)
|
)
|
||||||
pub_sock.send(payload)
|
if not self.pub_sock:
|
||||||
|
self.pub_connect()
|
||||||
|
self.pub_sock.send(payload)
|
||||||
log.debug('Sent payload to publish daemon.')
|
log.debug('Sent payload to publish daemon.')
|
||||||
|
|
||||||
pub_sock.close()
|
|
||||||
context.term()
|
|
||||||
|
|
||||||
|
|
||||||
class AsyncReqMessageClientPool(salt.transport.MessageClientPool):
|
class AsyncReqMessageClientPool(salt.transport.MessageClientPool):
|
||||||
'''
|
'''
|
||||||
|
@ -5,9 +5,16 @@ Decorators for salt.state
|
|||||||
:codeauthor: :email:`Bo Maryniuk (bo@suse.de)`
|
:codeauthor: :email:`Bo Maryniuk (bo@suse.de)`
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
# Import Python libs
|
||||||
from __future__ import absolute_import, unicode_literals
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
import logging
|
||||||
|
|
||||||
|
# Import salt libs
|
||||||
|
import salt.utils.stringutils
|
||||||
from salt.exceptions import SaltException
|
from salt.exceptions import SaltException
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class OutputUnifier(object):
|
class OutputUnifier(object):
|
||||||
def __init__(self, *policies):
|
def __init__(self, *policies):
|
||||||
@ -24,12 +31,14 @@ class OutputUnifier(object):
|
|||||||
for pls in self.policies:
|
for pls in self.policies:
|
||||||
try:
|
try:
|
||||||
result = pls(result)
|
result = pls(result)
|
||||||
except Exception as ex:
|
except Exception as exc:
|
||||||
|
log.debug('An exception occurred in this state: %s', exc,
|
||||||
|
exc_info_on_loglevel=logging.DEBUG)
|
||||||
result = {
|
result = {
|
||||||
'result': False,
|
'result': False,
|
||||||
'name': 'later',
|
'name': 'later',
|
||||||
'changes': {},
|
'changes': {},
|
||||||
'comment': 'An exception occurred in this state: {0}'.format(ex)
|
'comment': 'An exception occurred in this state: {0}'.format(exc)
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
return _func
|
return _func
|
||||||
@ -75,7 +84,9 @@ class OutputUnifier(object):
|
|||||||
:return:
|
:return:
|
||||||
'''
|
'''
|
||||||
if isinstance(result.get('comment'), list):
|
if isinstance(result.get('comment'), list):
|
||||||
result['comment'] = '\n'.join([str(elm) for elm in result['comment']])
|
result['comment'] = u'\n'.join([
|
||||||
|
salt.utils.stringutils.to_unicode(elm) for elm in result['comment']
|
||||||
|
])
|
||||||
if result.get('result') is not None:
|
if result.get('result') is not None:
|
||||||
result['result'] = bool(result['result'])
|
result['result'] = bool(result['result'])
|
||||||
|
|
||||||
|
@ -15,8 +15,7 @@ except ImportError:
|
|||||||
# Import 3rd-party libs
|
# Import 3rd-party libs
|
||||||
import copy
|
import copy
|
||||||
import logging
|
import logging
|
||||||
from salt.ext import six
|
import salt.ext.six as six
|
||||||
from salt.serializers.yamlex import merge_recursive as _yamlex_merge_recursive
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -94,6 +93,7 @@ def merge_recurse(obj_a, obj_b, merge_lists=False):
|
|||||||
|
|
||||||
|
|
||||||
def merge_aggregate(obj_a, obj_b):
|
def merge_aggregate(obj_a, obj_b):
|
||||||
|
from salt.serializers.yamlex import merge_recursive as _yamlex_merge_recursive
|
||||||
return _yamlex_merge_recursive(obj_a, obj_b, level=1)
|
return _yamlex_merge_recursive(obj_a, obj_b, level=1)
|
||||||
|
|
||||||
|
|
||||||
|
@ -205,10 +205,22 @@ def rename(src, dst):
|
|||||||
os.rename(src, dst)
|
os.rename(src, dst)
|
||||||
|
|
||||||
|
|
||||||
def process_read_exception(exc, path):
|
def process_read_exception(exc, path, ignore=None):
|
||||||
'''
|
'''
|
||||||
Common code for raising exceptions when reading a file fails
|
Common code for raising exceptions when reading a file fails
|
||||||
|
|
||||||
|
The ignore argument can be an iterable of integer error codes (or a single
|
||||||
|
integer error code) that should be ignored.
|
||||||
'''
|
'''
|
||||||
|
if ignore is not None:
|
||||||
|
if isinstance(ignore, six.integer_types):
|
||||||
|
ignore = (ignore,)
|
||||||
|
else:
|
||||||
|
ignore = ()
|
||||||
|
|
||||||
|
if exc.errno in ignore:
|
||||||
|
return
|
||||||
|
|
||||||
if exc.errno == errno.ENOENT:
|
if exc.errno == errno.ENOENT:
|
||||||
raise CommandExecutionError('{0} does not exist'.format(path))
|
raise CommandExecutionError('{0} does not exist'.format(path))
|
||||||
elif exc.errno == errno.EACCES:
|
elif exc.errno == errno.EACCES:
|
||||||
|
@ -584,10 +584,20 @@ class GitProvider(object):
|
|||||||
'''
|
'''
|
||||||
cleaned = []
|
cleaned = []
|
||||||
cmd_str = 'git remote prune origin'
|
cmd_str = 'git remote prune origin'
|
||||||
|
|
||||||
|
# Attempt to force all output to plain ascii english, which is what some parsing code
|
||||||
|
# may expect.
|
||||||
|
# According to stackoverflow (http://goo.gl/l74GC8), we are setting LANGUAGE as well
|
||||||
|
# just to be sure.
|
||||||
|
env = os.environ.copy()
|
||||||
|
env[b"LANGUAGE"] = b"C"
|
||||||
|
env[b"LC_ALL"] = b"C"
|
||||||
|
|
||||||
cmd = subprocess.Popen(
|
cmd = subprocess.Popen(
|
||||||
shlex.split(cmd_str),
|
shlex.split(cmd_str),
|
||||||
close_fds=not salt.utils.platform.is_windows(),
|
close_fds=not salt.utils.platform.is_windows(),
|
||||||
cwd=os.path.dirname(self.gitdir),
|
cwd=os.path.dirname(self.gitdir),
|
||||||
|
env=env,
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
stderr=subprocess.STDOUT)
|
stderr=subprocess.STDOUT)
|
||||||
output = cmd.communicate()[0]
|
output = cmd.communicate()[0]
|
||||||
@ -984,6 +994,11 @@ class GitProvider(object):
|
|||||||
Resolve dynamically-set branch
|
Resolve dynamically-set branch
|
||||||
'''
|
'''
|
||||||
if self.role == 'git_pillar' and self.branch == '__env__':
|
if self.role == 'git_pillar' and self.branch == '__env__':
|
||||||
|
try:
|
||||||
|
return self.all_saltenvs
|
||||||
|
except AttributeError:
|
||||||
|
# all_saltenvs not configured for this remote
|
||||||
|
pass
|
||||||
target = self.opts.get('pillarenv') \
|
target = self.opts.get('pillarenv') \
|
||||||
or self.opts.get('saltenv') \
|
or self.opts.get('saltenv') \
|
||||||
or 'base'
|
or 'base'
|
||||||
@ -1598,6 +1613,10 @@ class Pygit2(GitProvider):
|
|||||||
will let the calling function know whether or not a new repo was
|
will let the calling function know whether or not a new repo was
|
||||||
initialized by this function.
|
initialized by this function.
|
||||||
'''
|
'''
|
||||||
|
# https://github.com/libgit2/pygit2/issues/339
|
||||||
|
# https://github.com/libgit2/libgit2/issues/2122
|
||||||
|
home = os.path.expanduser('~')
|
||||||
|
pygit2.settings.search_path[pygit2.GIT_CONFIG_LEVEL_GLOBAL] = home
|
||||||
new = False
|
new = False
|
||||||
if not os.listdir(self.cachedir):
|
if not os.listdir(self.cachedir):
|
||||||
# Repo cachedir is empty, initialize a new repo there
|
# Repo cachedir is empty, initialize a new repo there
|
||||||
@ -1606,17 +1625,7 @@ class Pygit2(GitProvider):
|
|||||||
else:
|
else:
|
||||||
# Repo cachedir exists, try to attach
|
# Repo cachedir exists, try to attach
|
||||||
try:
|
try:
|
||||||
try:
|
self.repo = pygit2.Repository(self.cachedir)
|
||||||
self.repo = pygit2.Repository(self.cachedir)
|
|
||||||
except GitError as exc:
|
|
||||||
import pwd
|
|
||||||
# https://github.com/libgit2/pygit2/issues/339
|
|
||||||
# https://github.com/libgit2/libgit2/issues/2122
|
|
||||||
if "Error stat'ing config file" not in six.text_type(exc):
|
|
||||||
raise
|
|
||||||
home = pwd.getpwnam(salt.utils.user.get_user()).pw_dir
|
|
||||||
pygit2.settings.search_path[pygit2.GIT_CONFIG_LEVEL_GLOBAL] = home
|
|
||||||
self.repo = pygit2.Repository(self.cachedir)
|
|
||||||
except KeyError:
|
except KeyError:
|
||||||
log.error(_INVALID_REPO, self.cachedir, self.url, self.role)
|
log.error(_INVALID_REPO, self.cachedir, self.url, self.role)
|
||||||
return new
|
return new
|
||||||
@ -2977,7 +2986,11 @@ class GitPillar(GitBase):
|
|||||||
cachedir = self.do_checkout(repo)
|
cachedir = self.do_checkout(repo)
|
||||||
if cachedir is not None:
|
if cachedir is not None:
|
||||||
# Figure out which environment this remote should be assigned
|
# Figure out which environment this remote should be assigned
|
||||||
if repo.env:
|
if repo.branch == '__env__' and hasattr(repo, 'all_saltenvs'):
|
||||||
|
env = self.opts.get('pillarenv') \
|
||||||
|
or self.opts.get('saltenv') \
|
||||||
|
or self.opts.get('git_pillar_base')
|
||||||
|
elif repo.env:
|
||||||
env = repo.env
|
env = repo.env
|
||||||
else:
|
else:
|
||||||
env = 'base' if repo.branch == repo.base else repo.get_checkout_target()
|
env = 'base' if repo.branch == repo.base else repo.get_checkout_target()
|
||||||
@ -2994,6 +3007,7 @@ class GitPillar(GitBase):
|
|||||||
points at the correct path
|
points at the correct path
|
||||||
'''
|
'''
|
||||||
lcachelink = salt.utils.path.join(repo.linkdir, repo._mountpoint)
|
lcachelink = salt.utils.path.join(repo.linkdir, repo._mountpoint)
|
||||||
|
lcachedest = salt.utils.path.join(repo.cachedir, repo.root()).rstrip(os.sep)
|
||||||
wipe_linkdir = False
|
wipe_linkdir = False
|
||||||
create_link = False
|
create_link = False
|
||||||
try:
|
try:
|
||||||
@ -3027,11 +3041,11 @@ class GitPillar(GitBase):
|
|||||||
)
|
)
|
||||||
wipe_linkdir = True
|
wipe_linkdir = True
|
||||||
else:
|
else:
|
||||||
if ldest != repo.cachedir:
|
if ldest != lcachedest:
|
||||||
log.debug(
|
log.debug(
|
||||||
'Destination of %s (%s) does not match '
|
'Destination of %s (%s) does not match '
|
||||||
'the expected value (%s)',
|
'the expected value (%s)',
|
||||||
lcachelink, ldest, repo.cachedir
|
lcachelink, ldest, lcachedest
|
||||||
)
|
)
|
||||||
# Since we know that the parent dirs of the
|
# Since we know that the parent dirs of the
|
||||||
# link are set up properly, all we need to do
|
# link are set up properly, all we need to do
|
||||||
@ -3076,16 +3090,16 @@ class GitPillar(GitBase):
|
|||||||
|
|
||||||
if create_link:
|
if create_link:
|
||||||
try:
|
try:
|
||||||
os.symlink(repo.cachedir, lcachelink)
|
os.symlink(lcachedest, lcachelink)
|
||||||
log.debug(
|
log.debug(
|
||||||
'Successfully linked %s to cachedir %s',
|
'Successfully linked %s to cachedir %s',
|
||||||
lcachelink, repo.cachedir
|
lcachelink, lcachedest
|
||||||
)
|
)
|
||||||
return True
|
return True
|
||||||
except OSError as exc:
|
except OSError as exc:
|
||||||
log.error(
|
log.error(
|
||||||
'Failed to create symlink to %s at path %s: %s',
|
'Failed to create symlink to %s at path %s: %s',
|
||||||
repo.cachedir, lcachelink, exc.__str__()
|
lcachedest, lcachelink, exc.__str__()
|
||||||
)
|
)
|
||||||
return False
|
return False
|
||||||
except GitLockError:
|
except GitLockError:
|
||||||
|
28
salt/utils/icinga2.py
Normal file
28
salt/utils/icinga2.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
'''
|
||||||
|
Icinga2 Common Utils
|
||||||
|
=================
|
||||||
|
|
||||||
|
This module provides common functionality for icinga2 module and state.
|
||||||
|
|
||||||
|
.. versionadded:: 2018.8.3
|
||||||
|
'''
|
||||||
|
# Import python libs
|
||||||
|
from __future__ import absolute_import, print_function, unicode_literals
|
||||||
|
import logging
|
||||||
|
import re
|
||||||
|
|
||||||
|
# Import Salt libs
|
||||||
|
import salt.utils.path
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def get_certs_path():
|
||||||
|
icinga2_output = __salt__['cmd.run_all']([salt.utils.path.which('icinga2'), "--version"], python_shell=False)
|
||||||
|
version = re.search(r'r\d+\.\d+', icinga2_output['stdout']).group(0)
|
||||||
|
# Return new certs path for icinga2 >= 2.8
|
||||||
|
if int(version.split('.')[1]) >= 8:
|
||||||
|
return '/var/lib/icinga2/certs/'
|
||||||
|
# Keep backwords compatibility with older icinga2
|
||||||
|
return '/etc/icinga2/pki/'
|
@ -103,10 +103,12 @@ def store_job(opts, load, event=None, mminion=None):
|
|||||||
log.error(emsg)
|
log.error(emsg)
|
||||||
raise KeyError(emsg)
|
raise KeyError(emsg)
|
||||||
|
|
||||||
try:
|
if job_cache != 'local_cache':
|
||||||
mminion.returners[savefstr](load['jid'], load)
|
try:
|
||||||
except KeyError as e:
|
mminion.returners[savefstr](load['jid'], load)
|
||||||
log.error("Load does not contain 'jid': %s", e)
|
except KeyError as e:
|
||||||
|
log.error("Load does not contain 'jid': %s", e)
|
||||||
|
|
||||||
mminion.returners[fstr](load)
|
mminion.returners[fstr](load)
|
||||||
|
|
||||||
if (opts.get('job_cache_store_endtime')
|
if (opts.get('job_cache_store_endtime')
|
||||||
|
@ -11,6 +11,7 @@ import subprocess
|
|||||||
import os
|
import os
|
||||||
import plistlib
|
import plistlib
|
||||||
import time
|
import time
|
||||||
|
import xml.parsers.expat
|
||||||
|
|
||||||
# Import Salt Libs
|
# Import Salt Libs
|
||||||
import salt.modules.cmdmod
|
import salt.modules.cmdmod
|
||||||
@ -40,6 +41,11 @@ __salt__ = {
|
|||||||
'cmd.run': salt.modules.cmdmod._run_quiet,
|
'cmd.run': salt.modules.cmdmod._run_quiet,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if six.PY2:
|
||||||
|
class InvalidFileException(Exception):
|
||||||
|
pass
|
||||||
|
plistlib.InvalidFileException = InvalidFileException
|
||||||
|
|
||||||
|
|
||||||
def __virtual__():
|
def __virtual__():
|
||||||
'''
|
'''
|
||||||
@ -301,6 +307,12 @@ def launchctl(sub_cmd, *args, **kwargs):
|
|||||||
def _available_services(refresh=False):
|
def _available_services(refresh=False):
|
||||||
'''
|
'''
|
||||||
This is a helper function for getting the available macOS services.
|
This is a helper function for getting the available macOS services.
|
||||||
|
|
||||||
|
The strategy is to look through the known system locations for
|
||||||
|
launchd plist files, parse them, and use their information for
|
||||||
|
populating the list of services. Services can run without a plist
|
||||||
|
file present, but normally services which have an automated startup
|
||||||
|
will have a plist file, so this is a minor compromise.
|
||||||
'''
|
'''
|
||||||
try:
|
try:
|
||||||
if __context__['available_services'] and not refresh:
|
if __context__['available_services'] and not refresh:
|
||||||
@ -316,6 +328,15 @@ def _available_services(refresh=False):
|
|||||||
'/System/Library/LaunchAgents',
|
'/System/Library/LaunchAgents',
|
||||||
'/System/Library/LaunchDaemons',
|
'/System/Library/LaunchDaemons',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
try:
|
||||||
|
for user in os.listdir('/Users/'):
|
||||||
|
agent_path = '/Users/{}/Library/LaunchAgents'.format(user)
|
||||||
|
if os.path.isdir(agent_path):
|
||||||
|
launchd_paths.append(agent_path)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
_available_services = dict()
|
_available_services = dict()
|
||||||
for launch_dir in launchd_paths:
|
for launch_dir in launchd_paths:
|
||||||
for root, dirs, files in salt.utils.path.os_walk(launch_dir):
|
for root, dirs, files in salt.utils.path.os_walk(launch_dir):
|
||||||
@ -328,39 +349,59 @@ def _available_services(refresh=False):
|
|||||||
# Follow symbolic links of files in _launchd_paths
|
# Follow symbolic links of files in _launchd_paths
|
||||||
file_path = os.path.join(root, file_name)
|
file_path = os.path.join(root, file_name)
|
||||||
true_path = os.path.realpath(file_path)
|
true_path = os.path.realpath(file_path)
|
||||||
|
log.trace('Gathering service info for %s', true_path)
|
||||||
# ignore broken symlinks
|
# ignore broken symlinks
|
||||||
if not os.path.exists(true_path):
|
if not os.path.exists(true_path):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# This assumes most of the plist files
|
if six.PY2:
|
||||||
# will be already in XML format
|
# py2 plistlib can't read binary plists, and
|
||||||
plist = plistlib.readPlist(true_path)
|
# uses a different API than py3.
|
||||||
|
plist = plistlib.readPlist(true_path)
|
||||||
|
else:
|
||||||
|
with salt.utils.files.fopen(true_path, 'rb') as handle:
|
||||||
|
plist = plistlib.load(handle)
|
||||||
|
|
||||||
except Exception:
|
except plistlib.InvalidFileException:
|
||||||
# If plistlib is unable to read the file we'll need to use
|
# Raised in python3 if the file is not XML.
|
||||||
# the system provided plutil program to do the conversion
|
# There's nothing we can do; move on to the next one.
|
||||||
|
msg = 'Unable to parse "%s" as it is invalid XML: InvalidFileException.'
|
||||||
|
logging.warning(msg, true_path)
|
||||||
|
continue
|
||||||
|
|
||||||
|
except xml.parsers.expat.ExpatError:
|
||||||
|
# Raised by py2 for all errors.
|
||||||
|
# Raised by py3 if the file is XML, but with errors.
|
||||||
|
if six.PY3:
|
||||||
|
# There's an error in the XML, so move on.
|
||||||
|
msg = 'Unable to parse "%s" as it is invalid XML: xml.parsers.expat.ExpatError.'
|
||||||
|
logging.warning(msg, true_path)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Use the system provided plutil program to attempt
|
||||||
|
# conversion from binary.
|
||||||
cmd = '/usr/bin/plutil -convert xml1 -o - -- "{0}"'.format(
|
cmd = '/usr/bin/plutil -convert xml1 -o - -- "{0}"'.format(
|
||||||
true_path)
|
true_path)
|
||||||
plist_xml = __salt__['cmd.run'](cmd)
|
try:
|
||||||
if six.PY2:
|
plist_xml = __salt__['cmd.run'](cmd)
|
||||||
plist = plistlib.readPlistFromString(plist_xml)
|
plist = plistlib.readPlistFromString(plist_xml)
|
||||||
else:
|
except xml.parsers.expat.ExpatError:
|
||||||
plist = plistlib.loads(
|
# There's still an error in the XML, so move on.
|
||||||
salt.utils.stringutils.to_bytes(plist_xml))
|
msg = 'Unable to parse "%s" as it is invalid XML: xml.parsers.expat.ExpatError.'
|
||||||
|
logging.warning(msg, true_path)
|
||||||
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
_available_services[plist.Label.lower()] = {
|
# not all launchd plists contain a Label key
|
||||||
'file_name': file_name,
|
_available_services[plist['Label'].lower()] = {
|
||||||
'file_path': true_path,
|
|
||||||
'plist': plist}
|
|
||||||
except AttributeError:
|
|
||||||
# Handle malformed plist files
|
|
||||||
_available_services[os.path.basename(file_name).lower()] = {
|
|
||||||
'file_name': file_name,
|
'file_name': file_name,
|
||||||
'file_path': true_path,
|
'file_path': true_path,
|
||||||
'plist': plist}
|
'plist': plist}
|
||||||
|
except KeyError:
|
||||||
|
log.debug('Service %s does not contain a'
|
||||||
|
' Label key. Skipping.', true_path)
|
||||||
|
continue
|
||||||
|
|
||||||
# put this in __context__ as this is a time consuming function.
|
# put this in __context__ as this is a time consuming function.
|
||||||
# a fix for this issue. https://github.com/saltstack/salt/issues/48414
|
# a fix for this issue. https://github.com/saltstack/salt/issues/48414
|
||||||
|
@ -39,6 +39,98 @@ from salt.utils.zeromq import zmq
|
|||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def get_running_jobs(opts):
|
||||||
|
'''
|
||||||
|
Return the running jobs on this minion
|
||||||
|
'''
|
||||||
|
|
||||||
|
ret = []
|
||||||
|
proc_dir = os.path.join(opts['cachedir'], 'proc')
|
||||||
|
if not os.path.isdir(proc_dir):
|
||||||
|
return ret
|
||||||
|
for fn_ in os.listdir(proc_dir):
|
||||||
|
path = os.path.join(proc_dir, fn_)
|
||||||
|
try:
|
||||||
|
data = _read_proc_file(path, opts)
|
||||||
|
if data is not None:
|
||||||
|
ret.append(data)
|
||||||
|
except (IOError, OSError):
|
||||||
|
# proc files may be removed at any time during this process by
|
||||||
|
# the master process that is executing the JID in question, so
|
||||||
|
# we must ignore ENOENT during this process
|
||||||
|
log.trace('%s removed during processing by master process', path)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
def _read_proc_file(path, opts):
|
||||||
|
'''
|
||||||
|
Return a dict of JID metadata, or None
|
||||||
|
'''
|
||||||
|
serial = salt.payload.Serial(opts)
|
||||||
|
with salt.utils.files.fopen(path, 'rb') as fp_:
|
||||||
|
buf = fp_.read()
|
||||||
|
fp_.close()
|
||||||
|
if buf:
|
||||||
|
data = serial.loads(buf)
|
||||||
|
else:
|
||||||
|
# Proc file is empty, remove
|
||||||
|
try:
|
||||||
|
os.remove(path)
|
||||||
|
except IOError:
|
||||||
|
log.debug('Unable to remove proc file %s.', path)
|
||||||
|
return None
|
||||||
|
if not isinstance(data, dict):
|
||||||
|
# Invalid serial object
|
||||||
|
return None
|
||||||
|
if not salt.utils.process.os_is_running(data['pid']):
|
||||||
|
# The process is no longer running, clear out the file and
|
||||||
|
# continue
|
||||||
|
try:
|
||||||
|
os.remove(path)
|
||||||
|
except IOError:
|
||||||
|
log.debug('Unable to remove proc file %s.', path)
|
||||||
|
return None
|
||||||
|
|
||||||
|
if not _check_cmdline(data):
|
||||||
|
pid = data.get('pid')
|
||||||
|
if pid:
|
||||||
|
log.warning(
|
||||||
|
'PID %s exists but does not appear to be a salt process.', pid
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
os.remove(path)
|
||||||
|
except IOError:
|
||||||
|
log.debug('Unable to remove proc file %s.', path)
|
||||||
|
return None
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def _check_cmdline(data):
|
||||||
|
'''
|
||||||
|
In some cases where there are an insane number of processes being created
|
||||||
|
on a system a PID can get recycled or assigned to a non-Salt process.
|
||||||
|
On Linux this fn checks to make sure the PID we are checking on is actually
|
||||||
|
a Salt process.
|
||||||
|
|
||||||
|
For non-Linux systems we punt and just return True
|
||||||
|
'''
|
||||||
|
if not salt.utils.platform.is_linux():
|
||||||
|
return True
|
||||||
|
pid = data.get('pid')
|
||||||
|
if not pid:
|
||||||
|
return False
|
||||||
|
if not os.path.isdir('/proc'):
|
||||||
|
return True
|
||||||
|
path = os.path.join('/proc/{0}/cmdline'.format(pid))
|
||||||
|
if not os.path.isfile(path):
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
with salt.utils.files.fopen(path, 'rb') as fp_:
|
||||||
|
return b'salt' in fp_.read()
|
||||||
|
except (OSError, IOError):
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class MasterPillarUtil(object):
|
class MasterPillarUtil(object):
|
||||||
'''
|
'''
|
||||||
Helper utility for easy access to targeted minion grain and
|
Helper utility for easy access to targeted minion grain and
|
||||||
@ -721,6 +813,7 @@ def get_values_of_matching_keys(pattern_dict, user_name):
|
|||||||
ret.extend(pattern_dict[expr])
|
ret.extend(pattern_dict[expr])
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
# test code for the ConCache class
|
# test code for the ConCache class
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
||||||
|
@ -42,6 +42,9 @@ def running(opts):
|
|||||||
|
|
||||||
|
|
||||||
def cache_jobs(opts, jid, ret):
|
def cache_jobs(opts, jid, ret):
|
||||||
|
'''
|
||||||
|
Write job information to cache
|
||||||
|
'''
|
||||||
serial = salt.payload.Serial(opts=opts)
|
serial = salt.payload.Serial(opts=opts)
|
||||||
|
|
||||||
fn_ = os.path.join(
|
fn_ = os.path.join(
|
||||||
@ -73,7 +76,7 @@ def _read_proc_file(path, opts):
|
|||||||
try:
|
try:
|
||||||
os.remove(path)
|
os.remove(path)
|
||||||
except IOError:
|
except IOError:
|
||||||
pass
|
log.debug('Unable to remove proc file %s.', path)
|
||||||
return None
|
return None
|
||||||
if not isinstance(data, dict):
|
if not isinstance(data, dict):
|
||||||
# Invalid serial object
|
# Invalid serial object
|
||||||
@ -84,7 +87,7 @@ def _read_proc_file(path, opts):
|
|||||||
try:
|
try:
|
||||||
os.remove(path)
|
os.remove(path)
|
||||||
except IOError:
|
except IOError:
|
||||||
pass
|
log.debug('Unable to remove proc file %s.', path)
|
||||||
return None
|
return None
|
||||||
if opts.get('multiprocessing'):
|
if opts.get('multiprocessing'):
|
||||||
if data.get('pid') == pid:
|
if data.get('pid') == pid:
|
||||||
@ -94,7 +97,7 @@ def _read_proc_file(path, opts):
|
|||||||
try:
|
try:
|
||||||
os.remove(path)
|
os.remove(path)
|
||||||
except IOError:
|
except IOError:
|
||||||
pass
|
log.debug('Unable to remove proc file %s.', path)
|
||||||
return None
|
return None
|
||||||
if data.get('jid') == current_thread:
|
if data.get('jid') == current_thread:
|
||||||
return None
|
return None
|
||||||
@ -102,7 +105,7 @@ def _read_proc_file(path, opts):
|
|||||||
try:
|
try:
|
||||||
os.remove(path)
|
os.remove(path)
|
||||||
except IOError:
|
except IOError:
|
||||||
pass
|
log.debug('Unable to remove proc file %s.', path)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if not _check_cmdline(data):
|
if not _check_cmdline(data):
|
||||||
@ -114,7 +117,7 @@ def _read_proc_file(path, opts):
|
|||||||
try:
|
try:
|
||||||
os.remove(path)
|
os.remove(path)
|
||||||
except IOError:
|
except IOError:
|
||||||
pass
|
log.debug('Unable to remove proc file %s.', path)
|
||||||
return None
|
return None
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
@ -245,8 +245,12 @@ class CkMinions(object):
|
|||||||
try:
|
try:
|
||||||
if self.opts['key_cache'] and os.path.exists(pki_cache_fn):
|
if self.opts['key_cache'] and os.path.exists(pki_cache_fn):
|
||||||
log.debug('Returning cached minion list')
|
log.debug('Returning cached minion list')
|
||||||
with salt.utils.files.fopen(pki_cache_fn) as fn_:
|
if six.PY2:
|
||||||
return self.serial.load(fn_)
|
with salt.utils.files.fopen(pki_cache_fn) as fn_:
|
||||||
|
return self.serial.load(fn_)
|
||||||
|
else:
|
||||||
|
with salt.utils.files.fopen(pki_cache_fn, mode='rb') as fn_:
|
||||||
|
return self.serial.load(fn_)
|
||||||
else:
|
else:
|
||||||
for fn_ in salt.utils.data.sorted_ignorecase(os.listdir(os.path.join(self.opts['pki_dir'], self.acc))):
|
for fn_ in salt.utils.data.sorted_ignorecase(os.listdir(os.path.join(self.opts['pki_dir'], self.acc))):
|
||||||
if not fn_.startswith('.') and os.path.isfile(os.path.join(self.opts['pki_dir'], self.acc, fn_)):
|
if not fn_.startswith('.') and os.path.isfile(os.path.join(self.opts['pki_dir'], self.acc, fn_)):
|
||||||
|
@ -32,6 +32,7 @@ import salt.utils.error
|
|||||||
import salt.utils.event
|
import salt.utils.event
|
||||||
import salt.utils.files
|
import salt.utils.files
|
||||||
import salt.utils.jid
|
import salt.utils.jid
|
||||||
|
import salt.utils.master
|
||||||
import salt.utils.minion
|
import salt.utils.minion
|
||||||
import salt.utils.platform
|
import salt.utils.platform
|
||||||
import salt.utils.process
|
import salt.utils.process
|
||||||
@ -47,6 +48,10 @@ import salt.log.setup as log_setup
|
|||||||
import salt.defaults.exitcodes
|
import salt.defaults.exitcodes
|
||||||
from salt.utils.odict import OrderedDict
|
from salt.utils.odict import OrderedDict
|
||||||
|
|
||||||
|
from salt.exceptions import (
|
||||||
|
SaltInvocationError
|
||||||
|
)
|
||||||
|
|
||||||
# Import 3rd-party libs
|
# Import 3rd-party libs
|
||||||
from salt.ext import six
|
from salt.ext import six
|
||||||
|
|
||||||
@ -173,7 +178,11 @@ class Schedule(object):
|
|||||||
data['run'] = True
|
data['run'] = True
|
||||||
if 'jid_include' not in data or data['jid_include']:
|
if 'jid_include' not in data or data['jid_include']:
|
||||||
jobcount = 0
|
jobcount = 0
|
||||||
for job in salt.utils.minion.running(self.opts):
|
if self.opts['__role'] == 'master':
|
||||||
|
current_jobs = salt.utils.master.get_running_jobs(self.opts)
|
||||||
|
else:
|
||||||
|
current_jobs = salt.utils.minion.running(self.opts)
|
||||||
|
for job in current_jobs:
|
||||||
if 'schedule' in job:
|
if 'schedule' in job:
|
||||||
log.debug(
|
log.debug(
|
||||||
'schedule.handle_func: Checking job against fun '
|
'schedule.handle_func: Checking job against fun '
|
||||||
@ -609,6 +618,7 @@ class Schedule(object):
|
|||||||
log.warning('schedule: The metadata parameter must be '
|
log.warning('schedule: The metadata parameter must be '
|
||||||
'specified as a dictionary. Ignoring.')
|
'specified as a dictionary. Ignoring.')
|
||||||
|
|
||||||
|
data_returner = data.get('returner', None)
|
||||||
salt.utils.process.appendproctitle('{0} {1}'.format(self.__class__.__name__, ret['jid']))
|
salt.utils.process.appendproctitle('{0} {1}'.format(self.__class__.__name__, ret['jid']))
|
||||||
|
|
||||||
if not self.standalone:
|
if not self.standalone:
|
||||||
@ -626,6 +636,22 @@ class Schedule(object):
|
|||||||
|
|
||||||
# TODO: Make it readable! Splt to funcs, remove nested try-except-finally sections.
|
# TODO: Make it readable! Splt to funcs, remove nested try-except-finally sections.
|
||||||
try:
|
try:
|
||||||
|
|
||||||
|
minion_blackout_violation = False
|
||||||
|
if self.opts.get('pillar', {}).get('minion_blackout', False):
|
||||||
|
whitelist = self.opts.get('pillar', {}).get('minion_blackout_whitelist', [])
|
||||||
|
# this minion is blacked out. Only allow saltutil.refresh_pillar and the whitelist
|
||||||
|
if func != 'saltutil.refresh_pillar' and func not in whitelist:
|
||||||
|
minion_blackout_violation = True
|
||||||
|
elif self.opts.get('grains', {}).get('minion_blackout', False):
|
||||||
|
whitelist = self.opts.get('grains', {}).get('minion_blackout_whitelist', [])
|
||||||
|
if func != 'saltutil.refresh_pillar' and func not in whitelist:
|
||||||
|
minion_blackout_violation = True
|
||||||
|
if minion_blackout_violation:
|
||||||
|
raise SaltInvocationError('Minion in blackout mode. Set \'minion_blackout\' '
|
||||||
|
'to False in pillar or grains to resume operations. Only '
|
||||||
|
'saltutil.refresh_pillar allowed in blackout mode.')
|
||||||
|
|
||||||
ret['pid'] = os.getpid()
|
ret['pid'] = os.getpid()
|
||||||
|
|
||||||
if not self.standalone:
|
if not self.standalone:
|
||||||
@ -704,6 +730,7 @@ class Schedule(object):
|
|||||||
self.functions[mod_name].__globals__[global_key] = value
|
self.functions[mod_name].__globals__[global_key] = value
|
||||||
|
|
||||||
self.functions.pack['__context__']['retcode'] = 0
|
self.functions.pack['__context__']['retcode'] = 0
|
||||||
|
|
||||||
ret['return'] = self.functions[func](*args, **kwargs)
|
ret['return'] = self.functions[func](*args, **kwargs)
|
||||||
|
|
||||||
if not self.standalone:
|
if not self.standalone:
|
||||||
@ -713,7 +740,6 @@ class Schedule(object):
|
|||||||
|
|
||||||
ret['success'] = True
|
ret['success'] = True
|
||||||
|
|
||||||
data_returner = data.get('returner', None)
|
|
||||||
if data_returner or self.schedule_returner:
|
if data_returner or self.schedule_returner:
|
||||||
if 'return_config' in data:
|
if 'return_config' in data:
|
||||||
ret['ret_config'] = data['return_config']
|
ret['ret_config'] = data['return_config']
|
||||||
|
@ -40,8 +40,8 @@ class TimedProc(object):
|
|||||||
|
|
||||||
if self.timeout and not isinstance(self.timeout, (int, float)):
|
if self.timeout and not isinstance(self.timeout, (int, float)):
|
||||||
raise salt.exceptions.TimedProcTimeoutError('Error: timeout {0} must be a number'.format(self.timeout))
|
raise salt.exceptions.TimedProcTimeoutError('Error: timeout {0} must be a number'.format(self.timeout))
|
||||||
if six.PY2 and kwargs.get('shell', False):
|
if kwargs.get('shell', False):
|
||||||
args = salt.utils.stringutils.to_bytes(args)
|
args = salt.utils.data.decode(args, to_str=True)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.process = subprocess.Popen(args, **kwargs)
|
self.process = subprocess.Popen(args, **kwargs)
|
||||||
|
@ -81,6 +81,14 @@ def ipv6_addr(addr):
|
|||||||
return __ip_addr(addr, socket.AF_INET6)
|
return __ip_addr(addr, socket.AF_INET6)
|
||||||
|
|
||||||
|
|
||||||
|
def ip_addr(addr):
|
||||||
|
'''
|
||||||
|
Returns True if the IPv4 or IPv6 address (and optional subnet) are valid,
|
||||||
|
otherwise returns False.
|
||||||
|
'''
|
||||||
|
return ipv4_addr(addr) or ipv6_addr(addr)
|
||||||
|
|
||||||
|
|
||||||
def netmask(mask):
|
def netmask(mask):
|
||||||
'''
|
'''
|
||||||
Returns True if the value passed is a valid netmask, otherwise return False
|
Returns True if the value passed is a valid netmask, otherwise return False
|
||||||
|
578
salt/utils/win_lgpo_netsh.py
Normal file
578
salt/utils/win_lgpo_netsh.py
Normal file
@ -0,0 +1,578 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
r'''
|
||||||
|
A salt util for modifying firewall settings.
|
||||||
|
|
||||||
|
.. versionadded:: 2018.3.4
|
||||||
|
.. versionadded:: Fluorine
|
||||||
|
|
||||||
|
This util allows you to modify firewall settings in the local group policy in
|
||||||
|
addition to the normal firewall settings. Parameters are taken from the
|
||||||
|
netsh advfirewall prompt.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
More information can be found in the advfirewall context in netsh. This can
|
||||||
|
be access by opening a netsh prompt. At a command prompt type the following:
|
||||||
|
|
||||||
|
c:\>netsh
|
||||||
|
netsh>advfirewall
|
||||||
|
netsh advfirewall>set help
|
||||||
|
netsh advfirewall>set domain help
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
import salt.utils.win_lgpo_netsh
|
||||||
|
|
||||||
|
# Get the inbound/outbound firewall settings for connections on the
|
||||||
|
# local domain profile
|
||||||
|
salt.utils.win_lgpo_netsh.get_settings(profile='domain',
|
||||||
|
section='firewallpolicy')
|
||||||
|
|
||||||
|
# Get the inbound/outbound firewall settings for connections on the
|
||||||
|
# domain profile as defined by local group policy
|
||||||
|
salt.utils.win_lgpo_netsh.get_settings(profile='domain',
|
||||||
|
section='firewallpolicy',
|
||||||
|
store='lgpo')
|
||||||
|
|
||||||
|
# Get all firewall settings for connections on the domain profile
|
||||||
|
salt.utils.win_lgpo_netsh.get_all_settings(profile='domain')
|
||||||
|
|
||||||
|
# Get all firewall settings for connections on the domain profile as
|
||||||
|
# defined by local group policy
|
||||||
|
salt.utils.win_lgpo_netsh.get_all_settings(profile='domain', store='lgpo')
|
||||||
|
|
||||||
|
# Get all firewall settings for all profiles
|
||||||
|
salt.utils.win_lgpo_netsh.get_all_settings()
|
||||||
|
|
||||||
|
# Get all firewall settings for all profiles as defined by local group
|
||||||
|
# policy
|
||||||
|
salt.utils.win_lgpo_netsh.get_all_settings(store='lgpo')
|
||||||
|
|
||||||
|
# Set the inbound setting for the domain profile to block inbound
|
||||||
|
# connections
|
||||||
|
salt.utils.win_lgpo_netsh.set_firewall_settings(profile='domain',
|
||||||
|
inbound='blockinbound')
|
||||||
|
|
||||||
|
# Set the outbound setting for the domain profile to allow outbound
|
||||||
|
# connections
|
||||||
|
salt.utils.win_lgpo_netsh.set_firewall_settings(profile='domain',
|
||||||
|
outbound='allowoutbound')
|
||||||
|
|
||||||
|
# Set inbound/outbound settings for the domain profile in the group
|
||||||
|
# policy to block inbound and allow outbound
|
||||||
|
salt.utils.win_lgpo_netsh.set_firewall_settings(profile='domain',
|
||||||
|
inbound='blockinbound',
|
||||||
|
outbound='allowoutbound',
|
||||||
|
store='lgpo')
|
||||||
|
'''
|
||||||
|
# Import Python libs
|
||||||
|
from __future__ import absolute_import, unicode_literals, print_function
|
||||||
|
from textwrap import dedent
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import socket
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
import salt.modules.cmdmod
|
||||||
|
from salt.exceptions import CommandExecutionError
|
||||||
|
from salt.ext.six.moves import zip
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
__hostname__ = socket.gethostname()
|
||||||
|
|
||||||
|
|
||||||
|
def _netsh_file(content):
|
||||||
|
'''
|
||||||
|
helper function to get the results of ``netsh -f content.txt``
|
||||||
|
|
||||||
|
Running ``netsh`` will drop you into a ``netsh`` prompt where you can issue
|
||||||
|
``netsh`` commands. You can put a series of commands in an external file and
|
||||||
|
run them as if from a ``netsh`` prompt using the ``-f`` switch. That's what
|
||||||
|
this function does.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
|
||||||
|
content (str):
|
||||||
|
The contents of the file that will be run by the ``netsh -f``
|
||||||
|
command
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: The text returned by the netsh command
|
||||||
|
'''
|
||||||
|
with tempfile.NamedTemporaryFile(mode='w',
|
||||||
|
prefix='salt-',
|
||||||
|
suffix='.netsh',
|
||||||
|
delete=False) as fp:
|
||||||
|
fp.write(content)
|
||||||
|
try:
|
||||||
|
log.debug('{0}:\n{1}'.format(fp.name, content))
|
||||||
|
return salt.modules.cmdmod.run('netsh -f {0}'.format(fp.name), python_shell=True)
|
||||||
|
finally:
|
||||||
|
os.remove(fp.name)
|
||||||
|
|
||||||
|
|
||||||
|
def _netsh_command(command, store):
|
||||||
|
if store.lower() not in ('local', 'lgpo'):
|
||||||
|
raise ValueError('Incorrect store: {0}'.format(store))
|
||||||
|
# set the store for local or lgpo
|
||||||
|
if store.lower() == 'local':
|
||||||
|
netsh_script = dedent('''\
|
||||||
|
advfirewall
|
||||||
|
set store local
|
||||||
|
{0}
|
||||||
|
'''.format(command))
|
||||||
|
else:
|
||||||
|
netsh_script = dedent('''\
|
||||||
|
advfirewall
|
||||||
|
set store gpo = {0}
|
||||||
|
{1}
|
||||||
|
'''.format(__hostname__, command))
|
||||||
|
return _netsh_file(content=netsh_script).splitlines()
|
||||||
|
|
||||||
|
|
||||||
|
def get_settings(profile, section, store='local'):
|
||||||
|
'''
|
||||||
|
Get the firewall property from the specified profile in the specified store
|
||||||
|
as returned by ``netsh advfirewall``.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
|
||||||
|
profile (str):
|
||||||
|
The firewall profile to query. Valid options are:
|
||||||
|
|
||||||
|
- domain
|
||||||
|
- public
|
||||||
|
- private
|
||||||
|
|
||||||
|
section (str):
|
||||||
|
The property to query within the selected profile. Valid options
|
||||||
|
are:
|
||||||
|
|
||||||
|
- firewallpolicy : inbound/outbound behavior
|
||||||
|
- logging : firewall logging settings
|
||||||
|
- settings : firewall properties
|
||||||
|
- state : firewalls state (on | off)
|
||||||
|
|
||||||
|
store (str):
|
||||||
|
The store to use. This is either the local firewall policy or the
|
||||||
|
policy defined by local group policy. Valid options are:
|
||||||
|
|
||||||
|
- lgpo
|
||||||
|
- local
|
||||||
|
|
||||||
|
Default is ``local``
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: A dictionary containing the properties for the specified profile
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
CommandExecutionError: If an error occurs
|
||||||
|
ValueError: If the parameters are incorrect
|
||||||
|
'''
|
||||||
|
# validate input
|
||||||
|
if profile.lower() not in ('domain', 'public', 'private'):
|
||||||
|
raise ValueError('Incorrect profile: {0}'.format(profile))
|
||||||
|
if section.lower() not in ('state', 'firewallpolicy', 'settings', 'logging'):
|
||||||
|
raise ValueError('Incorrect section: {0}'.format(section))
|
||||||
|
if store.lower() not in ('local', 'lgpo'):
|
||||||
|
raise ValueError('Incorrect store: {0}'.format(store))
|
||||||
|
command = 'show {0}profile {1}'.format(profile, section)
|
||||||
|
# run it
|
||||||
|
results = _netsh_command(command=command, store=store)
|
||||||
|
# sample output:
|
||||||
|
# Domain Profile Settings:
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
# LocalFirewallRules N/A (GPO-store only)
|
||||||
|
# LocalConSecRules N/A (GPO-store only)
|
||||||
|
# InboundUserNotification Disable
|
||||||
|
# RemoteManagement Disable
|
||||||
|
# UnicastResponseToMulticast Enable
|
||||||
|
|
||||||
|
# if it's less than 3 lines it failed
|
||||||
|
if len(results) < 3:
|
||||||
|
raise CommandExecutionError('Invalid results: {0}'.format(results))
|
||||||
|
ret = {}
|
||||||
|
# Skip the first 2 lines. Add everything else to a dictionary
|
||||||
|
for line in results[3:]:
|
||||||
|
ret.update(dict(list(zip(*[iter(re.split(r"\s{2,}", line))]*2))))
|
||||||
|
|
||||||
|
# Remove spaces from the values so that `Not Configured` is detected
|
||||||
|
# correctly
|
||||||
|
for item in ret:
|
||||||
|
ret[item] = ret[item].replace(' ', '')
|
||||||
|
|
||||||
|
# special handling for firewallpolicy
|
||||||
|
if section == 'firewallpolicy':
|
||||||
|
inbound, outbound = ret['Firewall Policy'].split(',')
|
||||||
|
return {'Inbound': inbound, 'Outbound': outbound}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
def get_all_settings(profile, store='local'):
|
||||||
|
'''
|
||||||
|
Gets all the properties for the specified profile in the specified store
|
||||||
|
|
||||||
|
Args:
|
||||||
|
|
||||||
|
profile (str):
|
||||||
|
The firewall profile to query. Valid options are:
|
||||||
|
|
||||||
|
- domain
|
||||||
|
- public
|
||||||
|
- private
|
||||||
|
|
||||||
|
store (str):
|
||||||
|
The store to use. This is either the local firewall policy or the
|
||||||
|
policy defined by local group policy. Valid options are:
|
||||||
|
|
||||||
|
- lgpo
|
||||||
|
- local
|
||||||
|
|
||||||
|
Default is ``local``
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: A dictionary containing the specified settings
|
||||||
|
'''
|
||||||
|
ret = dict()
|
||||||
|
ret.update(get_settings(profile=profile, section='state', store=store))
|
||||||
|
ret.update(get_settings(profile=profile, section='firewallpolicy', store=store))
|
||||||
|
ret.update(get_settings(profile=profile, section='settings', store=store))
|
||||||
|
ret.update(get_settings(profile=profile, section='logging', store=store))
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
def get_all_profiles(store='local'):
|
||||||
|
'''
|
||||||
|
Gets all properties for all profiles in the specified store
|
||||||
|
|
||||||
|
Args:
|
||||||
|
|
||||||
|
store (str):
|
||||||
|
The store to use. This is either the local firewall policy or the
|
||||||
|
policy defined by local group policy. Valid options are:
|
||||||
|
|
||||||
|
- lgpo
|
||||||
|
- local
|
||||||
|
|
||||||
|
Default is ``local``
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: A dictionary containing the specified settings for each profile
|
||||||
|
'''
|
||||||
|
return {
|
||||||
|
'Domain Profile': get_all_settings(profile='domain', store=store),
|
||||||
|
'Private Profile': get_all_settings(profile='private', store=store),
|
||||||
|
'Public Profile': get_all_settings(profile='public', store=store)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def set_firewall_settings(profile,
|
||||||
|
inbound=None,
|
||||||
|
outbound=None,
|
||||||
|
store='local'):
|
||||||
|
'''
|
||||||
|
Set the firewall inbound/outbound settings for the specified profile and
|
||||||
|
store
|
||||||
|
|
||||||
|
Args:
|
||||||
|
|
||||||
|
profile (str):
|
||||||
|
The firewall profile to configure. Valid options are:
|
||||||
|
|
||||||
|
- domain
|
||||||
|
- public
|
||||||
|
- private
|
||||||
|
|
||||||
|
inbound (str):
|
||||||
|
The inbound setting. If ``None`` is passed, the setting will remain
|
||||||
|
unchanged. Valid values are:
|
||||||
|
|
||||||
|
- blockinbound
|
||||||
|
- blockinboundalways
|
||||||
|
- allowinbound
|
||||||
|
- notconfigured
|
||||||
|
|
||||||
|
Default is ``None``
|
||||||
|
|
||||||
|
outbound (str):
|
||||||
|
The outbound setting. If ``None`` is passed, the setting will remain
|
||||||
|
unchanged. Valid values are:
|
||||||
|
|
||||||
|
- allowoutbound
|
||||||
|
- blockoutbound
|
||||||
|
- notconfigured
|
||||||
|
|
||||||
|
Default is ``None``
|
||||||
|
|
||||||
|
store (str):
|
||||||
|
The store to use. This is either the local firewall policy or the
|
||||||
|
policy defined by local group policy. Valid options are:
|
||||||
|
|
||||||
|
- lgpo
|
||||||
|
- local
|
||||||
|
|
||||||
|
Default is ``local``
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: ``True`` if successful
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
CommandExecutionError: If an error occurs
|
||||||
|
ValueError: If the parameters are incorrect
|
||||||
|
'''
|
||||||
|
# Input validation
|
||||||
|
if profile.lower() not in ('domain', 'public', 'private'):
|
||||||
|
raise ValueError('Incorrect profile: {0}'.format(profile))
|
||||||
|
if inbound and inbound.lower() not in ('blockinbound',
|
||||||
|
'blockinboundalways',
|
||||||
|
'allowinbound',
|
||||||
|
'notconfigured'):
|
||||||
|
raise ValueError('Incorrect inbound value: {0}'.format(inbound))
|
||||||
|
if outbound and outbound.lower() not in ('allowoutbound',
|
||||||
|
'blockoutbound',
|
||||||
|
'notconfigured'):
|
||||||
|
raise ValueError('Incorrect outbound value: {0}'.format(outbound))
|
||||||
|
if not inbound and not outbound:
|
||||||
|
raise ValueError('Must set inbound or outbound')
|
||||||
|
|
||||||
|
# You have to specify inbound and outbound setting at the same time
|
||||||
|
# If you're only specifying one, you have to get the current setting for the
|
||||||
|
# other
|
||||||
|
if not inbound or not outbound:
|
||||||
|
ret = get_settings(profile=profile,
|
||||||
|
section='firewallpolicy',
|
||||||
|
store=store)
|
||||||
|
if not inbound:
|
||||||
|
inbound = ret['Inbound']
|
||||||
|
if not outbound:
|
||||||
|
outbound = ret['Outbound']
|
||||||
|
|
||||||
|
command = 'set {0}profile firewallpolicy {1},{2}' \
|
||||||
|
''.format(profile, inbound, outbound)
|
||||||
|
|
||||||
|
results = _netsh_command(command=command, store=store)
|
||||||
|
|
||||||
|
if results:
|
||||||
|
raise CommandExecutionError('An error occurred: {0}'.format(results))
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def set_logging_settings(profile, setting, value, store='local'):
|
||||||
|
'''
|
||||||
|
Configure logging settings for the Windows firewall.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
|
||||||
|
profile (str):
|
||||||
|
The firewall profile to configure. Valid options are:
|
||||||
|
|
||||||
|
- domain
|
||||||
|
- public
|
||||||
|
- private
|
||||||
|
|
||||||
|
setting (str):
|
||||||
|
The logging setting to configure. Valid options are:
|
||||||
|
|
||||||
|
- allowedconnections
|
||||||
|
- droppedconnections
|
||||||
|
- filename
|
||||||
|
- maxfilesize
|
||||||
|
|
||||||
|
value (str):
|
||||||
|
The value to apply to the setting. Valid values are dependent upon
|
||||||
|
the setting being configured. Valid options are:
|
||||||
|
|
||||||
|
allowedconnections:
|
||||||
|
|
||||||
|
- enable
|
||||||
|
- disable
|
||||||
|
- notconfigured
|
||||||
|
|
||||||
|
droppedconnections:
|
||||||
|
|
||||||
|
- enable
|
||||||
|
- disable
|
||||||
|
- notconfigured
|
||||||
|
|
||||||
|
filename:
|
||||||
|
|
||||||
|
- Full path and name of the firewall log file
|
||||||
|
- notconfigured
|
||||||
|
|
||||||
|
maxfilesize:
|
||||||
|
|
||||||
|
- 1 - 32767 (Kb)
|
||||||
|
- notconfigured
|
||||||
|
|
||||||
|
store (str):
|
||||||
|
The store to use. This is either the local firewall policy or the
|
||||||
|
policy defined by local group policy. Valid options are:
|
||||||
|
|
||||||
|
- lgpo
|
||||||
|
- local
|
||||||
|
|
||||||
|
Default is ``local``
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: ``True`` if successful
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
CommandExecutionError: If an error occurs
|
||||||
|
ValueError: If the parameters are incorrect
|
||||||
|
'''
|
||||||
|
# Input validation
|
||||||
|
if profile.lower() not in ('domain', 'public', 'private'):
|
||||||
|
raise ValueError('Incorrect profile: {0}'.format(profile))
|
||||||
|
if setting.lower() not in ('allowedconnections',
|
||||||
|
'droppedconnections',
|
||||||
|
'filename',
|
||||||
|
'maxfilesize'):
|
||||||
|
raise ValueError('Incorrect setting: {0}'.format(setting))
|
||||||
|
if setting.lower() in ('allowedconnections', 'droppedconnections'):
|
||||||
|
if value.lower() not in ('enable', 'disable', 'notconfigured'):
|
||||||
|
raise ValueError('Incorrect value: {0}'.format(value))
|
||||||
|
# TODO: Consider adding something like the following to validate filename
|
||||||
|
# https://stackoverflow.com/questions/9532499/check-whether-a-path-is-valid-in-python-without-creating-a-file-at-the-paths-ta
|
||||||
|
if setting.lower() == 'maxfilesize':
|
||||||
|
if value.lower() != 'notconfigured':
|
||||||
|
# Must be a number between 1 and 32767
|
||||||
|
try:
|
||||||
|
int(value)
|
||||||
|
except ValueError:
|
||||||
|
raise ValueError('Incorrect value: {0}'.format(value))
|
||||||
|
if not 1 <= int(value) <= 32767:
|
||||||
|
raise ValueError('Incorrect value: {0}'.format(value))
|
||||||
|
# Run the command
|
||||||
|
command = 'set {0}profile logging {1} {2}'.format(profile, setting, value)
|
||||||
|
results = _netsh_command(command=command, store=store)
|
||||||
|
|
||||||
|
# A successful run should return an empty list
|
||||||
|
if results:
|
||||||
|
raise CommandExecutionError('An error occurred: {0}'.format(results))
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def set_settings(profile, setting, value, store='local'):
|
||||||
|
'''
|
||||||
|
Configure firewall settings.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
|
||||||
|
profile (str):
|
||||||
|
The firewall profile to configure. Valid options are:
|
||||||
|
|
||||||
|
- domain
|
||||||
|
- public
|
||||||
|
- private
|
||||||
|
|
||||||
|
setting (str):
|
||||||
|
The firewall setting to configure. Valid options are:
|
||||||
|
|
||||||
|
- localfirewallrules
|
||||||
|
- localconsecrules
|
||||||
|
- inboundusernotification
|
||||||
|
- remotemanagement
|
||||||
|
- unicastresponsetomulticast
|
||||||
|
|
||||||
|
value (str):
|
||||||
|
The value to apply to the setting. Valid options are
|
||||||
|
|
||||||
|
- enable
|
||||||
|
- disable
|
||||||
|
- notconfigured
|
||||||
|
|
||||||
|
store (str):
|
||||||
|
The store to use. This is either the local firewall policy or the
|
||||||
|
policy defined by local group policy. Valid options are:
|
||||||
|
|
||||||
|
- lgpo
|
||||||
|
- local
|
||||||
|
|
||||||
|
Default is ``local``
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: ``True`` if successful
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
CommandExecutionError: If an error occurs
|
||||||
|
ValueError: If the parameters are incorrect
|
||||||
|
'''
|
||||||
|
# Input validation
|
||||||
|
if profile.lower() not in ('domain', 'public', 'private'):
|
||||||
|
raise ValueError('Incorrect profile: {0}'.format(profile))
|
||||||
|
if setting.lower() not in ('localfirewallrules',
|
||||||
|
'localconsecrules',
|
||||||
|
'inboundusernotification',
|
||||||
|
'remotemanagement',
|
||||||
|
'unicastresponsetomulticast'):
|
||||||
|
raise ValueError('Incorrect setting: {0}'.format(setting))
|
||||||
|
if value.lower() not in ('enable', 'disable', 'notconfigured'):
|
||||||
|
raise ValueError('Incorrect value: {0}'.format(value))
|
||||||
|
|
||||||
|
# Run the command
|
||||||
|
command = 'set {0}profile settings {1} {2}'.format(profile, setting, value)
|
||||||
|
results = _netsh_command(command=command, store=store)
|
||||||
|
|
||||||
|
# A successful run should return an empty list
|
||||||
|
if results:
|
||||||
|
raise CommandExecutionError('An error occurred: {0}'.format(results))
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def set_state(profile, state, store='local'):
|
||||||
|
'''
|
||||||
|
Configure the firewall state.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
|
||||||
|
profile (str):
|
||||||
|
The firewall profile to configure. Valid options are:
|
||||||
|
|
||||||
|
- domain
|
||||||
|
- public
|
||||||
|
- private
|
||||||
|
|
||||||
|
state (str):
|
||||||
|
The firewall state. Valid options are:
|
||||||
|
|
||||||
|
- on
|
||||||
|
- off
|
||||||
|
- notconfigured
|
||||||
|
|
||||||
|
store (str):
|
||||||
|
The store to use. This is either the local firewall policy or the
|
||||||
|
policy defined by local group policy. Valid options are:
|
||||||
|
|
||||||
|
- lgpo
|
||||||
|
- local
|
||||||
|
|
||||||
|
Default is ``local``
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: ``True`` if successful
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
CommandExecutionError: If an error occurs
|
||||||
|
ValueError: If the parameters are incorrect
|
||||||
|
'''
|
||||||
|
# Input validation
|
||||||
|
if profile.lower() not in ('domain', 'public', 'private'):
|
||||||
|
raise ValueError('Incorrect profile: {0}'.format(profile))
|
||||||
|
if state.lower() not in ('on', 'off', 'notconfigured'):
|
||||||
|
raise ValueError('Incorrect state: {0}'.format(state))
|
||||||
|
|
||||||
|
# Run the command
|
||||||
|
command = 'set {0}profile state {1}'.format(profile, state)
|
||||||
|
results = _netsh_command(command=command, store=store)
|
||||||
|
|
||||||
|
# A successful run should return an empty list
|
||||||
|
if results:
|
||||||
|
raise CommandExecutionError('An error occurred: {0}'.format(results))
|
||||||
|
|
||||||
|
return True
|
@ -271,7 +271,7 @@ def make_inheritable(token):
|
|||||||
win32con.DUPLICATE_SAME_ACCESS)
|
win32con.DUPLICATE_SAME_ACCESS)
|
||||||
|
|
||||||
|
|
||||||
def runas_system(cmd, username, password):
|
def runas_system(cmd, username, password, cwd=None):
|
||||||
# This only works as system, when salt is running as a service for example
|
# This only works as system, when salt is running as a service for example
|
||||||
|
|
||||||
# Check for a domain
|
# Check for a domain
|
||||||
@ -310,8 +310,8 @@ def runas_system(cmd, username, password):
|
|||||||
|
|
||||||
except win32security.error as exc:
|
except win32security.error as exc:
|
||||||
# User doesn't have admin, use existing token
|
# User doesn't have admin, use existing token
|
||||||
if exc[0] == winerror.ERROR_NO_SUCH_LOGON_SESSION \
|
if exc.winerror == winerror.ERROR_NO_SUCH_LOGON_SESSION \
|
||||||
or exc[0] == winerror.ERROR_PRIVILEGE_NOT_HELD:
|
or exc.winerror == winerror.ERROR_PRIVILEGE_NOT_HELD:
|
||||||
elevated_token = token
|
elevated_token = token
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
@ -352,7 +352,7 @@ def runas_system(cmd, username, password):
|
|||||||
1,
|
1,
|
||||||
0,
|
0,
|
||||||
user_environment,
|
user_environment,
|
||||||
None,
|
cwd,
|
||||||
startup_info)
|
startup_info)
|
||||||
|
|
||||||
hProcess, hThread, PId, TId = \
|
hProcess, hThread, PId, TId = \
|
||||||
@ -397,7 +397,7 @@ def runas(cmd, username, password, cwd=None):
|
|||||||
# This only works when not running under the system account
|
# This only works when not running under the system account
|
||||||
# Debug mode for example
|
# Debug mode for example
|
||||||
if salt.utils.win_functions.get_current_user() == 'SYSTEM':
|
if salt.utils.win_functions.get_current_user() == 'SYSTEM':
|
||||||
return runas_system(cmd, username, password)
|
return runas_system(cmd, username, password, cwd)
|
||||||
|
|
||||||
# Create a pipe to set as stdout in the child. The write handle needs to be
|
# Create a pipe to set as stdout in the child. The write handle needs to be
|
||||||
# inheritable.
|
# inheritable.
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user