Merge branch '2018.3' into jid

This commit is contained in:
Nicole Thomas 2018-08-05 15:16:41 -04:00 committed by GitHub
commit 084e7f0ef4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
195 changed files with 12502 additions and 1770 deletions

View File

@ -1,10 +1,23 @@
pipeline {
agent { label 'docs' }
options {
timestamps()
ansiColor('xterm')
}
environment {
PYENV_ROOT = "/usr/local/pyenv"
PATH = "$PYENV_ROOT/bin:$PATH"
PY_COLORS = 1
}
stages {
stage('github-pending') {
steps {
githubNotify credentialsId: 'test-jenkins-credentials',
description: 'Testing docs...',
status: 'PENDING',
context: "jenkins/pr/docs"
}
}
stage('setup') {
steps {
sh 'eval "$(pyenv init -)"; pyenv install 2.7.14 || echo "We already have this python."; pyenv local 2.7.14; pyenv shell 2.7.14'
@ -14,16 +27,24 @@ pipeline {
stage('build') {
steps {
sh 'eval "$(pyenv init -)"; make -C doc clean html'
archiveArtifacts artifacts: 'doc/_build/html'
}
}
}
post {
always {
cleanWs()
}
success {
githubNotify description: "The docs job has passed, artifacts have been saved", status: "SUCCESS"
githubNotify credentialsId: 'test-jenkins-credentials',
description: 'The docs job has passed',
status: 'SUCCESS',
context: "jenkins/pr/docs"
}
failure {
githubNotify description: "The docs job has failed", status: "FAILURE"
githubNotify credentialsId: 'test-jenkins-credentials',
description: 'The docs job has failed',
status: 'FAILURE',
context: "jenkins/pr/docs"
}
}
}

View File

@ -1,5 +1,9 @@
pipeline {
agent { label 'kitchen-slave' }
options {
timestamps()
ansiColor('xterm')
}
environment {
SALT_KITCHEN_PLATFORMS = "/var/jenkins/workspace/platforms.yml"
SALT_KITCHEN_DRIVER = "/var/jenkins/workspace/driver.yml"
@ -7,8 +11,17 @@ pipeline {
RBENV_VERSION = "2.4.2"
TEST_SUITE = "py2"
TEST_PLATFORM = "centos-7"
PY_COLORS = 1
}
stages {
stage('github-pending') {
steps {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "running ${TEST_SUITE}-${TEST_PLATFORM}...",
status: 'PENDING',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
}
stage('setup') {
steps {
sh 'bundle install --with ec2 windows --without opennebula docker'
@ -23,26 +36,36 @@ pipeline {
sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM'
}
}}
archiveArtifacts artifacts: 'artifacts/xml-unittests-output/*.xml'
}
post {
always {
script { withCredentials([[$class: 'AmazonWebServicesCredentialsBinding', accessKeyVariable: 'AWS_ACCESS_KEY_ID', credentialsId: 'AWS_ACCESS_KEY_ID', secretKeyVariable: 'AWS_SECRET_ACCESS_KEY']]) {
sshagent(credentials: ['jenkins-testing-ssh-key']) {
sh 'ssh-add ~/.ssh/jenkins/jenkins-testing.pem'
sh 'ssh-add ~/.ssh/jenkins-testing.pem'
sh 'bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM'
}
}}
archiveArtifacts artifacts: 'artifacts/xml-unittests-output/*.xml'
}
}
}
}
post {
always {
junit 'artifacts/xml-unittests-output/*.xml'
cleanWs()
}
success {
githubNotify description: "The centos7-py2 job has passed", status: "SUCCESS"
githubNotify credentialsId: 'test-jenkins-credentials',
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has passed",
status: 'SUCCESS',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
failure {
githubNotify description: "The centos7-py2 job has failed", status: "FAILURE"
githubNotify credentialsId: 'test-jenkins-credentials',
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has failed",
status: 'FAILURE',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
}
}

View File

@ -1,5 +1,9 @@
pipeline {
agent { label 'kitchen-slave' }
options {
timestamps()
ansiColor('xterm')
}
environment {
SALT_KITCHEN_PLATFORMS = "/var/jenkins/workspace/platforms.yml"
SALT_KITCHEN_DRIVER = "/var/jenkins/workspace/driver.yml"
@ -7,8 +11,17 @@ pipeline {
RBENV_VERSION = "2.4.2"
TEST_SUITE = "py3"
TEST_PLATFORM = "centos-7"
PY_COLORS = 1
}
stages {
stage('github-pending') {
steps {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "running ${TEST_SUITE}-${TEST_PLATFORM}...",
status: 'PENDING',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
}
stage('setup') {
steps {
sh 'bundle install --with ec2 windows --without opennebula docker'
@ -23,26 +36,36 @@ pipeline {
sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM'
}
}}
archiveArtifacts artifacts: 'artifacts/xml-unittests-output/*.xml'
}
post {
always {
script { withCredentials([[$class: 'AmazonWebServicesCredentialsBinding', accessKeyVariable: 'AWS_ACCESS_KEY_ID', credentialsId: 'AWS_ACCESS_KEY_ID', secretKeyVariable: 'AWS_SECRET_ACCESS_KEY']]) {
sshagent(credentials: ['jenkins-testing-ssh-key']) {
sh 'ssh-add ~/.ssh/jenkins/jenkins-testing.pem'
sh 'ssh-add ~/.ssh/jenkins-testing.pem'
sh 'bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM'
}
}}
archiveArtifacts artifacts: 'artifacts/xml-unittests-output/*.xml'
}
}
}
}
post {
always {
junit 'artifacts/xml-unittests-output/*.xml'
cleanWs()
}
success {
githubNotify description: "The centos7-py3 job has passed", status: "SUCCESS"
githubNotify credentialsId: 'test-jenkins-credentials',
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has passed",
status: 'SUCCESS',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
failure {
githubNotify description: "The centos7-py3 job has failed", status: "FAILURE"
githubNotify credentialsId: 'test-jenkins-credentials',
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has failed",
status: 'FAILURE',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
}
}

View File

@ -1,5 +1,9 @@
pipeline {
agent { label 'kitchen-slave' }
options {
timestamps()
ansiColor('xterm')
}
environment {
SALT_KITCHEN_PLATFORMS = "/var/jenkins/workspace/platforms.yml"
SALT_KITCHEN_DRIVER = "/var/jenkins/workspace/driver.yml"
@ -7,8 +11,17 @@ pipeline {
RBENV_VERSION = "2.4.2"
TEST_SUITE = "py2"
TEST_PLATFORM = "ubuntu-1604"
PY_COLORS = 1
}
stages {
stage('github-pending') {
steps {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "running ${TEST_SUITE}-${TEST_PLATFORM}...",
status: 'PENDING',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
}
stage('setup') {
steps {
sh 'bundle install --with ec2 windows --without opennebula docker'
@ -23,26 +36,36 @@ pipeline {
sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM'
}
}}
archiveArtifacts artifacts: 'artifacts/xml-unittests-output/*.xml'
}
post {
always {
script { withCredentials([[$class: 'AmazonWebServicesCredentialsBinding', accessKeyVariable: 'AWS_ACCESS_KEY_ID', credentialsId: 'AWS_ACCESS_KEY_ID', secretKeyVariable: 'AWS_SECRET_ACCESS_KEY']]) {
sshagent(credentials: ['jenkins-testing-ssh-key']) {
sh 'ssh-add ~/.ssh/jenkins/jenkins-testing.pem'
sh 'ssh-add ~/.ssh/jenkins-testing.pem'
sh 'bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM'
}
}}
archiveArtifacts artifacts: 'artifacts/xml-unittests-output/*.xml'
}
}
}
}
post {
always {
junit 'artifacts/xml-unittests-output/*.xml'
cleanWs()
}
success {
githubNotify description: "The ubuntu-1604-py2 job has passed", status: "SUCCESS"
githubNotify credentialsId: 'test-jenkins-credentials',
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has passed",
status: 'SUCCESS',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
failure {
githubNotify description: "The ubuntu-1604-py2 job has failed", status: "FAILURE"
githubNotify credentialsId: 'test-jenkins-credentials',
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has failed",
status: 'FAILURE',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
}
}

View File

@ -1,5 +1,9 @@
pipeline {
agent { label 'kitchen-slave' }
options {
timestamps()
ansiColor('xterm')
}
environment {
SALT_KITCHEN_PLATFORMS = "/var/jenkins/workspace/platforms.yml"
SALT_KITCHEN_DRIVER = "/var/jenkins/workspace/driver.yml"
@ -7,8 +11,17 @@ pipeline {
RBENV_VERSION = "2.4.2"
TEST_SUITE = "py3"
TEST_PLATFORM = "ubuntu-1604"
PY_COLORS = 1
}
stages {
stage('github-pending') {
steps {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "running ${TEST_SUITE}-${TEST_PLATFORM}...",
status: 'PENDING',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
}
stage('setup') {
steps {
sh 'bundle install --with ec2 windows --without opennebula docker'
@ -23,26 +36,36 @@ pipeline {
sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM'
}
}}
archiveArtifacts artifacts: 'artifacts/xml-unittests-output/*.xml'
}
post {
always {
script { withCredentials([[$class: 'AmazonWebServicesCredentialsBinding', accessKeyVariable: 'AWS_ACCESS_KEY_ID', credentialsId: 'AWS_ACCESS_KEY_ID', secretKeyVariable: 'AWS_SECRET_ACCESS_KEY']]) {
sshagent(credentials: ['jenkins-testing-ssh-key']) {
sh 'ssh-add ~/.ssh/jenkins/jenkins-testing.pem'
sh 'ssh-add ~/.ssh/jenkins-testing.pem'
sh 'bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM'
}
}}
archiveArtifacts artifacts: 'artifacts/xml-unittests-output/*.xml'
}
}
}
}
post {
always {
junit 'artifacts/xml-unittests-output/*.xml'
cleanWs()
}
success {
githubNotify description: "The ubuntu-1604-py3 job has passed", status: "SUCCESS"
githubNotify credentialsId: 'test-jenkins-credentials',
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has passed",
status: 'SUCCESS',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
failure {
githubNotify description: "The ubuntu-1604-py3 job has failed", status: "FAILURE"
githubNotify credentialsId: 'test-jenkins-credentials',
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has failed",
status: 'FAILURE',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
}
}

View File

@ -1,15 +1,27 @@
pipeline {
agent { label 'pr-lint-slave' }
options {
timestamps()
ansiColor('xterm')
}
environment {
PYENV_ROOT = "/usr/local/pyenv"
PATH = "$PYENV_ROOT/bin:$PATH"
PY_COLORS = 1
}
stages {
stage('github-pending') {
steps {
githubNotify credentialsId: 'test-jenkins-credentials',
description: 'Testing lint...',
status: 'PENDING',
context: "jenkins/pr/lint"
}
}
stage('setup') {
steps {
sh 'eval "$(pyenv init -)"; pyenv install 2.7.14 || echo "We already have this python."; pyenv local 2.7.14; pyenv shell 2.7.14'
sh 'eval "$(pyenv init -)"; pip install pylint SaltPyLint'
sh 'eval "$(pyenv init -)"; which pylint; pylint --version'
sh 'eval "$(pyenv init -)"; pip install tox'
}
}
stage('linting') {
@ -17,13 +29,13 @@ pipeline {
parallel {
stage('salt linting') {
steps {
sh 'eval "$(pyenv init -)"; pylint --rcfile=.testing.pylintrc --disable=W1307,str-format-in-logging setup.py salt/ | tee pylint-report.xml'
sh 'eval "$(pyenv init - --no-rehash)"; tox -e pylint-salt $(find salt/ -name "*.py" -exec git diff --name-only "origin/$CHANGE_TARGET" "origin/$BRANCH_NAME" setup.py {} +) | tee pylint-report.xml'
archiveArtifacts artifacts: 'pylint-report.xml'
}
}
stage('test linting') {
steps {
sh 'eval "$(pyenv init -)"; pylint --rcfile=.testing.pylintrc --disable=W0232,E1002,W1307,str-format-in-logging tests/ | tee pylint-report-tests.xml'
sh 'eval "$(pyenv init - --no-rehash)"; tox -e pylint-tests $(find tests/ -name "*.py" -exec git diff --name-only "origin/$CHANGE_TARGET" "origin/$BRANCH_NAME" {} +) | tee pylint-report-tests.xml'
archiveArtifacts artifacts: 'pylint-report-tests.xml'
}
}
@ -31,11 +43,30 @@ pipeline {
}
}
post {
always {
step([$class: 'WarningsPublisher',
parserConfigurations: [[
parserName: 'PyLint',
pattern: 'pylint-report*.xml'
]],
failedTotalAll: '0',
useDeltaValues: false,
canRunOnFailed: true,
usePreviousBuildAsReference: true
])
cleanWs()
}
success {
githubNotify description: "The lint job has passed", status: "SUCCESS"
githubNotify credentialsId: 'test-jenkins-credentials',
description: 'The lint job has passed',
status: 'SUCCESS',
context: "jenkins/pr/lint"
}
failure {
githubNotify description: "The lint job has failed", status: "FAILURE"
githubNotify credentialsId: 'test-jenkins-credentials',
description: 'The lint job has failed',
status: 'FAILURE',
context: "jenkins/pr/lint"
}
}
}

1
.github/CODEOWNERS vendored
View File

@ -48,6 +48,7 @@ salt/spm/* @saltstack/team-spm
# Team SSH
salt/cli/ssh.py @saltstack/team-ssh
salt/client/ssh/* @saltstack/team-ssh
salt/roster/* @saltstack/team-ssh
salt/runners/ssh.py @saltstack/team-ssh
salt/**/thin.py @saltstack/team-ssh

View File

@ -1,6 +1,6 @@
---
<% vagrant = system('gem list -i kitchen-vagrant 2>/dev/null >/dev/null') %>
<% version = '2017.7.4' %>
<% version = '2017.7.6' %>
<% platformsfile = ENV['SALT_KITCHEN_PLATFORMS'] || '.kitchen/platforms.yml' %>
<% driverfile = ENV['SALT_KITCHEN_DRIVER'] || '.kitchen/driver.yml' %>
<% verifierfile = ENV['SALT_KITCHEN_VERIFIER'] || '.kitchen/verifier.yml' %>
@ -31,7 +31,7 @@ provisioner:
salt_version: latest
salt_bootstrap_url: https://bootstrap.saltstack.com
salt_bootstrap_options: -X -p rsync stable <%= version %>
log_level: debug
log_level: info
sudo: true
require_chef: false
retry_on_exit_code:

View File

@ -138,7 +138,7 @@
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a href="http://saltstack.com/" target="_blank"><img src="{{ pathto('_static/images/saltstack.svg', 1) }}" class="nolightbox" height="40px" width="170px"></a>
<a href="http://saltstack.com/" target="_blank"><img src="{{ pathto('_static/images/SaltStack_white.svg', 1) }}" class="nolightbox" height="40px" width="170px"></a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="navbarCollapse">
@ -252,13 +252,11 @@
<p>© {{ copyright }} SaltStack. All Rights Reserved, SaltStack Inc. | <a href="http://saltstack.com/privacy-policy" target="_blank">Privacy Policy</a></p>
</div>
</div>
<div class="col-sm-6">
<!--
<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>
</div>
{% endif %}
</div>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 240 KiB

After

Width:  |  Height:  |  Size: 589 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 301.57 44.47"><defs><style>.cls-1{fill:#fff;}</style></defs><title>Asset 2</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path class="cls-1" d="M50.63,6.05h8.79L72.58,43.24H64.15L61.7,35.6H48l-2.52,7.64H37.35Zm-.51,23.14h9.52l-4.7-14.64Z"/><path class="cls-1" d="M76.12,6.05h7.77V36.56h18.58v6.68H76.12Z"/><path class="cls-1" d="M127.07,6.05v6.58H115.94V43.24h-7.82V12.63H96.94V6.05Z"/><path class="cls-1" d="M190.53,6.05v6.58H179.41V43.24h-7.83V12.63H160.4V6.05Z"/><path class="cls-1" d="M200.41,6.05h8.8l13.16,37.19h-8.43l-2.45-7.64h-13.7l-2.52,7.64h-8.13Zm-.51,23.14h9.53l-4.7-14.64Z"/><path class="cls-1" d="M227.91,10.17a15.34,15.34,0,0,1,11.34-4.83q9.28-.2,13.71,5.87a13.46,13.46,0,0,1,2.69,6.89l-7.77.16a10.78,10.78,0,0,0-2-4A7.68,7.68,0,0,0,239.68,12a7.59,7.59,0,0,0-6.48,3.56Q230.88,19,231,25.25t2.73,9.32a7.91,7.91,0,0,0,6.49,3,7.27,7.27,0,0,0,6-2.8,11.86,11.86,0,0,0,1.84-4.35l7.72-.17a16.79,16.79,0,0,1-4.9,10,14.83,14.83,0,0,1-10.44,4q-7.94.16-12.59-4.88t-4.84-14.09Q222.85,15.56,227.91,10.17Z"/><path class="cls-1" d="M260.39,6.05H268V21.37L282.4,6.05h10.05L277.18,21.34l16.05,21.9h-10L271.78,27,268,30.8V43.24h-7.64Z"/><path class="cls-1" d="M15.65,12.47a9.05,9.05,0,0,1,4.6-1.05,10.15,10.15,0,0,1,4.63.94,5.29,5.29,0,0,1,3,4.77h7.47a11.06,11.06,0,0,0-4.4-9A16.57,16.57,0,0,0,20.79,5a23,23,0,0,0-5.14.54Z"/><path class="cls-1" d="M33.21,24.77a16.6,16.6,0,0,0-6.43-2.7l-6.53-1.54A22.45,22.45,0,0,1,15.31,19a3.18,3.18,0,0,1-1.81-3c0-.12,0-.22,0-.34H6.35c0,.3,0,.59,0,.9q0,5.47,3.76,8.05,2.22,1.54,8.15,2.85l4,.89a17.48,17.48,0,0,1,5.17,1.74A3.17,3.17,0,0,1,29,33c0,2.1-1.1,3.54-3.32,4.31a14.08,14.08,0,0,1-4.56.61c-3.13,0-5.33-.77-6.62-2.3a7.48,7.48,0,0,1-1.42-3.81H5.7a11,11,0,0,0,4.21,9.12q4.22,3.33,11.56,3.32,7.19,0,11.13-3.37a10.64,10.64,0,0,0,3.94-8.46A9.1,9.1,0,0,0,33.21,24.77Z"/><path class="cls-1" d="M137.55,12.73a9,9,0,0,1,4.6-1.05,10.18,10.18,0,0,1,4.63.94,5.33,5.33,0,0,1,3,4.77h7.47a11,11,0,0,0-4.41-9,16.54,16.54,0,0,0-10.15-3.12,23,23,0,0,0-5.14.54Z"/><path class="cls-1" d="M155.11,25a16.83,16.83,0,0,0-6.43-2.7l-6.53-1.54a21.71,21.71,0,0,1-4.94-1.54,3.17,3.17,0,0,1-1.81-3c0-.13,0-.23,0-.35h-7.19c0,.3-.05.6-.05.9,0,3.65,1.26,6.34,3.77,8.05,1.48,1,4.19,2,8.15,2.85l4,.89a17.29,17.29,0,0,1,5.16,1.74,3.16,3.16,0,0,1,1.63,2.85q0,3.15-3.32,4.32a14.39,14.39,0,0,1-4.56.6c-3.13,0-5.34-.76-6.62-2.3A7.48,7.48,0,0,1,135,32H127.6a11,11,0,0,0,4.21,9.13c2.8,2.21,6.66,3.32,11.55,3.32s8.51-1.13,11.14-3.37a10.7,10.7,0,0,0,3.94-8.47A9.1,9.1,0,0,0,155.11,25Z"/><rect class="cls-1" width="9.85" height="9.85"/><path class="cls-1" d="M299.8,40.44a.94.94,0,0,0,.16-.57,1.06,1.06,0,0,0-.08-.45.82.82,0,0,0-.24-.32,1,1,0,0,0-.38-.19,1.72,1.72,0,0,0-.52-.07h-1.2v3.4h.59V40.93h.66l.68,1.31h.63v0l-.76-1.42A1,1,0,0,0,299.8,40.44Zm-.59-.14a.52.52,0,0,1-.19.12,1.18,1.18,0,0,1-.28,0h-.61V39.32h.61a.79.79,0,0,1,.28,0,.55.55,0,0,1,.2.11.76.76,0,0,1,.12.19.85.85,0,0,1,0,.23.56.56,0,0,1,0,.23A.36.36,0,0,1,299.21,40.3Z"/><path class="cls-1" d="M298.7,37.68a2.88,2.88,0,1,0,2.87,2.87A2.88,2.88,0,0,0,298.7,37.68Zm0,5.27a2.4,2.4,0,1,1,2.39-2.4A2.41,2.41,0,0,1,298.7,43Z"/></g></g></svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -250,8 +250,8 @@ on_saltstack = 'SALT_ON_SALTSTACK' in os.environ
project = 'Salt'
version = salt.version.__version__
latest_release = '2018.3.1' # latest release
previous_release = '2017.7.6' # latest release from previous branch
latest_release = '2018.3.2' # latest release
previous_release = '2017.7.7' # latest release from previous branch
previous_release_dir = '2017.7' # path on web server for previous branch
next_release = '' # next release
next_release_dir = '' # path on web server for next release branch

View File

@ -148,22 +148,23 @@ Why aren't my custom modules/states/etc. available on my Minions?
-----------------------------------------------------------------
Custom modules are synced to Minions when
:mod:`saltutil.sync_modules <salt.modules.saltutil.sync_modules>`,
or :mod:`saltutil.sync_all <salt.modules.saltutil.sync_all>` is run.
Custom modules are also synced by :mod:`state.apply` when run without
any arguments.
:py:func:`saltutil.sync_modules <salt.modules.saltutil.sync_modules>`,
or :py:func:`saltutil.sync_all <salt.modules.saltutil.sync_all>` is run.
Similarly, custom states are synced to Minions when :py:func:`saltutil.sync_states
<salt.modules.saltutil.sync_states>`, or :py:func:`saltutil.sync_all
<salt.modules.saltutil.sync_all>` is run.
Similarly, custom states are synced to Minions
when :mod:`state.apply <salt.modules.state.apply_>`,
:mod:`saltutil.sync_states <salt.modules.saltutil.sync_states>`, or
:mod:`saltutil.sync_all <salt.modules.saltutil.sync_all>` is run.
They are both also synced when a :ref:`highstate <running-highstate>` is
triggered.
Custom states are also synced by :mod:`state.apply<salt.modules.state.apply_>`
when run without any arguments.
As of the Fluorine release, as well as 2017.7.7 and 2018.3.2 in their
respective release cycles, the ``sync`` argument to :py:func:`state.apply
<salt.modules.state.apply_>`/:py:func:`state.sls <salt.modules.state.sls>` can
be used to sync custom types when running individual SLS files.
Other custom types (renderers, outputters, etc.) have similar behavior, see the
documentation for the :mod:`saltutil <salt.modules.saltutil>` module for more
documentation for the :py:func:`saltutil <salt.modules.saltutil>` module for more
information.
:ref:`This reactor example <minion-start-reactor>` can be used to automatically

View File

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "SALT-API" "1" "May 09, 2018" "2018.3.1" "Salt"
.TH "SALT-API" "1" "Jun 14, 2018" "2018.3.2" "Salt"
.SH NAME
salt-api \- salt-api Command
.

View File

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "SALT-CALL" "1" "May 09, 2018" "2018.3.1" "Salt"
.TH "SALT-CALL" "1" "Jun 14, 2018" "2018.3.2" "Salt"
.SH NAME
salt-call \- salt-call Documentation
.

View File

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "SALT-CLOUD" "1" "May 09, 2018" "2018.3.1" "Salt"
.TH "SALT-CLOUD" "1" "Jun 14, 2018" "2018.3.2" "Salt"
.SH NAME
salt-cloud \- Salt Cloud Command
.

View File

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "SALT-CP" "1" "May 09, 2018" "2018.3.1" "Salt"
.TH "SALT-CP" "1" "Jun 14, 2018" "2018.3.2" "Salt"
.SH NAME
salt-cp \- salt-cp Documentation
.

View File

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "SALT-KEY" "1" "May 09, 2018" "2018.3.1" "Salt"
.TH "SALT-KEY" "1" "Jun 14, 2018" "2018.3.2" "Salt"
.SH NAME
salt-key \- salt-key Documentation
.

View File

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "SALT-MASTER" "1" "May 09, 2018" "2018.3.1" "Salt"
.TH "SALT-MASTER" "1" "Jun 14, 2018" "2018.3.2" "Salt"
.SH NAME
salt-master \- salt-master Documentation
.

View File

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "SALT-MINION" "1" "May 09, 2018" "2018.3.1" "Salt"
.TH "SALT-MINION" "1" "Jun 14, 2018" "2018.3.2" "Salt"
.SH NAME
salt-minion \- salt-minion Documentation
.

View File

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "SALT-PROXY" "1" "May 09, 2018" "2018.3.1" "Salt"
.TH "SALT-PROXY" "1" "Jun 14, 2018" "2018.3.2" "Salt"
.SH NAME
salt-proxy \- salt-proxy Documentation
.

View File

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "SALT-RUN" "1" "May 09, 2018" "2018.3.1" "Salt"
.TH "SALT-RUN" "1" "Jun 14, 2018" "2018.3.2" "Salt"
.SH NAME
salt-run \- salt-run Documentation
.

View File

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "SALT-SSH" "1" "May 09, 2018" "2018.3.1" "Salt"
.TH "SALT-SSH" "1" "Jun 14, 2018" "2018.3.2" "Salt"
.SH NAME
salt-ssh \- salt-ssh Documentation
.

View File

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "SALT-SYNDIC" "1" "May 09, 2018" "2018.3.1" "Salt"
.TH "SALT-SYNDIC" "1" "Jun 14, 2018" "2018.3.2" "Salt"
.SH NAME
salt-syndic \- salt-syndic Documentation
.

View File

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "SALT-UNITY" "1" "May 09, 2018" "2018.3.1" "Salt"
.TH "SALT-UNITY" "1" "Jun 14, 2018" "2018.3.2" "Salt"
.SH NAME
salt-unity \- salt-unity Command
.

View File

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "SALT" "1" "May 09, 2018" "2018.3.1" "Salt"
.TH "SALT" "1" "Jun 14, 2018" "2018.3.2" "Salt"
.SH NAME
salt \- salt
.

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "SPM" "1" "May 09, 2018" "2018.3.1" "Salt"
.TH "SPM" "1" "Jun 14, 2018" "2018.3.2" "Salt"
.SH NAME
spm \- Salt Package Manager Command
.

View File

@ -5054,6 +5054,21 @@ https://github.com/ytoolshed/range/wiki/%22yamlfile%22-module-file-spec
Include Configuration
=====================
Configuration can be loaded from multiple files. The order in which this is
done is:
1. The master config file itself
2. The files matching the glob in :conf_master:`default_include`
3. The files matching the glob in :conf_master:`include` (if defined)
Each successive step overrides any values defined in the previous steps.
Therefore, any config options defined in one of the
:conf_master:`default_include` files would override the same value in the
master config file, and any options defined in :conf_master:`include` would
override both.
.. conf_master:: default_include
``default_include``

View File

@ -2928,7 +2928,22 @@ at the moment a single state fails
Include Configuration
=====================
.. conf_minion:: include
Configuration can be loaded from multiple files. The order in which this is
done is:
1. The minion config file itself
2. The files matching the glob in :conf_minion:`default_include`
3. The files matching the glob in :conf_minion:`include` (if defined)
Each successive step overrides any values defined in the previous steps.
Therefore, any config options defined in one of the
:conf_minion:`default_include` files would override the same value in the
minion config file, and any options defined in :conf_minion:`include` would
override both.
.. conf_minion:: default_include
``default_include``
-------------------
@ -2946,6 +2961,7 @@ file.
files are prefixed with an underscore. A common example of this is the
``_schedule.conf`` file.
.. conf_minion:: include
``include``
-----------

View File

@ -21,11 +21,21 @@ SaltStack has its own coding style guide that informs contributors on various co
approaches. Please review the :ref:`Salt Coding Style <coding-style>` documentation
for information about Salt's particular coding patterns.
Within the :ref:`Salt Coding Style <coding-style>` documentation, there is a section
about running Salt's ``.pylintrc`` file. SaltStack recommends running the ``.pylintrc``
file on any files you are changing with your code contribution before submitting a
pull request to Salt's repository. Please see the :ref:`Linting<pylint-instructions>`
documentation for more information.
Within the :ref:`Salt Coding Style <coding-style>` documentation, there is a
section about running Salt's ``.testing.pylintrc`` file. SaltStack recommends
running the ``.testing.pylintrc`` file on any files you are changing with your
code contribution before submitting a pull request to Salt's repository. Please
see the :ref:`Linting<pylint-instructions>` documentation for more information.
.. note::
There are two pylint files in the ``salt`` directory. One is the
``.pylintrc`` file and the other is the ``.testing.pylintrc`` file. The
tests that run in Jenkins against GitHub Pull Requests use
``.testing.pylintrc``. The ``testing.pylintrc`` file is a little less
strict than the ``.pylintrc`` and is used to make it easier for contributors
to submit changes. The ``.pylintrc`` file can be used for linting, but the
``testing.pylintrc`` is the source of truth when submitting pull requests.
.. _github-pull-request:

View File

@ -22,21 +22,31 @@ improve Salt)!!
Linting
=======
Most Salt style conventions are codified in Salt's ``.pylintrc`` file. Salt's
pylint file has two dependencies: pylint_ and saltpylint_. You can install
these dependencies with ``pip``:
Most Salt style conventions are codified in Salt's ``.testing.pylintrc`` file.
Salt's pylint file has two dependencies: pylint_ and saltpylint_. You can
install these dependencies with ``pip``:
.. code-block:: bash
pip install pylint
pip install saltpylint
The ``.pylintrc`` file is found in the root of the Salt project and can be passed
as an argument to the pylint_ program as follows:
The ``.testing.pylintrc`` file is found in the root of the Salt project and can
be passed as an argument to the pylint_ program as follows:
.. code-block:: bash
pylint --rcfile=/path/to/salt/.pylintrc salt/dir/to/lint
pylint --rcfile=/path/to/salt/.testing.pylintrc salt/dir/to/lint
.. note::
There are two pylint files in the ``salt`` directory. One is the
``.pylintrc`` file and the other is the ``.testing.pylintrc`` file. The
tests that run in Jenkins against GitHub Pull Requests use
``.testing.pylintrc``. The ``testing.pylintrc`` file is a little less
strict than the ``.pylintrc`` and is used to make it easier for contributors
to submit changes. The ``.pylintrc`` file can be used for linting, but the
``testing.pylintrc`` is the source of truth when submitting pull requests.
.. _pylint: http://www.pylint.org
.. _saltpylint: https://github.com/saltstack/salt-pylint

View File

@ -1,3 +1,5 @@
.. _event-system:
============
Event System
============
@ -12,8 +14,8 @@ Event Bus
The event system is comprised of a two primary components, which make up the
concept of an Event Bus:
* The event sockets which publishes events.
* The event library which can listen to events and send events into the salt system.
- The event sockets, which publish events
- The event library, which can listen to events and send events into the salt system
Events are published onto the event bus and event bus subscribers listen for the
published events.
@ -237,4 +239,4 @@ done at the CLI:
'success': True,
'message': "It works!",
}
)
)

View File

@ -886,6 +886,10 @@ Example:
encoding (usually a ``unicode`` type). This filter was incorrectly-named
when it was added. ``json_decode_list`` will be supported until the Neon
release.
.. deprecated:: 2018.3.3,Fluorine
The :jinja_ref:`tojson` filter accomplishes what this filter was designed
to do, making this filter redundant.
Recursively encodes all string elements of the list to bytes.
@ -915,6 +919,9 @@ Returns:
encoding (usually a ``unicode`` type). This filter was incorrectly-named
when it was added. ``json_decode_dict`` will be supported until the Neon
release.
.. deprecated:: 2018.3.3,Fluorine
The :jinja_ref:`tojson` filter accomplishes what this filter was designed
to do, making this filter redundant.
Recursively encodes all string items in the dictionary to bytes.
@ -934,6 +941,22 @@ Returns:
{'a': '\xd0\x94'}
.. jinja_ref:: tojson
``tojson``
----------
.. versionadded:: 2018.3.3,Fluorine
Dumps a data structure to JSON.
This filter was added to provide this functionality to hosts which have a
Jinja release older than version 2.9 installed. If Jinja 2.9 or newer is
installed, then the upstream version of the filter will be used. See the
`upstream docs`__ for more information.
.. __: http://jinja.pocoo.org/docs/2.10/templates/#tojson
.. jinja_ref:: random_hash
``random_hash``
@ -1125,6 +1148,40 @@ Returns:
}'
}
.. jinja_ref:: traverse
``traverse``
------------
.. versionadded:: 2018.3.3
Traverse a dict or list using a colon-delimited target string.
The target 'foo:bar:0' will return data['foo']['bar'][0] if this value exists,
and will otherwise return the provided default value.
Example:
.. code-block:: jinja
{{ {'a1': {'b1': {'c1': 'foo'}}, 'a2': 'bar'} | traverse('a1:b1', 'default') }}
Returns:
.. code-block:: python
{'c1': 'foo'}
.. code-block:: jinja
{{ {'a1': {'b1': {'c1': 'foo'}}, 'a2': 'bar'} | traverse('a2:b2', 'default') }}
Returns:
.. code-block:: python
'default'
.. _`builtin filters`: http://jinja.pocoo.org/docs/templates/#builtin-filters
.. _`timelib`: https://github.com/pediapress/timelib/

View File

@ -15,6 +15,23 @@ Statistics
- Contributors: **52** (`Ch3LL`_, `DmitryKuzmenko`_, `GwiYeong`_, `L4rS6`_, `SteffenKockel`_, `The-Loeki`_, `amendlik`_, `andreaspe`_, `angeloudy`_, `aphor`_, `bdrung`_, `cebe`_, `ciiqr`_, `damon-atkins`_, `danlsgiga`_, `ddoh94`_, `dmurphy18`_, `dwoz`_, `eliasp`_, `frogunder`_, `garethgreenaway`_, `gclinch`_, `gtmanfred`_, `jfindlay`_, `kstreee`_, `marccardinal`_, `mcalmer`_, `mchugh19`_, `meaksh`_, `michelsen`_, `nullify005`_, `oarmstrong`_, `oeuftete`_, `philpep`_, `racker-markh`_, `rallytime`_, `redbaron4`_, `roaldnefs`_, `rongshengfang`_, `rongzeng54`_, `rrroo`_, `samilaine`_, `samodid`_, `skizunov`_, `terminalmage`_, `tintoy`_, `twangboy`_, `viktordaniel`_, `vutny`_, `while0pass`_, `whytewolf`_, `zer0def`_)
Changes to :py:func:`file.blockreplace <salt.states.file.blockreplace>` State
=============================================================================
The ``append_newline`` argument was added to this state. Additionally, to
improve idempotence, if the string represented by ``marker_end`` is found in
the middle of the line, the content preceding the marker will be removed when
the block is replaced. This allows one to remove ``append_newline: False`` from
the SLS and have the block properly replaced if the end of the content block is
immediately followed by the ``marker_end`` (i.e. no newline before the marker).
.. note::
This will require changes to your SLS if your ``marker_end`` does not
include the very beginning of the content you want to keep.
See the :py:func:`file.blockreplace <salt.states.file.blockreplace>` state
documentation for further information.
Changelog for v2017.7.4..v2017.7.5
==================================

View File

@ -1,27 +1,67 @@
========================================
In Progress: Salt 2017.7.7 Release Notes
========================================
===========================
Salt 2017.7.7 Release Notes
===========================
Version 2017.7.7 is an **unreleased** bugfix release for :ref:`2017.7.0 <release-2017-7-0>`.
This release is still in progress and has not been released yet.
Version 2017.7.7 is a bugfix release for :ref:`2017.7.0 <release-2017-7-0>`.
The ``2017.7.7`` release contains only a single fix for Issue `#48038`_, which
is a critical bug that occurs in a multi-syndic setup where the same job is run
multiple times on a minion.
The ``2017.7.7`` release contains only a small number of fixes, which are detailed
below.
This release fixes two critical issues.
The first is Issue `#48038`_, which is a critical bug that occurs in a multi-syndic
setup where the same job is run multiple times on a minion.
The second issue is `#48130`_. This bug appears in certain setups where the Master
reports a Minion time-out, even though the job is still running on the Minion.
Both of these issues have been fixed with this release.
Statistics
==========
- Total Merges: **1**
- Total Issue References: **1**
- Total PR References: **2**
- Total Merges: **5**
- Total Issue References: **2**
- Total PR References: **6**
- Contributors: **2** (`garethgreenaway`_, `rallytime`_)
- Contributors: **3** (`garethgreenaway`_, `gtmanfred`_, `rallytime`_)
Changelog for v2017.7.6..v2017.7.7
==================================
*Generated at: 2018-06-14 15:43:34 UTC*
*Generated at: 2018-06-17 19:26:52 UTC*
* **ISSUE** `#48130`_: (`rmarchei`_) Minion timeouts with 2018.3.1 (refs: `#48157`_)
* **PR** `#48157`_: (`gtmanfred`_) always listen when gathering job info
@ *2018-06-17 19:04:09 UTC*
* 8af4452134 Merge pull request `#48157`_ from gtmanfred/2017.7.7
* d8209e8a40 always listen when gathering job info
* **PR** `#48140`_: (`rallytime`_) Update man pages for 2017.7.7
@ *2018-06-14 21:22:43 UTC*
* b98c52ee51 Merge pull request `#48140`_ from rallytime/man-pages-2017.7.7
* 8893bf0d4c Update man pages for 2017.7.7
* **PR** `#48136`_: (`gtmanfred`_) [2017.7.7] bootstrap kitchen branch tests with 2017.7.6
@ *2018-06-14 21:20:16 UTC*
* baa0363336 Merge pull request `#48136`_ from gtmanfred/2017.7.7
* fce1c31146 bootstrap kitchen branch tests with 2017.7.6
* **PR** `#48134`_: (`rallytime`_) Add release notes file for 2017.7.7
@ *2018-06-14 16:31:34 UTC*
* b0ba08f4d9 Merge pull request `#48134`_ from rallytime/release-notes-2017.7.7
* 217005b8f1 Add missing `v` for tag reference
* d53569d1e3 Add release notes file for 2017.7.7
* **ISSUE** `#48038`_: (`austinpapp`_) jobs are not dedup'ing minion side (refs: `#48075`_)
@ -37,6 +77,13 @@ Changelog for v2017.7.6..v2017.7.7
.. _`#48038`: https://github.com/saltstack/salt/issues/48038
.. _`#48075`: https://github.com/saltstack/salt/pull/48075
.. _`#48098`: https://github.com/saltstack/salt/pull/48098
.. _`#48130`: https://github.com/saltstack/salt/issues/48130
.. _`#48134`: https://github.com/saltstack/salt/pull/48134
.. _`#48136`: https://github.com/saltstack/salt/pull/48136
.. _`#48140`: https://github.com/saltstack/salt/pull/48140
.. _`#48157`: https://github.com/saltstack/salt/pull/48157
.. _`austinpapp`: https://github.com/austinpapp
.. _`garethgreenaway`: https://github.com/garethgreenaway
.. _`gtmanfred`: https://github.com/gtmanfred
.. _`rallytime`: https://github.com/rallytime
.. _`rmarchei`: https://github.com/rmarchei

View File

@ -43,6 +43,23 @@ replaced with ``--update-roster``, which will enable salt-ssh to add minions
to the flat roster file. This behavior can also be enabled by setting
``ssh_update_roster: True`` in the master config file.
Changes to :py:func:`file.blockreplace <salt.states.file.blockreplace>` State
=============================================================================
The ``append_newline`` argument was added to this state. Additionally, to
improve idempotence, if the string represented by ``marker_end`` is found in
the middle of the line, the content preceding the marker will be removed when
the block is replaced. This allows one to remove ``append_newline: False`` from
the SLS and have the block properly replaced if the end of the content block is
immediately followed by the ``marker_end`` (i.e. no newline before the marker).
.. note::
This will require changes to your SLS if your ``marker_end`` does not
include the very beginning of the content you want to keep.
See the :py:func:`file.blockreplace <salt.states.file.blockreplace>` state
documentation for further information.
Changelog for v2018.3.0..v2018.3.1
==================================

View File

@ -1,9 +1,8 @@
========================================
In Progress: Salt 2018.3.2 Release Notes
========================================
===========================
Salt 2018.3.2 Release Notes
===========================
Version 2018.3.2 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.
Version 2018.3.2 is a bugfix release for :ref:`2018.3.0 <release-2018-3-0>`.
The ``2018.3.2`` release contains only a small number of fixes, which are detailed
below.

View File

@ -14,3 +14,20 @@ Improves timezone detection by using the pytz module.
Adds ``timezone.list`` to list supported timezones in either Windows or Unix
format.
New Jinja Filter
================
The :jinja_ref:`tojson` filter (from Jinja 2.9 and later) has been ported to
Salt, and will be used when this filter is not available. This allows older LTS
releases such as CentOS 7 and Ubuntu 14.04 to use this filter.
You should use this filter any time you wish to dump a list or dictionary into
an SLS file, to ensure that the result is able to be loaded by the YAML
renderer. For example:
.. code-block:: jinja
foo:
bar.baz:
- some_arg: {{ mydict|tojson }}

View File

@ -330,7 +330,13 @@ Nested pillar values can also be set via the command line:
.. code-block:: bash
salt '*' state.sls my_sls_file pillar='{"foo": {"bar": "baz"}}'
salt '*' state.sls my_sls_file pillar='{"foo": {"bar": "baz"}}'
Lists can be passed via command line pillar data as follows:
.. code-block:: bash
salt '*' state.sls my_sls_file pillar='{"some_list": ["foo", "bar", "baz"]}'
.. note::

View File

@ -139,13 +139,18 @@ where it is necessary to invoke the same function from a custom :ref:`outputter
<all-salt.output>`/returner, as well as an execution module.
Utility modules placed in ``salt://_utils/`` will be synced to the minions when
any of the following Salt functions are called:
a :ref:`highstate <running-highstate>` is run, as well as when any of the
following Salt functions are called:
* :mod:`state.apply <salt.modules.state.apply_>`
* :mod:`saltutil.sync_utils <salt.modules.saltutil.sync_utils>`
* :mod:`saltutil.sync_all <salt.modules.saltutil.sync_all>`
* :py:func:`saltutil.sync_utils <salt.modules.saltutil.sync_utils>`
* :py:func:`saltutil.sync_all <salt.modules.saltutil.sync_all>`
As of the Fluorine release, as well as 2017.7.7 and 2018.3.2 in their
respective release cycles, the ``sync`` argument to :py:func:`state.apply
<salt.modules.state.apply_>`/:py:func:`state.sls <salt.modules.state.sls>` can
be used to sync custom types when running individual SLS files.
To sync to the Master, use either of the following:
* :mod:`saltutil.sync_utils <salt.runners.saltutil.sync_utils>`
* :mod:`saltutil.sync_all <salt.runners.saltutil.sync_all>`
* :py:func:`saltutil.sync_utils <salt.runners.saltutil.sync_utils>`
* :py:func:`saltutil.sync_all <salt.runners.saltutil.sync_all>`

View File

@ -130,20 +130,18 @@ If Defined ProgramFiles(x86) (
)
@echo.
@echo Copying VCRedist to Prerequisites
@echo ----------------------------------------------------------------------
:: Make sure the "prereq" directory exists
If NOT Exist "%PreDir%" mkdir "%PreDir%"
:: Set the location of the vcredist to download
If %Python%==3 (
Set Url64="http://repo.saltstack.com/windows/dependencies/64/vcredist_x64_2015.exe"
Set Url32="http://repo.saltstack.com/windows/dependencies/32/vcredist_x86_2015.exe"
:: Don't include the vcredist for Py3 installations
If %Python%==3 goto :vcredist_end
) Else (
Set Url64="http://repo.saltstack.com/windows/dependencies/64/vcredist_x64_2008_mfc.exe"
Set Url32="http://repo.saltstack.com/windows/dependencies/32/vcredist_x86_2008_mfc.exe"
)
@echo Copying VCRedist to Prerequisites
@echo ----------------------------------------------------------------------
:: Set the location of the vcredist to download
Set Url64="http://repo.saltstack.com/windows/dependencies/64/vcredist_x64_2008_mfc.exe"
Set Url32="http://repo.saltstack.com/windows/dependencies/32/vcredist_x86_2008_mfc.exe"
:: Check for 64 bit by finding the Program Files (x86) directory
If Defined ProgramFiles(x86) (
@ -153,6 +151,8 @@ If Defined ProgramFiles(x86) (
)
@echo.
:vcredist_end
:: Remove the fixed path in .exe files
@echo Removing fixed path from .exe files
@echo ----------------------------------------------------------------------

View File

@ -415,26 +415,13 @@ Section -Prerequisites
Var /Global CheckVcRedist
StrCpy $CheckVcRedist "False"
# Visual C++ 2015 redist packages
!define PY3_VC_REDIST_NAME "VC_Redist_2015"
!define PY3_VC_REDIST_X64_GUID "{50A2BC33-C9CD-3BF1-A8FF-53C10A0B183C}"
!define PY3_VC_REDIST_X86_GUID "{BBF2AC74-720C-3CB3-8291-5E34039232FA}"
# Visual C++ 2008 SP1 MFC Security Update redist packages
!define PY2_VC_REDIST_NAME "VC_Redist_2008_SP1_MFC"
!define PY2_VC_REDIST_X64_GUID "{5FCE6D76-F5DC-37AB-B2B8-22AB8CEDB1D4}"
!define PY2_VC_REDIST_X86_GUID "{9BE518E6-ECC6-35A9-88E4-87755C07200F}"
${If} ${PYTHON_VERSION} == 3
StrCpy $VcRedistName ${PY3_VC_REDIST_NAME}
${If} ${CPUARCH} == "AMD64"
StrCpy $VcRedistGuid ${PY3_VC_REDIST_X64_GUID}
${Else}
StrCpy $VcRedistGuid ${PY3_VC_REDIST_X86_GUID}
${EndIf}
StrCpy $CheckVcRedist "True"
${Else}
# VCRedist only needs to be installed for Python 2
${If} ${PYTHON_VERSION} == 2
StrCpy $VcRedistName ${PY2_VC_REDIST_NAME}
${If} ${CPUARCH} == "AMD64"

View File

@ -77,6 +77,7 @@ Function Get-Settings {
"SSLeay" = "ssleay32.dll"
"OpenSSLLic" = "OpenSSL_License.txt"
"msvcr" = "msvcr120.dll"
"Libsodium" = "libsodium.dll"
}
$ini.Add("64bitDLLs", $64bitDLLs)
@ -86,6 +87,7 @@ Function Get-Settings {
"SSLeay" = "ssleay32.dll"
"OpenSSLLic" = "OpenSSL_License.txt"
"msvcr" = "msvcr120.dll"
"Libsodium" = "libsodium.dll"
}
$ini.Add("32bitDLLs", $32bitDLLs)

View File

@ -9,34 +9,36 @@ salt for Windows with their corresponding licenses:
| certifi | ISC |
| cffi | MIT |
| CherryPy | BSD |
| cryptography | BSD |
| enum34 | BSD |
| futures | BSD |
| gitdb | BSD |
| GitPython | BSD |
| idna | BSD-like |
| ioflo | Apache 2.0 |
| ioloop | MIT |
| ipaddress | PSF |
| Jinja2 | BSD |
| libnacl | --- |
| libnacl | Apache |
| lxml | BSD |
| Mako | MIT |
| MarkupSafe | BSD |
| msgpack-python | --- |
| msgpack-python | Apache 2.0 |
| pip | MIT |
| psutil | BSD |
| pyasn1 | BSD |
| pycparser | BSD |
| pycurl | LGPL + MIT |
| PyMySQL | MIT |
| pypiwin32 | PSF |
| PyOpenSSL | Apache 2.0 |
| python-certifi-win32 | BSD |
| python-dateutil | Simplified BSD |
| python-gnupg | BSD |
| pywin32 | PSF |
| PyYAML | MIT |
| pyzmq | LGPL + BSD |
| requests | Apache 2.0 |
| setuptools | MIT |
| singledispatch | MIT |
| six | MIT |
| smmap | BSD |
| timelib | ZLIB/PHP |
| tornado | Apache 2.0 |

View File

@ -12,6 +12,7 @@ idna==2.5
ioloop==0.1a0
ipaddress==1.0.18
Jinja2==2.9.6
libnacl==1.6.1 # required by the nacl module
lxml==3.7.3
Mako==1.0.6
MarkupSafe==1.0
@ -23,6 +24,7 @@ pycrypto==2.6.1
pycurl==7.43.0
PyMySQL==0.7.11
pyOpenSSL==17.5.0
#python-certifi-win32==1.2
python-dateutil==2.6.1
python-gnupg==0.4.1
pythonnet==2.3.0

View File

@ -11,3 +11,4 @@ testinfra>=1.7.0
# satisfy other requirements, and httpretty 0.8.10 has bugs in setup.py that
# prevent it from being successfully installed (at least on Python 3.4).
httpretty; python_version >= '3.4'
pylint==1.6.5

View File

@ -22,6 +22,7 @@ psutil
pyvmomi
setproctitle
cherrypy; sys.platform != 'win32' and sys.platform != 'darwin'
ldap; sys.platform != 'win32' and sys.platform != 'darwin'
pyinotify; sys.platform != 'win32' and sys.platform != 'darwin'
PyMySQL; sys.platform != 'win32' and sys.platform != 'darwin'
jsonschema
@ -34,3 +35,5 @@ dnspython
SaltTesting==2017.6.1
junos-eznc
jxmlease
croniter
kazoo

View File

@ -1,4 +1,5 @@
-r base.txt
pycrypto>=2.6.1
pyzmq>=2.2.0
pyzmq>=2.2.0,<17.1.0; python_version == '3.4' # pyzmq 17.1.0 stopped building wheels for python3.4
pyzmq>=2.2.0; python_version != '3.4'

View File

@ -83,13 +83,19 @@ def beacon(config):
it will override the previously defined threshold.
'''
parts = psutil.disk_partitions(all=False)
parts = psutil.disk_partitions(all=True)
ret = []
for mounts in config:
mount = next(iter(mounts))
# Because we're using regular expressions
# if our mount doesn't end with a $, insert one.
mount_re = mount
if not mount.endswith('$'):
mount_re = '{0}$'.format(mount)
for part in parts:
if re.match(mount, part.mountpoint):
if re.match(mount_re, part.mountpoint):
_mount = part.mountpoint
try:
@ -100,7 +106,7 @@ def beacon(config):
current_usage = _current_usage.percent
monitor_usage = mounts[mount]
log.info('current_usage %s', current_usage)
log.debug('current_usage %s', current_usage)
if '%' in monitor_usage:
monitor_usage = re.sub('%', '', monitor_usage)
monitor_usage = float(monitor_usage)

View File

@ -11,6 +11,7 @@ import time
# Import salt libs
import salt.utils.path
import salt.utils.stringutils
import salt.utils.vt
__virtualname__ = 'sh'
@ -81,14 +82,14 @@ def beacon(config):
stream_stdout=False,
stream_stderr=False)
__context__[pkey][pid]['user'] = ps_out[pid].get('user')
for pid in __context__[pkey]:
for pid in list(__context__[pkey]):
out = ''
err = ''
while __context__[pkey][pid]['vt'].has_unread_data:
tout, terr = __context__[pkey][pid]['vt'].recv()
if not terr:
break
out += tout
out += salt.utils.stringutils.to_unicode(tout or '')
err += terr
for line in err.split('\n'):
event = {'args': [],

View File

@ -172,8 +172,12 @@ def store(bank, key, data):
'''
_init_client()
data = __context__['serial'].dumps(data)
query = "REPLACE INTO {0} (bank, etcd_key, data) values('{1}', '{2}', " \
"'{3}')".format(_table_name, bank, key, data)
query = b"REPLACE INTO {0} (bank, etcd_key, data) values('{1}', '{2}', " \
b"'{3}')".format(_table_name,
bank,
key,
data)
cur, cnt = run_query(client, query)
cur.close()
if cnt not in (1, 2):

View File

@ -229,7 +229,7 @@ class LocalClient(object):
# Looks like the timeout is invalid, use config
return self.opts['timeout']
def gather_job_info(self, jid, tgt, tgt_type, **kwargs):
def gather_job_info(self, jid, tgt, tgt_type, listen=True, **kwargs):
'''
Return the information about a given job
'''
@ -241,6 +241,7 @@ class LocalClient(object):
arg=[jid],
tgt_type=tgt_type,
timeout=timeout,
listen=listen,
**kwargs
)

View File

@ -350,7 +350,9 @@ class SSH(object):
return
hostname = self.opts['tgt'].split('@')[-1]
needs_expansion = '*' not in hostname and salt.utils.network.is_reachable_host(hostname)
needs_expansion = '*' not in hostname and \
salt.utils.network.is_reachable_host(hostname) and \
salt.utils.network.is_ip(hostname)
if needs_expansion:
hostname = salt.utils.network.ip_to_host(hostname)
if hostname is None:
@ -1245,7 +1247,10 @@ ARGS = {10}\n'''.format(self.minion_config,
shim_tmp_file.write(salt.utils.stringutils.to_bytes(cmd_str))
# Copy shim to target system, under $HOME/.<randomized name>
target_shim_file = '.{0}.{1}'.format(binascii.hexlify(os.urandom(6)), extension)
target_shim_file = '.{0}.{1}'.format(
binascii.hexlify(os.urandom(6)).decode('ascii'),
extension
)
if self.winrm:
target_shim_file = saltwinshell.get_target_shim_file(self, target_shim_file)
self.shell.send(shim_tmp_file.name, target_shim_file, makedirs=True)

View File

@ -167,7 +167,7 @@ def salt_refs(data, ret=None):
return ret
def prep_trans_tar(opts, file_client, chunks, file_refs, pillar=None, id_=None, roster_grains=None):
def prep_trans_tar(file_client, chunks, file_refs, pillar=None, id_=None, roster_grains=None):
'''
Generate the execution package from the saltenv file refs and a low state
data structure

View File

@ -36,6 +36,59 @@ __func_alias__ = {
log = logging.getLogger(__name__)
def _ssh_state(chunks, st_kwargs,
kwargs, test=False):
'''
Function to run a state with the given chunk via salt-ssh
'''
file_refs = salt.client.ssh.state.lowstate_file_refs(
chunks,
_merge_extra_filerefs(
kwargs.get('extra_filerefs', ''),
__opts__.get('extra_filerefs', '')
)
)
# Create the tar containing the state pkg and relevant files.
trans_tar = salt.client.ssh.state.prep_trans_tar(
__context__['fileclient'],
chunks,
file_refs,
__pillar__,
st_kwargs['id_'])
trans_tar_sum = salt.utils.hashutils.get_hash(trans_tar, __opts__['hash_type'])
cmd = 'state.pkg {0}/salt_state.tgz test={1} pkg_sum={2} hash_type={3}'.format(
__opts__['thin_dir'],
test,
trans_tar_sum,
__opts__['hash_type'])
single = salt.client.ssh.Single(
__opts__,
cmd,
fsclient=__context__['fileclient'],
minion_opts=__salt__.minion_opts,
**st_kwargs)
single.shell.send(
trans_tar,
'{0}/salt_state.tgz'.format(__opts__['thin_dir']))
stdout, stderr, _ = single.cmd_block()
# Clean up our tar
try:
os.remove(trans_tar)
except (OSError, IOError):
pass
# Read in the JSON data and return the data structure
try:
return salt.utils.data.decode(salt.utils.json.loads(stdout, object_hook=salt.utils.data.encode_dict))
except Exception as e:
log.error("JSON Render failed for: %s\n%s", stdout, stderr)
log.error(str(e))
# If for some reason the json load fails, return the stdout
return salt.utils.data.decode(stdout)
def _set_retcode(ret, highstate=None):
'''
Set the return code based on the data back from the state system
@ -165,7 +218,6 @@ def sls(mods, saltenv='base', test=None, exclude=None, **kwargs):
# Create the tar containing the state pkg and relevant files.
_cleanup_slsmod_low_data(chunks)
trans_tar = salt.client.ssh.state.prep_trans_tar(
opts,
__context__['fileclient'],
chunks,
file_refs,
@ -310,7 +362,6 @@ def low(data, **kwargs):
# Create the tar containing the state pkg and relevant files.
trans_tar = salt.client.ssh.state.prep_trans_tar(
__opts__,
__context__['fileclient'],
chunks,
file_refs,
@ -401,7 +452,6 @@ def high(data, **kwargs):
# Create the tar containing the state pkg and relevant files.
_cleanup_slsmod_low_data(chunks)
trans_tar = salt.client.ssh.state.prep_trans_tar(
opts,
__context__['fileclient'],
chunks,
file_refs,
@ -647,7 +697,6 @@ def highstate(test=None, **kwargs):
# Create the tar containing the state pkg and relevant files.
_cleanup_slsmod_low_data(chunks)
trans_tar = salt.client.ssh.state.prep_trans_tar(
opts,
__context__['fileclient'],
chunks,
file_refs,
@ -730,7 +779,6 @@ def top(topfn, test=None, **kwargs):
# Create the tar containing the state pkg and relevant files.
_cleanup_slsmod_low_data(chunks)
trans_tar = salt.client.ssh.state.prep_trans_tar(
opts,
__context__['fileclient'],
chunks,
file_refs,
@ -849,6 +897,7 @@ def sls_id(id_, mods, test=None, queue=False, **kwargs):
salt '*' state.sls_id my_state my_module,a_common_module
'''
st_kwargs = __salt__.kwargs
conflict = _check_queue(queue, kwargs)
if conflict is not None:
return conflict
@ -861,13 +910,11 @@ def sls_id(id_, mods, test=None, queue=False, **kwargs):
if opts['saltenv'] is None:
opts['saltenv'] = 'base'
try:
st_ = salt.state.HighState(opts,
proxy=__proxy__,
initial_pillar=_get_initial_pillar(opts))
except NameError:
st_ = salt.state.HighState(opts,
initial_pillar=_get_initial_pillar(opts))
st_ = salt.client.ssh.state.SSHHighState(
__opts__,
__pillar__,
__salt__,
__context__['fileclient'])
if not _check_pillar(kwargs, st_.opts['pillar']):
__context__['retcode'] = 5
@ -878,10 +925,7 @@ def sls_id(id_, mods, test=None, queue=False, **kwargs):
if isinstance(mods, six.string_types):
split_mods = mods.split(',')
st_.push_active()
try:
high_, errors = st_.render_highstate({opts['saltenv']: split_mods})
finally:
st_.pop_active()
high_, errors = st_.render_highstate({opts['saltenv']: split_mods})
errors += st_.state.verify_high(high_)
# Apply requisites to high data
high_, req_in_errors = st_.state.requisite_in(high_)
@ -893,17 +937,22 @@ def sls_id(id_, mods, test=None, queue=False, **kwargs):
__context__['retcode'] = 1
return errors
chunks = st_.state.compile_high_data(high_)
ret = {}
for chunk in chunks:
if chunk.get('__id__', '') == id_:
ret.update(st_.state.call_chunk(chunk, {}, chunks))
chunk = [x for x in chunks if x.get('__id__', '') == id_]
_set_retcode(ret, highstate=highstate)
if not ret:
if not chunk:
raise SaltInvocationError(
'No matches for ID \'{0}\' found in SLS \'{1}\' within saltenv '
'\'{2}\''.format(id_, mods, opts['saltenv'])
)
ret = _ssh_state(chunk,
st_kwargs,
kwargs,
test=test)
_set_retcode(ret, highstate=highstate)
# Work around Windows multiprocessing bug, set __opts__['test'] back to
# value from before this function was run.
__opts__['test'] = orig_test
return ret
@ -1093,7 +1142,6 @@ def single(fun, name, test=None, **kwargs):
# Create the tar containing the state pkg and relevant files.
trans_tar = salt.client.ssh.state.prep_trans_tar(
opts,
__context__['fileclient'],
chunks,
file_refs,

View File

@ -372,7 +372,8 @@ def create(vm_):
for iface_xml in domain_xml.findall('./devices/interface'):
iface_xml.remove(iface_xml.find('./mac'))
# enable IP learning, this might be a default behaviour...
if iface_xml.find("./filterref/parameter[@name='CTRL_IP_LEARNING']") is None:
# Don't always enable since it can cause problems through libvirt-4.5
if ip_source == 'ip-learning' and iface_xml.find("./filterref/parameter[@name='CTRL_IP_LEARNING']") is None:
iface_xml.append(ElementTree.fromstring(IP_LEARNING_XML))
# If a qemu agent is defined we need to fix the path to its socket
@ -422,7 +423,7 @@ def create(vm_):
else:
raise SaltCloudExecutionFailure("Disk type '{0}' not supported".format(disk_type))
clone_xml = ElementTree.tostring(domain_xml)
clone_xml = salt.utils.stringutils.to_str(ElementTree.tostring(domain_xml))
log.debug("Clone XML '%s'", clone_xml)
validate_flags = libvirt.VIR_DOMAIN_DEFINE_VALIDATE if validate_xml else 0
@ -615,7 +616,7 @@ def create_volume_xml(volume):
log.debug("Volume: %s", dir(volume))
volume_xml.find('capacity').text = six.text_type(volume.info()[1])
volume_xml.find('./target/path').text = volume.path()
xml_string = ElementTree.tostring(volume_xml)
xml_string = salt.utils.stringutils.to_str(ElementTree.tostring(volume_xml))
log.debug("Creating %s", xml_string)
return xml_string
@ -641,7 +642,7 @@ def create_volume_with_backing_store_xml(volume):
log.debug("volume: %s", dir(volume))
volume_xml.find('capacity').text = six.text_type(volume.info()[1])
volume_xml.find('./backingStore/path').text = volume.path()
xml_string = ElementTree.tostring(volume_xml)
xml_string = salt.utils.stringutils.to_str(ElementTree.tostring(volume_xml))
log.debug("Creating %s", xml_string)
return xml_string

View File

@ -206,8 +206,8 @@ def get_rsa_pub_key(path):
'''
log.debug('salt.crypt.get_rsa_pub_key: Loading public key')
if HAS_M2:
with salt.utils.files.fopen(path) as f:
data = f.read().replace(b'RSA ', '')
with salt.utils.files.fopen(path, 'rb') as f:
data = f.read().replace(b'RSA ', b'')
bio = BIO.MemoryBuffer(data)
key = RSA.load_pub_key_bio(bio)
else:
@ -608,9 +608,17 @@ class AsyncAuth(object):
error = SaltClientError('Detect mode is on')
break
if self.opts.get('caller'):
print('Minion failed to authenticate with the master, '
'has the minion key been accepted?')
sys.exit(2)
# We have a list of masters, so we should break
# and try the next one in the list.
if self.opts.get('local_masters', None):
error = SaltClientError('Minion failed to authenticate'
' with the master, has the '
'minion key been accepted?')
break
else:
print('Minion failed to authenticate with the master, '
'has the minion key been accepted?')
sys.exit(2)
if acceptance_wait_time:
log.info(
'Waiting %s seconds before retry.', acceptance_wait_time

View File

@ -1,26 +1,84 @@
# -*- coding: utf-8 -*-
'''
An engine that reads messages from Slack and can act on them.
It has two major uses.
1. When the ``control`` parameter is set to ``True`` and a message is prefaced
with the ``trigger`` (which defaults to ``!``) then the engine will
validate that the user has permission, and if so will run the command
2. In addition, when the parameter ``fire_all`` is set (defaults to False),
all other messages (the messages that aren't control messages) will be
fired off to the salt event bus with the tag prefixed by the string
provided by the ``tag`` config option (defaults to ``salt/engines/slack``).
This allows for configuration to be gotten from either the engine config, or from
the saltmaster's minion pillar.
An engine that reads messages from Slack and can act on them
.. versionadded: 2016.3.0
:configuration: Example configuration using only a 'default' group. The default
group is not special. In addition, other groups are being loaded from
pillars.
:depends: `slackclient <https://pypi.org/project/slackclient/>`_ Python module
.. important::
This engine requires a bot user. To create a bot user, first go to the
**Custom Integrations** page in your Slack Workspace. Copy and paste the
following URL, and replace ``myworkspace`` with the proper value for your
workspace:
``https://myworkspace.slack.com/apps/manage/custom-integrations``
Next, click on the ``Bots`` integration and request installation. Once
approved by an admin, you will be able to proceed with adding the bot user.
Once the bot user has been added, you can configure it by adding an avatar,
setting the display name, etc. You will also at this time have access to
your API token, which will be needed to configure this engine.
Finally, add this bot user to a channel by switching to the channel and
using ``/invite @mybotuser``. Keep in mind that this engine will process
messages from each channel in which the bot is a member, so it is
recommended to narrowly define the commands which can be executed, and the
Slack users which are allowed to run commands.
This engine has two boolean configuration parameters that toggle specific
features (both default to ``False``):
1. ``control`` - If set to ``True``, then any message which starts with the
trigger string (which defaults to ``!`` and can be overridden by setting the
``trigger`` option in the engine configuration) will be interpreted as a
Salt CLI command and the engine will attempt to run it. The permissions
defined in the various ``groups`` will determine if the Slack user is
allowed to run the command. The ``targets`` and ``default_target`` options
can be used to set targets for a given command, but the engine can also read
the following two keyword arguments:
- ``target`` - The target expression to use for the command
- ``tgt_type`` - The match type, can be one of ``glob``, ``list``,
``pcre``, ``grain``, ``grain_pcre``, ``pillar``, ``nodegroup``, ``range``,
``ipcidr``, or ``compound``. The default value is ``glob``.
Here are a few examples:
.. code-block:: text
!test.ping target=*
!state.apply foo target=os:CentOS tgt_type=grain
!pkg.version mypkg target=role:database tgt_type=pillar
2. ``fire_all`` - If set to ``True``, all messages which are not prefixed with
the trigger string will fired as events onto Salt's ref:`event bus
<event-system>`. The tag for these veents will be prefixed with the string
specified by the ``tag`` config option (default: ``salt/engines/slack``).
The ``groups_pillar_name`` config option can be used to pull group
configuration from the specified pillar key.
.. note::
In order to use ``groups_pillar_name``, the engine must be running as a
minion running on the master, so that the ``Caller`` client can be used to
retrieve that minions pillar data, because the master process does not have
pillar data.
Configuration Examples
======================
.. versionchanged:: 2017.7.0
Access control group support added
This example uses a single group called ``default``. In addition, other groups
are being loaded from pillar data. The group names do not have any
significance, it is the users and commands defined within them that are used to
determine whether the Slack user has permission to run the desired command.
.. code-block:: text
@ -33,7 +91,7 @@ the saltmaster's minion pillar.
groups:
default:
users:
- *
- '*'
commands:
- test.ping
- cmd.run
@ -55,12 +113,9 @@ the saltmaster's minion pillar.
target: saltmaster
tgt_type: list
:configuration: Example configuration using the 'default' group and a
non-default group and a pillar that will be merged in If the user is '*'
(without the quotes) then the group's users or commands will match all
users as appropriate
.. versionadded: 2017.7.0
This example shows multiple groups applying to different users, with all users
having access to run test.ping. Keep in mind that when using ``*``, the value
must be quoted, or else PyYAML will fail to load the configuration.
.. code-block:: text
@ -74,9 +129,9 @@ the saltmaster's minion pillar.
groups_pillar_name: 'slack_engine:groups_pillar'
groups:
default:
valid_users:
- *
valid_commands:
users:
- '*'
commands:
- test.ping
aliases:
list_jobs:
@ -87,16 +142,7 @@ the saltmaster's minion pillar.
users:
- garethgreenaway
commands:
- *
:depends: slackclient
.. note:: groups_pillar_name
In order to use this, the engine must be running as a minion running on
the master, so that the ``Caller`` client can be used to retrieve that
minions pillar data, because the master process does not have pillars.
- '*'
'''
@ -330,7 +376,6 @@ class SlackClient(object):
cmdlist.append(cmditem)
return cmdlist
# m_data -> m_data, _text -> test, all_slack_users -> all_slack_users,
def control_message_target(self, slack_user_name, text, loaded_groups, trigger_string):
'''Returns a tuple of (target, cmdline,) for the response

View File

@ -129,8 +129,6 @@ def serve_file(load, fnd):
with salt.utils.files.fopen(fpath, 'rb') as fp_:
fp_.seek(load['loc'])
data = fp_.read(__opts__['file_buffer_size'])
if data and six.PY3 and not salt.utils.files.is_binary(fpath):
data = data.decode(__salt_system_encoding__)
if gzip and data:
data = salt.utils.gzip_util.compress(data, gzip)
ret['gzip'] = gzip

View File

@ -82,6 +82,10 @@ if salt.utils.platform.is_windows():
'will be missing'
)
HAS_UNAME = True
if not hasattr(os, 'uname'):
HAS_UNAME = False
_INTERFACES = {}
@ -448,7 +452,7 @@ def _osx_memdata():
sysctl = salt.utils.path.which('sysctl')
if sysctl:
mem = __salt__['cmd.run']('{0} -n hw.memsize'.format(sysctl))
swap_total = __salt__['cmd.run']('{0} -n vm.swapusage'.format(sysctl)).split()[2]
swap_total = __salt__['cmd.run']('{0} -n vm.swapusage'.format(sysctl)).split()[2].replace(',', '.')
if swap_total.endswith('K'):
_power = 2**10
elif swap_total.endswith('M'):
@ -689,7 +693,7 @@ def _virtual(osdata):
# Quick backout for BrandZ (Solaris LX Branded zones)
# Don't waste time trying other commands to detect the virtual grain
if osdata['kernel'] == 'Linux' and 'BrandZ virtual linux' in os.uname():
if HAS_UNAME and osdata['kernel'] == 'Linux' and 'BrandZ virtual linux' in os.uname():
grains['virtual'] = 'zone'
return grains
@ -947,7 +951,7 @@ def _virtual(osdata):
if os.path.isfile('/sys/devices/virtual/dmi/id/product_name'):
try:
with salt.utils.files.fopen('/sys/devices/virtual/dmi/id/product_name', 'r') as fhr:
output = salt.utils.stringutils.to_unicode(fhr.read())
output = salt.utils.stringutils.to_unicode(fhr.read(), errors='replace')
if 'VirtualBox' in output:
grains['virtual'] = 'VirtualBox'
elif 'RHEV Hypervisor' in output:
@ -1597,7 +1601,7 @@ def os_data():
# my_init as pid1
grains['init'] = 'runit'
else:
log.info(
log.debug(
'Could not determine init system from command line: (%s)',
' '.join(init_cmdline)
)
@ -1780,7 +1784,10 @@ def os_data():
elif grains['kernel'] == 'SunOS':
if salt.utils.platform.is_smartos():
# See https://github.com/joyent/smartos-live/issues/224
uname_v = os.uname()[3] # format: joyent_20161101T004406Z
if HAS_UNAME:
uname_v = os.uname()[3] # format: joyent_20161101T004406Z
else:
uname_v = os.name
uname_v = uname_v[uname_v.index('_')+1:]
grains['os'] = grains['osfullname'] = 'SmartOS'
# store a parsed version of YYYY.MM.DD as osrelease
@ -1809,7 +1816,10 @@ def os_data():
else:
if development is not None:
osname = ' '.join((osname, development))
uname_v = os.uname()[3]
if HAS_UNAME:
uname_v = os.uname()[3]
else:
uname_v = os.name
grains['os'] = grains['osfullname'] = osname
if osname in ['Oracle Solaris'] and uname_v.startswith(osmajorrelease):
# Oracla Solars 11 and up have minor version in uname
@ -2288,7 +2298,7 @@ def _hw_data(osdata):
if os.path.exists(contents_file):
try:
with salt.utils.files.fopen(contents_file, 'r') as ifile:
grains[key] = ifile.read().strip()
grains[key] = salt.utils.stringutils.to_unicode(ifile.read().strip(), errors='replace')
if key == 'uuid':
grains['uuid'] = grains['uuid'].lower()
except (IOError, OSError) as err:

View File

@ -15,12 +15,14 @@ import inspect
import tempfile
import functools
import threading
import traceback
import types
from collections import MutableMapping
from zipimport import zipimporter
# Import salt libs
import salt.config
import salt.defaults.exitcodes
import salt.syspaths
import salt.utils.context
import salt.utils.data
@ -67,10 +69,10 @@ if USE_IMPORTLIB:
SUFFIXES = []
for suffix in importlib.machinery.EXTENSION_SUFFIXES:
SUFFIXES.append((suffix, 'rb', MODULE_KIND_EXTENSION))
for suffix in importlib.machinery.BYTECODE_SUFFIXES:
SUFFIXES.append((suffix, 'rb', MODULE_KIND_COMPILED))
for suffix in importlib.machinery.SOURCE_SUFFIXES:
SUFFIXES.append((suffix, 'rb', MODULE_KIND_SOURCE))
for suffix in importlib.machinery.BYTECODE_SUFFIXES:
SUFFIXES.append((suffix, 'rb', MODULE_KIND_COMPILED))
MODULE_KIND_MAP = {
MODULE_KIND_SOURCE: importlib.machinery.SourceFileLoader,
MODULE_KIND_COMPILED: importlib.machinery.SourcelessFileLoader,
@ -1480,6 +1482,17 @@ class LazyLoader(salt.utils.lazy.LazyDict):
self.missing_modules[name] = error
return False
except SystemExit as error:
try:
fn_, _, caller, _ = traceback.extract_tb(sys.exc_info()[2])[-1]
except Exception:
pass
else:
tgt_fn = os.path.join('salt', 'utils', 'process.py')
if fn_.endswith(tgt_fn) and '_handle_signals' in caller:
# Race conditon, SIGTERM or SIGINT received while loader
# was in process of loading a module. Call sys.exit to
# ensure that the process is killed.
sys.exit(salt.defaults.exitcodes.EX_OK)
log.error(
'Failed to import %s %s as the module called exit()\n',
self.tag, name, exc_info=True

View File

@ -359,13 +359,11 @@ class FileserverUpdate(salt.utils.process.SignalHandlingMultiprocessingProcess):
self.__init__(
state['opts'],
log_queue=state['log_queue'],
log_queue_level=state['log_queue_level']
)
def __getstate__(self):
return {'opts': self.opts,
'log_queue': self.log_queue,
'log_queue_level': self.log_queue_level
}
def fill_buckets(self):
@ -590,11 +588,19 @@ class Master(SMaster):
pass
if self.opts.get('git_pillar_verify_config', True):
git_pillars = [
x for x in self.opts.get('ext_pillar', [])
if 'git' in x
and not isinstance(x['git'], six.string_types)
]
try:
git_pillars = [
x for x in self.opts.get('ext_pillar', [])
if 'git' in x
and not isinstance(x['git'], six.string_types)
]
except TypeError:
git_pillars = []
critical_errors.append(
'Invalid ext_pillar configuration. It is likely that the '
'external pillar type was not specified for one or more '
'external pillars.'
)
if git_pillars:
try:
new_opts = copy.deepcopy(self.opts)

View File

@ -101,6 +101,7 @@ def cert(name,
server=None,
owner='root',
group='root',
mode='0640',
certname=None):
'''
Obtain/renew a certificate from an ACME CA, probably Let's Encrypt.
@ -113,8 +114,9 @@ def cert(name,
:param renew: True/'force' to force a renewal, or a window of renewal before expiry in days
:param keysize: RSA key bits
:param server: API endpoint to talk to
:param owner: owner of private key
:param group: group of private key
:param owner: owner of the private key file
:param group: group of the private key file
:param mode: mode of the private key file
:param certname: Name of the certificate to save
:return: dict with 'result' True/False/None, 'comment' and certificate's expiry date ('not_after')
@ -170,27 +172,17 @@ def cert(name,
return {'result': False, 'comment': 'Certificate {0} renewal failed with:\n{1}'.format(name, res['stderr'])}
if 'no action taken' in res['stdout']:
return {'result': None,
'comment': 'No action taken on certificate {0}'.format(cert_file),
'not_after': expires(name)}
if renew:
comment = 'Certificate {0} unchanged'.format(cert_file)
elif renew:
comment = 'Certificate {0} renewed'.format(name)
else:
comment = 'Certificate {0} obtained'.format(name)
ret = {'comment': comment, 'not_after': expires(name)}
res = __salt__['file.check_perms'](_cert_file(name, 'privkey'), {}, owner, group, '0600', follow_symlinks=True)
if res is None:
ret['result'] = False
ret['comment'] += ', but setting permissions failed.'
elif not res[0].get('result', False):
ret['result'] = False
ret['comment'] += ', but setting permissions failed with \n{0}'.format(res[0]['comment'])
else:
ret['result'] = True
ret['comment'] += '.'
ret = {'comment': comment, 'not_after': expires(name), 'changes': {}, 'result': True}
ret, _ = __salt__['file.check_perms'](_cert_file(name, 'privkey'),
ret,
owner, group, mode,
follow_symlinks=True)
return ret

View File

@ -289,12 +289,14 @@ def raw_cron(user):
# Preserve line endings
lines = sdecode(__salt__['cmd.run_stdout'](cmd,
runas=user,
ignore_retcode=True,
rstrip=False,
python_shell=False)).splitlines(True)
else:
cmd = 'crontab -u {0} -l'.format(user)
# Preserve line endings
lines = sdecode(__salt__['cmd.run_stdout'](cmd,
ignore_retcode=True,
rstrip=False,
python_shell=False)).splitlines(True)

View File

@ -393,20 +393,14 @@ def __within(within=None, errmsg=None, dtype=None):
def __space_delimited_list(value):
'''validate that a value contains one or more space-delimited values'''
valid, _value, errmsg = False, value, 'space-delimited string'
try:
if hasattr(value, '__iter__'):
valid = True # TODO:
else:
_value = value.split()
if _value == []:
raise ValueError
valid = True
except AttributeError:
pass
except ValueError:
pass
return (valid, _value, errmsg)
if isinstance(value, str):
value = value.strip().split()
if hasattr(value, '__iter__') and value != []:
return (True, value, 'space-delimited string')
else:
return (False, value, '{0} is not a valid space-delimited value.\n'.format(value))
SALT_ATTR_TO_DEBIAN_ATTR_MAP = {
'dns': 'dns-nameservers',

View File

@ -6467,7 +6467,6 @@ def _prepare_trans_tar(name, sls_opts, mods=None, pillar=None):
refs = salt.client.ssh.state.lowstate_file_refs(chunks)
_mk_fileclient()
trans_tar = salt.client.ssh.state.prep_trans_tar(
sls_opts,
__context__['cp.fileclient'],
chunks, refs, pillar, name)
return trans_tar

View File

@ -86,6 +86,9 @@ def fire_master(data, tag, preload=None):
channel = salt.transport.Channel.factory(__opts__, master_uri=master)
try:
channel.send(load)
# channel.send was successful.
# Ensure ret is True.
ret = True
except Exception:
ret = False
return ret

View File

@ -61,6 +61,7 @@ import salt.utils.stringutils
import salt.utils.templates
import salt.utils.url
import salt.utils.user
import salt.utils.data
from salt.exceptions import CommandExecutionError, MinionError, SaltInvocationError, get_error_message as _get_error_message
from salt.utils.files import HASHES, HASHES_REVMAP
@ -1718,18 +1719,19 @@ def _regex_to_static(src, regex):
return None
try:
src = re.search(regex, src, re.M)
compiled = re.compile(regex, re.DOTALL)
src = [line for line in src if compiled.search(line) or line.count(regex)]
except Exception as ex:
raise CommandExecutionError("{0}: '{1}'".format(_get_error_message(ex), regex))
return src and src.group().rstrip('\r') or regex
return src and src or []
def _assert_occurrence(src, probe, target, amount=1):
def _assert_occurrence(probe, target, amount=1):
'''
Raise an exception, if there are different amount of specified occurrences in src.
'''
occ = src.count(probe)
occ = len(probe)
if occ > amount:
msg = 'more than'
elif occ < amount:
@ -1745,7 +1747,7 @@ def _assert_occurrence(src, probe, target, amount=1):
return occ
def _get_line_indent(src, line, indent):
def _set_line_indent(src, line, indent):
'''
Indent the line with the source line.
'''
@ -1758,7 +1760,36 @@ def _get_line_indent(src, line, indent):
break
idt.append(c)
return ''.join(idt) + line.strip()
return ''.join(idt) + line.lstrip()
def _get_eol(line):
match = re.search('((?<!\r)\n|\r(?!\n)|\r\n)$', line)
return match and match.group() or ''
def _set_line_eol(src, line):
'''
Add line ending
'''
line_ending = _get_eol(src) or os.linesep
return line.rstrip() + line_ending
def _insert_line_before(idx, body, content, indent):
if not idx or (idx and _starts_till(body[idx - 1], content) < 0):
cnd = _set_line_indent(body[idx], content, indent)
body.insert(idx, cnd)
return body
def _insert_line_after(idx, body, content, indent):
# No duplicates or append, if "after" is the last line
next_line = idx + 1 < len(body) and body[idx + 1] or None
if next_line is None or _starts_till(next_line, content) < 0:
cnd = _set_line_indent(body[idx], content, indent)
body.insert(idx + 1, cnd)
return body
def line(path, content=None, match=None, mode=None, location=None,
@ -1889,132 +1920,110 @@ def line(path, content=None, match=None, mode=None, location=None,
match = content
with salt.utils.files.fopen(path, mode='r') as fp_:
body = salt.utils.stringutils.to_unicode(fp_.read())
body_before = hashlib.sha256(salt.utils.stringutils.to_bytes(body)).hexdigest()
body = salt.utils.data.decode_list(fp_.readlines())
body_before = hashlib.sha256(salt.utils.stringutils.to_bytes(''.join(body))).hexdigest()
# Add empty line at the end if last line ends with eol.
# Allows simpler code
if body and _get_eol(body[-1]):
body.append('')
after = _regex_to_static(body, after)
before = _regex_to_static(body, before)
match = _regex_to_static(body, match)
if os.stat(path).st_size == 0 and mode in ('delete', 'replace'):
log.warning('Cannot find text to {0}. File \'{1}\' is empty.'.format(mode, path))
body = ''
elif mode == 'delete':
body = os.linesep.join([line for line in body.split(os.linesep) if line.find(match) < 0])
elif mode == 'replace':
body = os.linesep.join([(_get_line_indent(file_line, content, indent)
if (file_line.find(match) > -1 and not file_line == content) else file_line)
for file_line in body.split(os.linesep)])
body = []
elif mode == 'delete' and match:
body = [line for line in body if line != match[0]]
elif mode == 'replace' and match:
idx = body.index(match[0])
file_line = body.pop(idx)
body.insert(idx, _set_line_indent(file_line, content, indent))
elif mode == 'insert':
if not location and not before and not after:
raise CommandExecutionError('On insert must be defined either "location" or "before/after" conditions.')
if not location:
if before and after:
_assert_occurrence(body, before, 'before')
_assert_occurrence(body, after, 'after')
_assert_occurrence(before, 'before')
_assert_occurrence(after, 'after')
out = []
lines = body.split(os.linesep)
in_range = False
for line in lines:
if line.find(after) > -1:
for line in body:
if line == after[0]:
in_range = True
elif line.find(before) > -1 and in_range:
out.append(_get_line_indent(line, content, indent))
elif line == before[0] and in_range:
cnd = _set_line_indent(line, content, indent)
out.append(cnd)
out.append(line)
body = os.linesep.join(out)
body = out
if before and not after:
_assert_occurrence(body, before, 'before')
out = []
lines = body.split(os.linesep)
for idx in range(len(lines)):
_line = lines[idx]
if _line.find(before) > -1:
cnd = _get_line_indent(_line, content, indent)
if not idx or (idx and _starts_till(lines[idx - 1], cnd) < 0): # Job for replace instead
out.append(cnd)
out.append(_line)
body = os.linesep.join(out)
_assert_occurrence(before, 'before')
idx = body.index(before[0])
body = _insert_line_before(idx, body, content, indent)
elif after and not before:
_assert_occurrence(body, after, 'after')
out = []
lines = body.split(os.linesep)
for idx, _line in enumerate(lines):
out.append(_line)
cnd = _get_line_indent(_line, content, indent)
# No duplicates or append, if "after" is the last line
if (_line.find(after) > -1 and
(lines[((idx + 1) < len(lines)) and idx + 1 or idx].strip() != cnd or
idx + 1 == len(lines))):
out.append(cnd)
body = os.linesep.join(out)
_assert_occurrence(after, 'after')
idx = body.index(after[0])
body = _insert_line_after(idx, body, content, indent)
else:
if location == 'start':
body = os.linesep.join((content, body))
if body:
body.insert(0, _set_line_eol(body[0], content))
else:
body.append(content + os.linesep)
elif location == 'end':
body = os.linesep.join((body, _get_line_indent(body[-1], content, indent) if body else content))
body.append(_set_line_indent(body[-1], content, indent) if body else content)
elif mode == 'ensure':
after = after and after.strip()
before = before and before.strip()
if before and after:
_assert_occurrence(body, before, 'before')
_assert_occurrence(body, after, 'after')
_assert_occurrence(before, 'before')
_assert_occurrence(after, 'after')
is_there = bool(body.count(content))
is_there = bool([l for l in body if l.count(content)])
if not is_there:
out = []
body = body.split(os.linesep)
for idx, line in enumerate(body):
out.append(line)
if line.find(content) > -1:
is_there = True
if not is_there:
if idx < (len(body) - 1) and line.find(after) > -1 and body[idx + 1].find(before) > -1:
out.append(content)
elif line.find(after) > -1:
raise CommandExecutionError('Found more than one line between '
'boundaries "before" and "after".')
body = os.linesep.join(out)
idx = body.index(after[0])
if idx < (len(body) - 1) and body[idx + 1] == before[0]:
cnd = _set_line_indent(body[idx], content, indent)
body.insert(idx + 1, cnd)
else:
raise CommandExecutionError('Found more than one line between '
'boundaries "before" and "after".')
elif before and not after:
_assert_occurrence(body, before, 'before')
body = body.split(os.linesep)
out = []
for idx in range(len(body)):
if body[idx].find(before) > -1:
prev = (idx > 0 and idx or 1) - 1
out.append(_get_line_indent(body[idx], content, indent))
if _starts_till(out[prev], content) > -1:
del out[prev]
out.append(body[idx])
body = os.linesep.join(out)
_assert_occurrence(before, 'before')
idx = body.index(before[0])
body = _insert_line_before(idx, body, content, indent)
elif not before and after:
_assert_occurrence(body, after, 'after')
body = body.split(os.linesep)
skip = None
out = []
for idx in range(len(body)):
if skip != body[idx]:
out.append(body[idx])
_assert_occurrence(after, 'after')
if body[idx].find(after) > -1:
next_line = idx + 1 < len(body) and body[idx + 1] or None
if next_line is not None and _starts_till(next_line, content) > -1:
skip = next_line
out.append(_get_line_indent(body[idx], content, indent))
body = os.linesep.join(out)
idx = body.index(after[0])
body = _insert_line_after(idx, body, content, indent)
else:
raise CommandExecutionError("Wrong conditions? "
"Unable to ensure line without knowing "
"where to put it before and/or after.")
changed = body_before != hashlib.sha256(salt.utils.stringutils.to_bytes(body)).hexdigest()
if body:
for idx, line in enumerate(body):
if not _get_eol(line) and idx+1 < len(body):
prev = idx and idx-1 or 1
body[idx] = _set_line_eol(body[prev], line)
# We do not need empty line at the end anymore
if '' == body[-1]:
body.pop()
changed = body_before != hashlib.sha256(salt.utils.stringutils.to_bytes(''.join(body))).hexdigest()
if backup and changed and __opts__['test'] is False:
try:
@ -2028,12 +2037,9 @@ def line(path, content=None, match=None, mode=None, location=None,
if changed:
if show_changes:
with salt.utils.files.fopen(path, 'r') as fp_:
path_content = [salt.utils.stringutils.to_unicode(x)
for x in fp_.read().splitlines(True)]
path_content = salt.utils.data.decode_list(fp_.read().splitlines(True))
changes_diff = ''.join(difflib.unified_diff(
path_content,
[salt.utils.stringutils.to_unicode(x)
for x in body.splitlines(True)]
path_content, body
))
if __opts__['test'] is False:
fh_ = None
@ -2041,12 +2047,12 @@ def line(path, content=None, match=None, mode=None, location=None,
# Make sure we match the file mode from salt.utils.files.fopen
if six.PY2 and salt.utils.platform.is_windows():
mode = 'wb'
body = salt.utils.stringutils.to_bytes(body)
body = salt.utils.data.encode_list(body)
else:
mode = 'w'
body = salt.utils.stringutils.to_str(body)
body = salt.utils.data.decode_list(body, to_str=True)
fh_ = salt.utils.atomicfile.atomic_open(path, mode)
fh_.write(body)
fh_.writelines(body)
finally:
if fh_:
fh_.close()
@ -2471,10 +2477,10 @@ def blockreplace(path,
final output
marker_end
The line content identifying a line as the end of the content block.
Note that the whole line containing this marker will be considered, so
whitespace or extra content before or after the marker is included in
final output
The line content identifying the end of the content block. As of
versions 2017.7.5 and 2018.3.1, everything up to the text matching the
marker will be replaced, so it's important to ensure that your marker
includes the beginning of the text you wish to replace.
content
The content to be used between the two lines identified by marker_start
@ -4463,27 +4469,6 @@ def check_perms(name, ret, user, group, mode, attrs=None, follow_symlinks=False)
if perms['lattrs']:
chattr(name, operator='remove', attributes=perms['lattrs'])
# Mode changes if needed
if mode is not None:
# File is a symlink, ignore the mode setting
# if follow_symlinks is False
if os.path.islink(name) and not follow_symlinks:
pass
else:
mode = salt.utils.files.normalize_mode(mode)
if mode != perms['lmode']:
if __opts__['test'] is True:
ret['changes']['mode'] = mode
else:
set_mode(name, mode)
if mode != salt.utils.files.normalize_mode(get_mode(name)):
ret['result'] = False
ret['comment'].append(
'Failed to change mode to {0}'.format(mode)
)
else:
ret['changes']['mode'] = mode
# user/group changes if needed, then check if it worked
if user:
if isinstance(user, int):
@ -4540,6 +4525,7 @@ def check_perms(name, ret, user, group, mode, attrs=None, follow_symlinks=False)
.format(user))
elif 'cuser' in perms and user != '':
ret['changes']['user'] = user
if group:
if isinstance(group, int):
group = gid_to_group(group)
@ -4560,18 +4546,32 @@ def check_perms(name, ret, user, group, mode, attrs=None, follow_symlinks=False)
elif 'cgroup' in perms and user != '':
ret['changes']['group'] = group
if isinstance(orig_comment, six.string_types):
if orig_comment:
ret['comment'].insert(0, orig_comment)
ret['comment'] = '; '.join(ret['comment'])
if __opts__['test'] is True and ret['changes']:
ret['result'] = None
if not salt.utils.platform.is_windows() and not is_dir:
# Replace attributes on file if it had been removed
if perms.get('lattrs', ''):
chattr(name, operator='add', attributes=perms['lattrs'])
# Mode changes if needed
if mode is not None:
# File is a symlink, ignore the mode setting
# if follow_symlinks is False
if os.path.islink(name) and not follow_symlinks:
pass
else:
mode = salt.utils.files.normalize_mode(mode)
if mode != perms['lmode']:
if __opts__['test'] is True:
ret['changes']['mode'] = mode
else:
set_mode(name, mode)
if mode != salt.utils.files.normalize_mode(get_mode(name)):
ret['result'] = False
ret['comment'].append(
'Failed to change mode to {0}'.format(mode)
)
else:
ret['changes']['mode'] = mode
# Modify attributes of file if needed
if attrs is not None and not is_dir:
# File is a symlink, ignore the mode setting
@ -4598,6 +4598,18 @@ def check_perms(name, ret, user, group, mode, attrs=None, follow_symlinks=False)
else:
ret['changes']['attrs'] = attrs
# Only combine the comment list into a string
# after all comments are added above
if isinstance(orig_comment, six.string_types):
if orig_comment:
ret['comment'].insert(0, orig_comment)
ret['comment'] = '; '.join(ret['comment'])
# Set result to None at the very end of the function,
# after all changes have been recorded above
if __opts__['test'] is True and ret['changes']:
ret['result'] = None
return ret, perms
@ -4722,6 +4734,11 @@ def check_managed_changes(
defaults,
skip_verify,
**kwargs)
# Ensure that user-provided hash string is lowercase
if source_sum and ('hsum' in source_sum):
source_sum['hsum'] = source_sum['hsum'].lower()
if comments:
__clean_tmp(sfn)
return False, comments
@ -5058,7 +5075,7 @@ def manage_file(name,
source
file reference on the master
source_hash
source_sum
sum hash for source
user
@ -6196,6 +6213,16 @@ def grep(path,
'''
path = os.path.expanduser(path)
# Backup the path in case the glob returns nothing
_path = path
path = glob.glob(path)
# If the list is empty no files exist
# so we revert back to the original path
# so the result is an error.
if not path:
path = _path
split_opts = []
for opt in opts:
try:
@ -6210,7 +6237,10 @@ def grep(path,
)
split_opts.extend(split)
cmd = ['grep'] + split_opts + [pattern, path]
if isinstance(path, list):
cmd = ['grep'] + split_opts + [pattern] + path
else:
cmd = ['grep'] + split_opts + [pattern, path]
try:
ret = __salt__['cmd.run_all'](cmd, python_shell=False)
except (IOError, OSError) as exc:

View File

@ -29,15 +29,16 @@ def __virtual__():
return (False, 'glusterfs server is not installed')
def _get_minor_version():
# Set default version to 6 for tests
version = 6
def _get_version():
# Set the default minor version to 6 for tests
version = [3, 6]
cmd = 'gluster --version'
result = __salt__['cmd.run'](cmd).splitlines()
for line in result:
if line.startswith('glusterfs'):
version = int(line.split()[1].split('.')[1])
return version
version = line.split()[-1].split('.')
version = [int(i) for i in version]
return tuple(version)
def _gluster_ok(xml_data):
@ -70,7 +71,7 @@ def _gluster_xml(cmd):
# We will pass the command string as stdin to allow for much longer
# command strings. This is especially useful for creating large volumes
# where the list of bricks exceeds 128 characters.
if _get_minor_version() < 6:
if _get_version() < (3, 6,):
result = __salt__['cmd.run'](
'script -q -c "gluster --xml --mode=script"', stdin="{0}\n\004".format(cmd)
)

View File

@ -58,22 +58,22 @@ def __virtual__():
'influxdb library not available.'))
def _client(user=None, password=None, host=None, port=None, **client_args):
if not user:
user = __salt__['config.option']('influxdb.user', 'root')
if not password:
password = __salt__['config.option']('influxdb.password', 'root')
if not host:
host = __salt__['config.option']('influxdb.host', 'localhost')
if not port:
port = __salt__['config.option']('influxdb.port', 8086)
def _client(influxdb_user=None, influxdb_password=None, influxdb_host=None, influxdb_port=None, **client_args):
if not influxdb_user:
influxdb_user = __salt__['config.option']('influxdb.user', 'root')
if not influxdb_password:
influxdb_password = __salt__['config.option']('influxdb.password', 'root')
if not influxdb_host:
influxdb_host = __salt__['config.option']('influxdb.host', 'localhost')
if not influxdb_port:
influxdb_port = __salt__['config.option']('influxdb.port', 8086)
for ignore in _STATE_INTERNAL_KEYWORDS:
if ignore in client_args:
del client_args[ignore]
return influxdb.InfluxDBClient(host=host,
port=port,
username=user,
password=password,
return influxdb.InfluxDBClient(host=influxdb_host,
port=influxdb_port,
username=influxdb_user,
password=influxdb_password,
**client_args)

View File

@ -35,9 +35,9 @@ def __virtual__():
return __virtualname__
ini_regx = re.compile(r'^\s*\[(.+?)\]\s*$', flags=re.M)
com_regx = re.compile(r'^\s*(#|;)\s*(.*)')
indented_regx = re.compile(r'(\s+)(.*)')
INI_REGX = re.compile(r'^\s*\[(.+?)\]\s*$', flags=re.M)
COM_REGX = re.compile(r'^\s*(#|;)\s*(.*)')
INDENTED_REGX = re.compile(r'(\s+)(.*)')
def set_option(file_name, sections=None, separator='='):
@ -105,7 +105,13 @@ def get_option(file_name, section, option, separator='='):
salt '*' ini.get_option /path/to/ini section_name option_name
'''
inifile = _Ini.get_ini_file(file_name, separator=separator)
return inifile.get(section, {}).get(option, None)
if section:
try:
return inifile.get(section, {}).get(option, None)
except AttributeError:
return None
else:
return inifile.get(option, None)
def remove_option(file_name, section, option, separator='='):
@ -129,7 +135,10 @@ def remove_option(file_name, section, option, separator='='):
salt '*' ini.remove_option /path/to/ini section_name option_name
'''
inifile = _Ini.get_ini_file(file_name, separator=separator)
value = inifile.get(section, {}).pop(option, None)
if isinstance(inifile.get(section), (dict, OrderedDict)):
value = inifile.get(section, {}).pop(option, None)
else:
value = inifile.pop(option, None)
inifile.flush()
return value
@ -182,15 +191,53 @@ def remove_section(file_name, section, separator='='):
salt '*' ini.remove_section /path/to/ini section_name
'''
inifile = _Ini.get_ini_file(file_name, separator=separator)
if section in inifile:
section = inifile.pop(section)
inifile.flush()
ret = {}
for key, value in six.iteritems(section):
if key[0] != '#':
ret.update({key: value})
return ret
def get_ini(file_name, separator='='):
'''
Retrieve whole structure from an ini file and return it as dictionary.
API Example:
.. code-block:: python
import salt
sc = salt.client.get_local_client()
sc.cmd('target', 'ini.get_ini',
[path_to_ini_file])
CLI Example:
.. code-block:: bash
salt '*' ini.get_ini /path/to/ini
'''
def ini_odict2dict(odict):
'''
Transform OrderedDict to regular dict recursively
:param odict: OrderedDict
:return: regular dict
'''
ret = {}
for key, val in six.iteritems(odict):
if key[0] != '#':
if isinstance(val, (dict, OrderedDict)):
ret.update({key: ini_odict2dict(val)})
else:
ret.update({key: val})
return ret
inifile = _Ini.get_ini_file(file_name, separator=separator)
section = inifile.pop(section, {})
inifile.flush()
ret = {}
for key, value in six.iteritems(section):
if key[0] != '#':
ret.update({key: value})
return ret
return ini_odict2dict(inifile)
class _Section(OrderedDict):
@ -221,7 +268,7 @@ class _Section(OrderedDict):
self.pop(opt)
for opt_str in inicontents.split(os.linesep):
# Match comments
com_match = com_regx.match(opt_str)
com_match = COM_REGX.match(opt_str)
if com_match:
name = '#comment{0}'.format(comment_count)
self.com = com_match.group(1)
@ -229,7 +276,7 @@ class _Section(OrderedDict):
self.update({name: opt_str})
continue
# Add indented lines to the value of the previous entry.
indented_match = indented_regx.match(opt_str)
indented_match = INDENTED_REGX.match(opt_str)
if indented_match:
indent = indented_match.group(1).replace('\t', ' ')
if indent > curr_indent:
@ -318,7 +365,7 @@ class _Section(OrderedDict):
sections_dict = OrderedDict()
for name, value in six.iteritems(self):
# Handle Comment Lines
if com_regx.match(name):
if COM_REGX.match(name):
yield '{0}{1}'.format(value, os.linesep)
# Handle Sections
elif isinstance(value, _Section):
@ -363,9 +410,6 @@ class _Section(OrderedDict):
class _Ini(_Section):
def __init__(self, name, inicontents='', separator='=', commenter='#'):
super(_Ini, self).__init__(name, inicontents, separator, commenter)
def refresh(self, inicontents=None):
if inicontents is None:
try:
@ -382,7 +426,7 @@ class _Ini(_Section):
# Remove anything left behind from a previous run.
self.clear()
inicontents = ini_regx.split(inicontents)
inicontents = INI_REGX.split(inicontents)
inicontents.reverse()
# Pop anything defined outside of a section (ie. at the top of
# the ini file).

View File

@ -2268,22 +2268,22 @@ def _change_state(cmd,
# as te command itself mess with double forks; we must not
# communicate with it, but just wait for the exit status
pkwargs = {'python_shell': False,
'redirect_stderr': True,
'with_communicate': with_communicate,
'use_vt': use_vt,
'stdin': stdin,
'stdout': stdout,
'stderr': stderr}
'stdout': stdout}
for i in [a for a in pkwargs]:
val = pkwargs[i]
if val is _marker:
pkwargs.pop(i, None)
error = __salt__['cmd.run_stderr'](cmd, **pkwargs)
_cmdout = __salt__['cmd.run_all'](cmd, **pkwargs)
if error:
if _cmdout['retcode'] != 0:
raise CommandExecutionError(
'Error changing state for container \'{0}\' using command '
'\'{1}\': {2}'.format(name, cmd, error)
'\'{1}\': {2}'.format(name, cmd, _cmdout['stdout'])
)
if expected is not None:
# some commands do not wait, so we will

View File

@ -48,7 +48,7 @@ def _list_taps():
'''
List currently installed brew taps
'''
cmd = 'brew tap'
cmd = 'tap'
return _call_brew(cmd)['stdout'].splitlines()
@ -60,7 +60,7 @@ def _tap(tap, runas=None):
if tap in _list_taps():
return True
cmd = 'brew tap {0}'.format(tap)
cmd = 'tap {0}'.format(tap)
try:
_call_brew(cmd)
except CommandExecutionError:
@ -85,6 +85,7 @@ def _call_brew(cmd, failhard=True):
'''
user = __salt__['file.get_user'](_homebrew_bin())
runas = user if user != __opts__['user'] else None
cmd = '{} {}'.format(salt.utils.path.which('brew'), cmd)
result = __salt__['cmd.run_all'](cmd,
runas=runas,
output_loglevel='trace',
@ -121,7 +122,7 @@ def list_pkgs(versions_as_list=False, **kwargs):
__salt__['pkg_resource.stringify'](ret)
return ret
cmd = 'brew list --versions'
cmd = 'list --versions'
ret = {}
out = _call_brew(cmd)['stdout']
for line in out.splitlines():
@ -230,7 +231,7 @@ def remove(name=None, pkgs=None, **kwargs):
targets = [x for x in pkg_params if x in old]
if not targets:
return {}
cmd = 'brew uninstall {0}'.format(' '.join(targets))
cmd = 'uninstall {0}'.format(' '.join(targets))
out = _call_brew(cmd)
if out['retcode'] != 0 and out['stderr']:
@ -263,7 +264,7 @@ def refresh_db():
'''
# Remove rtag file to keep multiple refreshes from happening in pkg states
salt.utils.pkg.clear_rtag(__opts__)
cmd = 'brew update'
cmd = 'update'
if _call_brew(cmd)['retcode']:
log.error('Failed to update')
return False
@ -286,7 +287,7 @@ def _info(*pkgs):
Caveat: If one of the packages does not exist, no packages will be
included in the output.
'''
cmd = 'brew info --json=v1 {0}'.format(' '.join(pkgs))
cmd = 'info --json=v1 {0}'.format(' '.join(pkgs))
brew_result = _call_brew(cmd)
if brew_result['retcode']:
log.error('Failed to get info about packages: %s',
@ -382,9 +383,9 @@ def install(name=None, pkgs=None, taps=None, options=None, **kwargs):
_tap(tap)
if options:
cmd = 'brew install {0} {1}'.format(formulas, ' '.join(options))
cmd = 'install {0} {1}'.format(formulas, ' '.join(options))
else:
cmd = 'brew install {0}'.format(formulas)
cmd = 'install {0}'.format(formulas)
out = _call_brew(cmd)
if out['retcode'] != 0 and out['stderr']:
@ -418,7 +419,7 @@ def list_upgrades(refresh=True, **kwargs): # pylint: disable=W0613
if refresh:
refresh_db()
res = _call_brew(['brew', 'outdated', '--json=v1'])
res = _call_brew('outdated --json=v1')
ret = {}
try:
@ -478,7 +479,7 @@ def upgrade(refresh=True):
if salt.utils.data.is_true(refresh):
refresh_db()
result = _call_brew('brew upgrade', failhard=False)
result = _call_brew('upgrade', failhard=False)
__context__.pop('pkg.list_pkgs', None)
new = list_pkgs()
ret = salt.utils.data.compare_dicts(old, new)

View File

@ -433,7 +433,6 @@ def disabled(name, runas=None, domain='system'):
disabled = launchctl('print-disabled',
domain,
return_stdout=True,
output_loglevel='trace',
runas=runas)
for service in disabled.split("\n"):
if name in service:

View File

@ -959,7 +959,7 @@ def alter_db(name, character_set=None, collate=None, **connection_args):
return []
cur = dbc.cursor()
existing = db_get(name, **connection_args)
qry = 'ALTER DATABASE {0} CHARACTER SET {1} COLLATE {2};'.format(
qry = 'ALTER DATABASE `{0}` CHARACTER SET {1} COLLATE {2};'.format(
name.replace('%', r'\%').replace('_', r'\_'),
character_set or existing.get('character_set'),
collate or existing.get('collate'))
@ -1812,11 +1812,16 @@ def grant_exists(grant,
if not target_tokens: # Avoid the overhead of re-calc in loop
target_tokens = _grant_to_tokens(target)
grant_tokens = _grant_to_tokens(grant)
grant_tokens_database = grant_tokens['database'].replace('"', '').replace('\\', '').replace('`', '')
target_tokens_database = target_tokens['database'].replace('"', '').replace('\\', '').replace('`', '')
if grant_tokens['user'] == target_tokens['user'] and \
grant_tokens_database == target_tokens_database and \
grant_tokens['host'] == target_tokens['host'] and \
_grant_tokens = {}
_target_tokens = {}
for item in ['user', 'database', 'host']:
_grant_tokens[item] = grant_tokens[item].replace('"', '').replace('\\', '').replace('`', '')
_target_tokens[item] = target_tokens[item].replace('"', '').replace('\\', '').replace('`', '')
if _grant_tokens['user'] == _target_tokens['user'] and \
_grant_tokens['database'] == _target_tokens['database'] and \
_grant_tokens['host'] == _target_tokens['host'] and \
set(grant_tokens['grant']) >= set(target_tokens['grant']):
return True
else:

View File

@ -98,20 +98,16 @@ def _space_delimited_list(value):
'''
validate that a value contains one or more space-delimited values
'''
valid, _value, errmsg = False, value, 'space-delimited string'
try:
if hasattr(value, '__iter__'):
valid = True
else:
_value = value.split()
if _value == []:
raise ValueError
valid = True
except AttributeError:
errmsg = '{0} is not a valid list.\n'.format(value)
except ValueError:
errmsg = '{0} is not a valid list.\n'.format(value)
return (valid, errmsg)
if isinstance(value, str):
items = value.split(' ')
valid = items and all(items)
else:
valid = hasattr(value, '__iter__') and (value != [])
if valid:
return (True, 'space-delimited string')
else:
return (False, '{0} is not a valid list.\n'.format(value))
def _validate_ipv4(value):

View File

@ -162,7 +162,11 @@ def install(pkg=None,
env.update({'SUDO_UID': uid, 'SUDO_USER': ''})
cmd = ' '.join(cmd)
result = __salt__['cmd.run_all'](cmd, python_shell=True, cwd=dir, runas=runas, env=env)
result = __salt__['cmd.run_all'](cmd,
python_shell=True,
cwd=dir,
runas=runas,
env=env)
if result['retcode'] != 0:
raise CommandExecutionError(result['stderr'])
@ -170,33 +174,9 @@ def install(pkg=None,
# npm >1.2.21 is putting the output to stderr even though retcode is 0
npm_output = result['stdout'] or result['stderr']
try:
return salt.utils.json.loads(npm_output)
return salt.utils.json.find_json(npm_output)
except ValueError:
pass
json_npm_output = _extract_json(npm_output)
return json_npm_output or npm_output
def _extract_json(npm_output):
lines = npm_output.splitlines()
log.error(lines)
# Strip all lines until JSON output starts
while lines and not lines[0].startswith('{') and not lines[0].startswith('['):
lines = lines[1:]
while lines and not lines[-1].startswith('}') and not lines[-1].startswith(']'):
lines = lines[:-1]
# macOS with fsevents includes the following line in the return
# when a new module is installed which is invalid JSON:
# [fsevents] Success: "..."
while lines and (lines[0].startswith('[fsevents]') or lines[0].startswith('Pass ')):
lines = lines[1:]
try:
return salt.utils.json.loads(''.join(lines))
except ValueError:
pass
return None
return npm_output
def uninstall(pkg, dir=None, runas=None, env=None):

View File

@ -25,13 +25,13 @@ def __virtual__():
'''
if salt.utils.platform.is_darwin() or salt.utils.platform.is_windows():
return True
return (False, 'Module proxy: module only works on Windows or MacOS systems')
return False, 'Module proxy: module only works on Windows or MacOS systems'
def _get_proxy_osx(function, network_service):
def _get_proxy_osx(cmd_function, network_service):
ret = {}
out = __salt__['cmd.run']('networksetup -{0} {1}'.format(function, network_service))
out = __salt__['cmd.run']('networksetup -{0} {1}'.format(cmd_function, network_service))
match = re.match('Enabled: (.*)\nServer: (.*)\nPort: (.*)\n', out)
if match is not None:
g = match.groups()
@ -41,8 +41,8 @@ def _get_proxy_osx(function, network_service):
return ret
def _set_proxy_osx(function, server, port, user, password, network_service):
cmd = 'networksetup -{0} {1} {2} {3}'.format(function, network_service, server, port)
def _set_proxy_osx(cmd_function, server, port, user, password, network_service):
cmd = 'networksetup -{0} {1} {2} {3}'.format(cmd_function, network_service, server, port)
if user is not None and password is not None:
cmd = cmd + ' On {0} {1}'.format(user, password)
@ -58,12 +58,12 @@ def _get_proxy_windows(types=None):
if types is None:
types = ['http', 'https', 'ftp']
reg_val = __salt__['reg.read_value']('HKEY_CURRENT_USER',
r'SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings',
'ProxyServer')
servers = reg_val['vdata']
servers = __salt__['reg.read_value'](
hive='HKEY_CURRENT_USER',
key=r'SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings',
vname='ProxyServer')['vdata']
if "=" in servers:
if servers and "=" in servers:
split = servers.split(";")
for s in split:
if len(s) == 0:
@ -87,16 +87,19 @@ def _get_proxy_windows(types=None):
del ret[key]
# Return enabled info
reg_val = __salt__['reg.read_value']('HKEY_CURRENT_USER',
r'SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings',
'ProxyEnable')
enabled = reg_val.get('vdata', 0)
ret['enabled'] = True if enabled == 1 else False
ret['enabled'] = __salt__['reg.read_value'](
hive='HKEY_CURRENT_USER',
key=r'SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings',
vname='ProxyEnable')['vdata'] == 1
return ret
def _set_proxy_windows(server, port, types=None, bypass_hosts=None, import_winhttp=True):
def _set_proxy_windows(server,
port,
types=None,
bypass_hosts=None,
import_winhttp=True):
if types is None:
types = ['http', 'https', 'ftp']
@ -104,17 +107,27 @@ def _set_proxy_windows(server, port, types=None, bypass_hosts=None, import_winht
for t in types:
server_str += '{0}={1}:{2};'.format(t, server, port)
__salt__['reg.set_value']('HKEY_CURRENT_USER', r'SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings',
'ProxyServer', server_str)
__salt__['reg.set_value'](
hive='HKEY_CURRENT_USER',
key=r'SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings',
vname='ProxyServer',
vdata=server_str)
__salt__['reg.set_value']('HKEY_CURRENT_USER', r'SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings',
'ProxyEnable', 1, vtype='REG_DWORD')
__salt__['reg.set_value'](
hive='HKEY_CURRENT_USER',
key=r'SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings',
vname='ProxyEnable',
vdata=1,
vtype='REG_DWORD')
if bypass_hosts is not None:
bypass_hosts_str = '<local>;{0}'.format(';'.join(bypass_hosts))
__salt__['reg.set_value']('HKEY_CURRENT_USER', r'SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings',
'ProxyOverride', bypass_hosts_str)
__salt__['reg.set_value'](
hive='HKEY_CURRENT_USER',
key=r'SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings',
vname='ProxyOverride',
vdata=bypass_hosts_str)
if import_winhttp:
cmd = 'netsh winhttp import proxy source=ie'
@ -138,15 +151,22 @@ def get_http_proxy(network_service="Ethernet"):
salt '*' proxy.get_http_proxy Ethernet
'''
if __grains__['os'] == 'Windows':
return _get_proxy_windows(['http'])
return _get_proxy_windows(types=['http'])
return _get_proxy_osx("getwebproxy", network_service)
return _get_proxy_osx(cmd_function="getwebproxy",
network_service=network_service)
def set_http_proxy(server, port, user=None, password=None, network_service="Ethernet", bypass_hosts=None):
def set_http_proxy(server,
port,
user=None,
password=None,
network_service="Ethernet",
bypass_hosts=None):
'''
Sets the http proxy settings. Note: On Windows this will override any other proxy settings you have,
the preferred method of updating proxies on windows is using set_proxy.
Sets the http proxy settings. Note: On Windows this will override any other
proxy settings you have, the preferred method of updating proxies on windows
is using set_proxy.
server
The proxy server to use
@ -165,8 +185,8 @@ def set_http_proxy(server, port, user=None, password=None, network_service="Ethe
macOS
bypass_hosts
The hosts that are allowed to by pass the proxy. Only used on Windows for other OS's use
set_proxy_bypass to edit the bypass hosts.
The hosts that are allowed to by pass the proxy. Only used on Windows
for other OS's use set_proxy_bypass to edit the bypass hosts.
CLI Example:
@ -175,9 +195,17 @@ def set_http_proxy(server, port, user=None, password=None, network_service="Ethe
salt '*' proxy.set_http_proxy example.com 1080 user=proxy_user password=proxy_pass network_service=Ethernet
'''
if __grains__['os'] == 'Windows':
return _set_proxy_windows(server, port, ['http'], bypass_hosts)
return _set_proxy_windows(server=server,
port=port,
types=['http'],
bypass_hosts=bypass_hosts)
return _set_proxy_osx("setwebproxy", server, port, user, password, network_service)
return _set_proxy_osx(cmd_function="setwebproxy",
server=server,
port=port,
user=user,
password=password,
network_service=network_service)
def get_https_proxy(network_service="Ethernet"):
@ -195,15 +223,22 @@ def get_https_proxy(network_service="Ethernet"):
salt '*' proxy.get_https_proxy Ethernet
'''
if __grains__['os'] == 'Windows':
return _get_proxy_windows(['https'])
return _get_proxy_windows(types=['https'])
return _get_proxy_osx("getsecurewebproxy", network_service)
return _get_proxy_osx(cmd_function="getsecurewebproxy",
network_service=network_service)
def set_https_proxy(server, port, user=None, password=None, network_service="Ethernet", bypass_hosts=None):
def set_https_proxy(server,
port,
user=None,
password=None,
network_service="Ethernet",
bypass_hosts=None):
'''
Sets the https proxy settings. Note: On Windows this will override any other proxy settings you have,
the preferred method of updating proxies on windows is using set_proxy.
Sets the https proxy settings. Note: On Windows this will override any other
proxy settings you have, the preferred method of updating proxies on windows
is using set_proxy.
server
The proxy server to use
@ -222,8 +257,8 @@ def set_https_proxy(server, port, user=None, password=None, network_service="Eth
macOS
bypass_hosts
The hosts that are allowed to by pass the proxy. Only used on Windows for other OS's use
set_proxy_bypass to edit the bypass hosts.
The hosts that are allowed to by pass the proxy. Only used on Windows
for other OS's use set_proxy_bypass to edit the bypass hosts.
CLI Example:
@ -232,9 +267,17 @@ def set_https_proxy(server, port, user=None, password=None, network_service="Eth
salt '*' proxy.set_https_proxy example.com 1080 user=proxy_user password=proxy_pass network_service=Ethernet
'''
if __grains__['os'] == 'Windows':
return _set_proxy_windows(server, port, ['https'], bypass_hosts)
return _set_proxy_windows(server=server,
port=port,
types=['https'],
bypass_hosts=bypass_hosts)
return _set_proxy_osx("setsecurewebproxy", server, port, user, password, network_service)
return _set_proxy_osx(cmd_function="setsecurewebproxy",
server=server,
port=port,
user=user,
password=password,
network_service=network_service)
def get_ftp_proxy(network_service="Ethernet"):
@ -252,12 +295,18 @@ def get_ftp_proxy(network_service="Ethernet"):
salt '*' proxy.get_ftp_proxy Ethernet
'''
if __grains__['os'] == 'Windows':
return _get_proxy_windows(['ftp'])
return _get_proxy_windows(types=['ftp'])
return _get_proxy_osx("getftpproxy", network_service)
return _get_proxy_osx(cmd_function="getftpproxy",
network_service=network_service)
def set_ftp_proxy(server, port, user=None, password=None, network_service="Ethernet", bypass_hosts=None):
def set_ftp_proxy(server,
port,
user=None,
password=None,
network_service="Ethernet",
bypass_hosts=None):
'''
Sets the ftp proxy settings
@ -278,8 +327,8 @@ def set_ftp_proxy(server, port, user=None, password=None, network_service="Ether
macOS
bypass_hosts
The hosts that are allowed to by pass the proxy. Only used on Windows for other OS's use
set_proxy_bypass to edit the bypass hosts.
The hosts that are allowed to by pass the proxy. Only used on Windows
for other OS's use set_proxy_bypass to edit the bypass hosts.
CLI Example:
@ -288,9 +337,17 @@ def set_ftp_proxy(server, port, user=None, password=None, network_service="Ether
salt '*' proxy.set_ftp_proxy example.com 1080 user=proxy_user password=proxy_pass network_service=Ethernet
'''
if __grains__['os'] == 'Windows':
return _set_proxy_windows(server, port, ['ftp'], bypass_hosts)
return _set_proxy_windows(server=server,
port=port,
types=['ftp'],
bypass_hosts=bypass_hosts)
return _set_proxy_osx("setftpproxy", server, port, user, password, network_service)
return _set_proxy_osx(cmd_function="setftpproxy",
server=server,
port=port,
user=user,
password=password,
network_service=network_service)
def get_proxy_bypass(network_service="Ethernet"):
@ -309,12 +366,16 @@ def get_proxy_bypass(network_service="Ethernet"):
'''
if __grains__['os'] == 'Windows':
reg_val = __salt__['reg.read_value']('HKEY_CURRENT_USER',
r'SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings',
'ProxyOverride')
bypass_servers = reg_val['vdata'].replace("<local>", "").split(";")
reg_val = __salt__['reg.read_value'](
hive='HKEY_CURRENT_USER',
key=r'SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings',
vname='ProxyOverride')['vdata']
return bypass_servers
# `reg.read_value` returns None if the key doesn't exist
if reg_val is None:
return []
return reg_val.replace('<local>', '').split(';')
out = __salt__['cmd.run']('networksetup -getproxybypassdomains {0}'.format(network_service))
@ -357,7 +418,12 @@ def set_proxy_win(server, port, types=None, bypass_hosts=None):
The password to use if required by the server
types
The types of proxy connections should be setup with this server. Valid types are http and https.
The types of proxy connections should be setup with this server. Valid
types are:
- ``http``
- ``https``
- ``ftp``
bypass_hosts
The hosts that are allowed to by pass the proxy.
@ -369,7 +435,10 @@ def set_proxy_win(server, port, types=None, bypass_hosts=None):
salt '*' proxy.set_http_proxy example.com 1080 types="['http', 'https']"
'''
if __grains__['os'] == 'Windows':
return _set_proxy_windows(server, port, types, bypass_hosts)
return _set_proxy_windows(server=server,
port=port,
types=types,
bypass_hosts=bypass_hosts)
def get_proxy_win():

View File

@ -225,6 +225,7 @@ def list_users(runas=None):
runas = salt.utils.user.get_user()
res = __salt__['cmd.run_all'](
[RABBITMQCTL, 'list_users', '-q'],
reset_system_locale=False,
runas=runas,
python_shell=False)
@ -248,6 +249,7 @@ def list_vhosts(runas=None):
runas = salt.utils.user.get_user()
res = __salt__['cmd.run_all'](
[RABBITMQCTL, 'list_vhosts', '-q'],
reset_system_locale=False,
runas=runas,
python_shell=False)
_check_response(res)
@ -322,6 +324,7 @@ def add_user(name, password=None, runas=None):
res = __salt__['cmd.run_all'](
cmd,
reset_system_locale=False,
output_loglevel='quiet',
runas=runas,
python_shell=python_shell)
@ -354,6 +357,7 @@ def delete_user(name, runas=None):
runas = salt.utils.user.get_user()
res = __salt__['cmd.run_all'](
[RABBITMQCTL, 'delete_user', name],
reset_system_locale=False,
python_shell=False,
runas=runas)
msg = 'Deleted'
@ -389,6 +393,7 @@ def change_password(name, password, runas=None):
cmd = [RABBITMQCTL, 'change_password', name, password]
res = __salt__['cmd.run_all'](
cmd,
reset_system_locale=False,
runas=runas,
output_loglevel='quiet',
python_shell=python_shell)
@ -411,6 +416,7 @@ def clear_password(name, runas=None):
runas = salt.utils.user.get_user()
res = __salt__['cmd.run_all'](
[RABBITMQCTL, 'clear_password', name],
reset_system_locale=False,
runas=runas,
python_shell=False)
msg = 'Password Cleared'
@ -436,7 +442,7 @@ def check_password(name, password, runas=None):
runas = salt.utils.user.get_user()
try:
res = __salt__['cmd.run']([RABBITMQCTL, 'status'], runas=runas, python_shell=False)
res = __salt__['cmd.run']([RABBITMQCTL, 'status'], reset_system_locale=False, runas=runas, python_shell=False)
server_version = re.search(r'\{rabbit,"RabbitMQ","(.+)"\}', res)
if server_version is None:
@ -468,6 +474,7 @@ def check_password(name, password, runas=None):
res = __salt__['cmd.run_all'](
cmd,
reset_system_locale=False,
runas=runas,
output_loglevel='quiet',
python_shell=python_shell)
@ -483,6 +490,7 @@ def check_password(name, password, runas=None):
res = __salt__['cmd.run_all'](
[RABBITMQCTL, 'eval', cmd],
reset_system_locale=False,
runas=runas,
output_loglevel='quiet',
python_shell=False)
@ -511,6 +519,7 @@ def add_vhost(vhost, runas=None):
runas = salt.utils.user.get_user()
res = __salt__['cmd.run_all'](
[RABBITMQCTL, 'add_vhost', vhost],
reset_system_locale=False,
runas=runas,
python_shell=False)
@ -532,6 +541,7 @@ def delete_vhost(vhost, runas=None):
runas = salt.utils.user.get_user()
res = __salt__['cmd.run_all'](
[RABBITMQCTL, 'delete_vhost', vhost],
reset_system_locale=False,
runas=runas,
python_shell=False)
msg = 'Deleted'
@ -553,6 +563,7 @@ def set_permissions(vhost, user, conf='.*', write='.*', read='.*', runas=None):
res = __salt__['cmd.run_all'](
[RABBITMQCTL, 'set_permissions', '-p',
vhost, user, conf, write, read],
reset_system_locale=False,
runas=runas,
python_shell=False)
msg = 'Permissions Set'
@ -573,6 +584,7 @@ def list_permissions(vhost, runas=None):
runas = salt.utils.user.get_user()
res = __salt__['cmd.run_all'](
[RABBITMQCTL, 'list_permissions', '-q', '-p', vhost],
reset_system_locale=False,
runas=runas,
python_shell=False)
@ -593,6 +605,7 @@ def list_user_permissions(name, runas=None):
runas = salt.utils.user.get_user()
res = __salt__['cmd.run_all'](
[RABBITMQCTL, 'list_user_permissions', name, '-q'],
reset_system_locale=False,
runas=runas,
python_shell=False)
@ -616,6 +629,7 @@ def set_user_tags(name, tags, runas=None):
res = __salt__['cmd.run_all'](
[RABBITMQCTL, 'set_user_tags', name] + list(tags),
reset_system_locale=False,
runas=runas,
python_shell=False)
msg = "Tag(s) set"
@ -636,6 +650,7 @@ def status(runas=None):
runas = salt.utils.user.get_user()
res = __salt__['cmd.run_all'](
[RABBITMQCTL, 'status'],
reset_system_locale=False,
runas=runas,
python_shell=False)
_check_response(res)
@ -656,6 +671,7 @@ def cluster_status(runas=None):
runas = salt.utils.user.get_user()
res = __salt__['cmd.run_all'](
[RABBITMQCTL, 'cluster_status'],
reset_system_locale=False,
runas=runas,
python_shell=False)
_check_response(res)
@ -680,7 +696,7 @@ def join_cluster(host, user='rabbit', ram_node=None, runas=None):
if runas is None and not salt.utils.platform.is_windows():
runas = salt.utils.user.get_user()
stop_app(runas)
res = __salt__['cmd.run_all'](cmd, runas=runas, python_shell=False)
res = __salt__['cmd.run_all'](cmd, reset_system_locale=False, runas=runas, python_shell=False)
start_app(runas)
return _format_response(res, 'Join')
@ -700,6 +716,7 @@ def stop_app(runas=None):
runas = salt.utils.user.get_user()
res = __salt__['cmd.run_all'](
[RABBITMQCTL, 'stop_app'],
reset_system_locale=False,
runas=runas,
python_shell=False)
_check_response(res)
@ -720,6 +737,7 @@ def start_app(runas=None):
runas = salt.utils.user.get_user()
res = __salt__['cmd.run_all'](
[RABBITMQCTL, 'start_app'],
reset_system_locale=False,
runas=runas,
python_shell=False)
_check_response(res)
@ -740,6 +758,7 @@ def reset(runas=None):
runas = salt.utils.user.get_user()
res = __salt__['cmd.run_all'](
[RABBITMQCTL, 'reset'],
reset_system_locale=False,
runas=runas,
python_shell=False)
_check_response(res)
@ -760,6 +779,7 @@ def force_reset(runas=None):
runas = salt.utils.user.get_user()
res = __salt__['cmd.run_all'](
[RABBITMQCTL, 'force_reset'],
reset_system_locale=False,
runas=runas,
python_shell=False)
_check_response(res)
@ -780,7 +800,7 @@ def list_queues(runas=None, *args):
runas = salt.utils.user.get_user()
cmd = [RABBITMQCTL, 'list_queues', '-q']
cmd.extend(args)
res = __salt__['cmd.run_all'](cmd, runas=runas, python_shell=False)
res = __salt__['cmd.run_all'](cmd, reset_system_locale=False, runas=runas, python_shell=False)
_check_response(res)
return _output_to_dict(res['stdout'])
@ -802,7 +822,7 @@ def list_queues_vhost(vhost, runas=None, *args):
runas = salt.utils.user.get_user()
cmd = [RABBITMQCTL, 'list_queues', '-q', '-p', vhost]
cmd.extend(args)
res = __salt__['cmd.run_all'](cmd, runas=runas, python_shell=False)
res = __salt__['cmd.run_all'](cmd, reset_system_locale=False, runas=runas, python_shell=False)
_check_response(res)
return _output_to_dict(res['stdout'])
@ -825,6 +845,7 @@ def list_policies(vhost="/", runas=None):
runas = salt.utils.user.get_user()
res = __salt__['cmd.run_all'](
[RABBITMQCTL, 'list_policies', '-q', '-p', vhost],
reset_system_locale=False,
runas=runas,
python_shell=False)
_check_response(res)
@ -902,7 +923,7 @@ def set_policy(vhost,
if apply_to:
cmd.extend(['--apply-to', apply_to])
cmd.extend([name, pattern, definition])
res = __salt__['cmd.run_all'](cmd, runas=runas, python_shell=False)
res = __salt__['cmd.run_all'](cmd, reset_system_locale=False, runas=runas, python_shell=False)
log.debug('Set policy: %s', res['stdout'])
return _format_response(res, 'Set')
@ -923,6 +944,7 @@ def delete_policy(vhost, name, runas=None):
runas = salt.utils.user.get_user()
res = __salt__['cmd.run_all'](
[RABBITMQCTL, 'clear_policy', '-p', vhost, name],
reset_system_locale=False,
runas=runas,
python_shell=False)
log.debug('Delete policy: %s', res['stdout'])
@ -960,7 +982,7 @@ def list_available_plugins(runas=None):
if runas is None and not salt.utils.platform.is_windows():
runas = salt.utils.user.get_user()
cmd = [_get_rabbitmq_plugin(), 'list', '-m']
ret = __salt__['cmd.run_all'](cmd, python_shell=False, runas=runas)
ret = __salt__['cmd.run_all'](cmd, reset_system_locale=False, python_shell=False, runas=runas)
_check_response(ret)
return _output_to_list(ret['stdout'])
@ -978,7 +1000,7 @@ def list_enabled_plugins(runas=None):
if runas is None and not salt.utils.platform.is_windows():
runas = salt.utils.user.get_user()
cmd = [_get_rabbitmq_plugin(), 'list', '-m', '-e']
ret = __salt__['cmd.run_all'](cmd, python_shell=False, runas=runas)
ret = __salt__['cmd.run_all'](cmd, reset_system_locale=False, python_shell=False, runas=runas)
_check_response(ret)
return _output_to_list(ret['stdout'])
@ -1011,7 +1033,7 @@ def enable_plugin(name, runas=None):
if runas is None and not salt.utils.platform.is_windows():
runas = salt.utils.user.get_user()
cmd = [_get_rabbitmq_plugin(), 'enable', name]
ret = __salt__['cmd.run_all'](cmd, runas=runas, python_shell=False)
ret = __salt__['cmd.run_all'](cmd, reset_system_locale=False, runas=runas, python_shell=False)
return _format_response(ret, 'Enabled')
@ -1028,5 +1050,5 @@ def disable_plugin(name, runas=None):
if runas is None and not salt.utils.platform.is_windows():
runas = salt.utils.user.get_user()
cmd = [_get_rabbitmq_plugin(), 'disable', name]
ret = __salt__['cmd.run_all'](cmd, runas=runas, python_shell=False)
ret = __salt__['cmd.run_all'](cmd, reset_system_locale=False, runas=runas, python_shell=False)
return _format_response(ret, 'Disabled')

View File

@ -19,6 +19,7 @@ Module to provide redis functionality to Salt
from __future__ import absolute_import, unicode_literals, print_function
from salt.ext.six.moves import zip
from salt.ext import six
from salt.utils import clean_kwargs
from datetime import datetime
# Import third party libs
@ -395,7 +396,7 @@ def hmset(key, **fieldsvals):
database = fieldsvals.pop('db', None)
password = fieldsvals.pop('password', None)
server = _connect(host, port, database, password)
return server.hmset(key, **fieldsvals)
return server.hmset(key, clean_kwargs(**fieldsvals))
def hset(key, field, value, host=None, port=None, db=None, password=None):

View File

@ -37,6 +37,7 @@ def __virtual__():
'Devuan',
'Arch',
'Arch ARM',
'Manjaro',
'ALT',
'SUSE Enterprise Server',
'SUSE',

View File

@ -137,13 +137,15 @@ def renderer(path=None, string=None, default_renderer='jinja|yaml', **kwargs):
path_or_string = ':string:'
kwargs['input_data'] = string
return salt.template.compile_template(
path_or_string,
renderers,
default_renderer,
__opts__['renderer_blacklist'],
__opts__['renderer_whitelist'],
**kwargs)
ret = salt.template.compile_template(
path_or_string,
renderers,
default_renderer,
__opts__['renderer_blacklist'],
__opts__['renderer_whitelist'],
**kwargs
)
return ret.read() if __utils__['stringio.is_readable'](ret) else ret
def _get_serialize_fn(serializer, fn_name):

View File

@ -29,18 +29,13 @@ from salt.ext.six.moves import zip # pylint: disable=import-error,redefined-bui
log = logging.getLogger(__name__)
DMIDECODER = salt.utils.path.which_bin(['dmidecode', 'smbios'])
def __virtual__():
'''
Only work when dmidecode is installed.
'''
if DMIDECODER is None:
log.debug('SMBIOS: neither dmidecode nor smbios found!')
return (False, 'The smbios execution module failed to load: neither dmidecode nor smbios in the path.')
else:
return True
return (bool(salt.utils.path.which_bin(['dmidecode', 'smbios'])),
'The smbios execution module failed to load: neither dmidecode nor smbios in the path.')
def get(string, clean=True):
@ -86,16 +81,12 @@ def get(string, clean=True):
val = _dmidecoder('-s {0}'.format(string)).strip()
# Sometimes dmidecode delivers comments in strings.
# Don't.
# Cleanup possible comments in strings.
val = '\n'.join([v for v in val.split('\n') if not v.startswith('#')])
if val.startswith('/dev/mem') or clean and not _dmi_isclean(string, val):
val = None
# handle missing /dev/mem
if val.startswith('/dev/mem'):
return None
if not clean or _dmi_isclean(string, val):
return val
return val
def records(rec_type=None, fields=None, clean=True):
@ -327,7 +318,11 @@ def _dmidecoder(args=None):
'''
Call DMIdecode
'''
if args is None:
return salt.modules.cmdmod._run_quiet(DMIDECODER)
dmidecoder = salt.utils.path.which_bin(['dmidecode', 'smbios'])
if not args:
out = salt.modules.cmdmod._run_quiet(dmidecoder)
else:
return salt.modules.cmdmod._run_quiet('{0} {1}'.format(DMIDECODER, args))
out = salt.modules.cmdmod._run_quiet('{0} {1}'.format(dmidecoder, args))
return out

View File

@ -602,8 +602,7 @@ def template_str(tem, queue=False, **kwargs):
return ret
def apply_(mods=None,
**kwargs):
def apply_(mods=None, **kwargs):
'''
.. versionadded:: 2015.5.0
@ -743,6 +742,22 @@ def apply_(mods=None,
.. code-block:: bash
salt '*' state.apply test localconfig=/path/to/minion.yml
sync_mods
If specified, the desired custom module types will be synced prior to
running the SLS files:
.. code-block:: bash
salt '*' state.apply test sync_mods=states,modules
salt '*' state.apply test sync_mods=all
.. note::
This option is ignored when no SLS files are specified, as a
:ref:`highstate <running-highstate>` automatically syncs all custom
module types.
.. versionadded:: 2017.7.8,2018.3.3,Fluorine
'''
if mods:
return sls(mods, **kwargs)
@ -1068,7 +1083,7 @@ def highstate(test=None, queue=False, **kwargs):
return ret
def sls(mods, test=None, exclude=None, queue=False, **kwargs):
def sls(mods, test=None, exclude=None, queue=False, sync_mods=None, **kwargs):
'''
Execute the states in one or more SLS files
@ -1160,6 +1175,17 @@ def sls(mods, test=None, exclude=None, queue=False, **kwargs):
.. versionadded:: 2015.8.4
sync_mods
If specified, the desired custom module types will be synced prior to
running the SLS files:
.. code-block:: bash
salt '*' state.sls test sync_mods=states,modules
salt '*' state.sls test sync_mods=all
.. versionadded:: 2017.7.8,2018.3.3,Fluorine
CLI Example:
.. code-block:: bash
@ -1223,6 +1249,28 @@ def sls(mods, test=None, exclude=None, queue=False, **kwargs):
'{0}.cache.p'.format(kwargs.get('cache_name', 'highstate'))
)
if sync_mods is True:
sync_mods = ['all']
if sync_mods is not None:
sync_mods = salt.utils.args.split_input(sync_mods)
else:
sync_mods = []
if 'all' in sync_mods and sync_mods != ['all']:
# Prevent unnecessary extra syncing
sync_mods = ['all']
for module_type in sync_mods:
try:
__salt__['saltutil.sync_{0}'.format(module_type)](
saltenv=opts['saltenv']
)
except KeyError:
log.warning(
'Invalid custom module type \'%s\', ignoring',
module_type
)
try:
st_ = salt.state.HighState(opts,
pillar_override,
@ -1253,6 +1301,10 @@ def sls(mods, test=None, exclude=None, queue=False, **kwargs):
high_ = serial.load(fp_)
return st_.state.call_high(high_, orchestration_jid)
# If the state file is an integer, convert to a string then to unicode
if isinstance(mods, six.integer_types):
mods = salt.utils.stringutils.to_unicode(str(mods)) # future lint: disable=blacklisted-function
if isinstance(mods, six.string_types):
mods = mods.split(',')

View File

@ -2010,7 +2010,7 @@ def snapshot(domain, name=None, suffix=None):
n_name = ElementTree.SubElement(doc, 'name')
n_name.text = name
_get_domain(domain).snapshotCreateXML(ElementTree.tostring(doc))
_get_domain(domain).snapshotCreateXML(salt.utils.stringutils.to_str(ElementTree.tostring(doc)))
return {'name': name}

View File

@ -58,8 +58,9 @@ from salt.modules.file import (check_hash, # pylint: disable=W0611
RE_FLAG_TABLE, blockreplace, prepend, seek_read, seek_write, rename,
lstat, path_exists_glob, write, pardir, join, HASHES, HASHES_REVMAP,
comment, uncomment, _add_flags, comment_line, _regex_to_static,
_get_line_indent, apply_template_on_contents, dirname, basename,
list_backups_dir, _assert_occurrence, _starts_till)
_set_line_indent, apply_template_on_contents, dirname, basename,
list_backups_dir, _assert_occurrence, _starts_till, _set_line_eol, _get_eol,
_insert_line_after, _insert_line_before)
from salt.modules.file import normpath as normpath_
from salt.utils.functools import namespaced_function as _namespaced_function
@ -116,8 +117,9 @@ def __virtual__():
global blockreplace, prepend, seek_read, seek_write, rename, lstat
global write, pardir, join, _add_flags, apply_template_on_contents
global path_exists_glob, comment, uncomment, _mkstemp_copy
global _regex_to_static, _get_line_indent, dirname, basename
global _regex_to_static, _set_line_indent, dirname, basename
global list_backups_dir, normpath_, _assert_occurrence, _starts_till
global _insert_line_before, _insert_line_after, _set_line_eol, _get_eol
replace = _namespaced_function(replace, globals())
search = _namespaced_function(search, globals())
@ -173,7 +175,11 @@ def __virtual__():
uncomment = _namespaced_function(uncomment, globals())
comment_line = _namespaced_function(comment_line, globals())
_regex_to_static = _namespaced_function(_regex_to_static, globals())
_get_line_indent = _namespaced_function(_get_line_indent, globals())
_set_line_indent = _namespaced_function(_set_line_indent, globals())
_set_line_eol = _namespaced_function(_set_line_eol, globals())
_get_eol = _namespaced_function(_get_eol, globals())
_insert_line_after = _namespaced_function(_insert_line_after, globals())
_insert_line_before = _namespaced_function(_insert_line_before, globals())
_mkstemp_copy = _namespaced_function(_mkstemp_copy, globals())
_add_flags = _namespaced_function(_add_flags, globals())
apply_template_on_contents = _namespaced_function(apply_template_on_contents, globals())

View File

@ -2588,7 +2588,9 @@ class _policy_info(object):
userSid = '{1}\\{0}'.format(userSid[0], userSid[1])
else:
userSid = '{0}'.format(userSid[0])
# TODO: This needs to be more specific
except Exception:
log.exception('Handle this explicitly')
userSid = win32security.ConvertSidToStringSid(_sid)
usernames.append(userSid)
return usernames
@ -2607,7 +2609,9 @@ class _policy_info(object):
try:
sid = win32security.LookupAccountName('', _user)[0]
sids.append(sid)
# This needs to be more specific
except Exception as e:
log.exception('Handle this explicitly')
raise CommandExecutionError((
'There was an error obtaining the SID of user "{0}". Error '
'returned: {1}'
@ -2760,7 +2764,9 @@ def _processPolicyDefinitions(policy_def_path='c:\\Windows\\PolicyDefinitions',
except lxml.etree.XMLSyntaxError:
try:
xmltree = _remove_unicode_encoding(admfile)
# TODO: This needs to be more specific
except Exception:
log.exception('Handle this explicitly')
log.error('A error was found while processing admx '
'file %s, all policies from this file will '
'be unavailable via this module', admfile)
@ -2845,7 +2851,9 @@ def _processPolicyDefinitions(policy_def_path='c:\\Windows\\PolicyDefinitions',
# see issue #38100
try:
xmltree = _remove_unicode_encoding(adml_file)
# TODO: This needs to be more specific
except Exception:
log.exception('Handle this explicitly')
log.error('An error was found while processing '
'adml file %s, all policy '
'language data from this file will be '
@ -2901,8 +2909,9 @@ def _findOptionValueInSeceditFile(option):
if _line.startswith(option):
return True, _line.split('=')[1].strip()
return True, 'Not Defined'
except Exception as e:
log.debug('error occurred while trying to get secedit data')
# TODO: This needs to be more specific
except Exception:
log.exception('error occurred while trying to get secedit data')
return False, None
@ -2932,8 +2941,9 @@ def _importSeceditConfig(infdata):
if __salt__['file.file_exists'](_tInfFile):
_ret = __salt__['file.remove'](_tInfFile)
return True
# TODO: This needs to be more specific
except Exception as e:
log.debug('error occurred while trying to import secedit data')
log.exception('error occurred while trying to import secedit data')
return False
@ -2995,9 +3005,10 @@ def _addAccountRights(sidObject, user_right):
user_rights_list = [user_right]
_ret = win32security.LsaAddAccountRights(_polHandle, sidObject, user_rights_list)
return True
# TODO: This needs to be more specific
except Exception as e:
log.error('Error attempting to add account right, exception was %s',
e)
log.exception('Error attempting to add account right, exception was %s',
e)
return False
@ -3011,8 +3022,7 @@ def _delAccountRights(sidObject, user_right):
_ret = win32security.LsaRemoveAccountRights(_polHandle, sidObject, False, user_rights_list)
return True
except Exception as e:
log.error('Error attempting to delete account right, '
'exception was %s', e)
log.exception('Error attempting to delete account right')
return False
@ -4180,7 +4190,7 @@ def _write_regpol_data(data_to_write,
try:
reg_pol_header = u'\u5250\u6765\x01\x00'
if not os.path.exists(policy_file_path):
ret = __salt__['file.makedirs'](policy_file_path)
__salt__['file.makedirs'](policy_file_path)
with salt.utils.files.fopen(policy_file_path, 'wb') as pol_file:
if not data_to_write.startswith(reg_pol_header.encode('utf-16-le')):
pol_file.write(reg_pol_header.encode('utf-16-le'))
@ -4188,11 +4198,12 @@ def _write_regpol_data(data_to_write,
try:
gpt_ini_data = ''
if os.path.exists(gpt_ini_path):
with salt.utils.files.fopen(gpt_ini_path, 'rb') as gpt_file:
with salt.utils.files.fopen(gpt_ini_path, 'r') as gpt_file:
gpt_ini_data = gpt_file.read()
if not _regexSearchRegPolData(r'\[General\]\r\n', gpt_ini_data):
gpt_ini_data = '[General]\r\n' + gpt_ini_data
if _regexSearchRegPolData(r'{0}='.format(re.escape(gpt_extension)), gpt_ini_data):
if _regexSearchRegPolData(r'{0}='.format(re.escape(gpt_extension)),
gpt_ini_data):
# ensure the line contains the ADM guid
gpt_ext_loc = re.search(r'^{0}=.*\r\n'.format(re.escape(gpt_extension)),
gpt_ini_data,
@ -4208,9 +4219,10 @@ def _write_regpol_data(data_to_write,
general_location = re.search(r'^\[General\]\r\n',
gpt_ini_data,
re.IGNORECASE | re.MULTILINE)
gpt_ini_data = "{0}{1}={2}\r\n{3}".format(
gpt_ini_data = '{0}{1}={2}\r\n{3}'.format(
gpt_ini_data[general_location.start():general_location.end()],
gpt_extension, gpt_extension_guid,
gpt_extension,
gpt_extension_guid,
gpt_ini_data[general_location.end():])
# https://technet.microsoft.com/en-us/library/cc978247.aspx
if _regexSearchRegPolData(r'Version=', gpt_ini_data):
@ -4225,9 +4237,10 @@ def _write_regpol_data(data_to_write,
elif gpt_extension.lower() == 'gPCUserExtensionNames'.lower():
version_nums = (version_nums[0] + 1, version_nums[1])
version_num = struct.unpack(b'>I', struct.pack(b'>2H', *version_nums))[0]
gpt_ini_data = "{0}{1}={2}\r\n{3}".format(
gpt_ini_data = '{0}{1}={2}\r\n{3}'.format(
gpt_ini_data[0:version_loc.start()],
'Version', version_num,
'Version',
version_num,
gpt_ini_data[version_loc.end():])
else:
general_location = re.search(r'^\[General\]\r\n',
@ -4237,20 +4250,26 @@ def _write_regpol_data(data_to_write,
version_nums = (0, 1)
elif gpt_extension.lower() == 'gPCUserExtensionNames'.lower():
version_nums = (1, 0)
gpt_ini_data = "{0}{1}={2}\r\n{3}".format(
gpt_ini_data = '{0}{1}={2}\r\n{3}'.format(
gpt_ini_data[general_location.start():general_location.end()],
'Version',
int("{0}{1}".format(six.text_type(version_nums[0]).zfill(4), six.text_type(version_nums[1]).zfill(4)), 16),
int("{0}{1}".format(six.text_type(version_nums[0]).zfill(4),
six.text_type(version_nums[1]).zfill(4)),
16),
gpt_ini_data[general_location.end():])
if gpt_ini_data:
with salt.utils.files.fopen(gpt_ini_path, 'wb') as gpt_file:
gpt_file.write(salt.utils.stringutils.to_bytes(gpt_ini_data))
with salt.utils.files.fopen(gpt_ini_path, 'w') as gpt_file:
gpt_file.write(gpt_ini_data)
# TODO: This needs to be more specific
except Exception as e:
msg = 'An error occurred attempting to write to {0}, the exception was {1}'.format(
gpt_ini_path, e)
log.exception(msg)
raise CommandExecutionError(msg)
# TODO: This needs to be more specific
except Exception as e:
msg = 'An error occurred attempting to write to {0}, the exception was {1}'.format(policy_file_path, e)
log.exception(msg)
raise CommandExecutionError(msg)
@ -4648,8 +4667,9 @@ def _writeAdminTemplateRegPolFile(admtemplate_data,
policy_data.gpt_ini_path,
policy_data.admx_registry_classes[registry_class]['gpt_extension_location'],
policy_data.admx_registry_classes[registry_class]['gpt_extension_guid'])
# TODO: This needs to be more specific or removed
except Exception:
log.error('Unhandled exception %s occurred while attempting to write Adm Template Policy File')
log.exception('Unhandled exception %s occurred while attempting to write Adm Template Policy File')
return False
return True
@ -4671,7 +4691,7 @@ def _getScriptSettingsFromIniFile(policy_info):
_existingData = deserialize(_existingData.decode('utf-16-le').lstrip('\ufeff'))
log.debug('Have deserialized data %s', _existingData)
except Exception as error:
log.error('An error occurred attempting to deserialize data for %s', policy_info['Policy'])
log.exception('An error occurred attempting to deserialize data for %s', policy_info['Policy'])
raise CommandExecutionError(error)
if 'Section' in policy_info['ScriptIni'] and policy_info['ScriptIni']['Section'].lower() in [z.lower() for z in _existingData.keys()]:
if 'SettingName' in policy_info['ScriptIni']:
@ -5540,8 +5560,10 @@ def set_(computer_policy=None, user_policy=None,
_newModalSetData = dictupdate.update(_existingModalData, _modal_sets[_modal_set])
log.debug('NEW MODAL SET = %s', _newModalSetData)
_ret = win32net.NetUserModalsSet(None, _modal_set, _newModalSetData)
except:
# TODO: This needs to be more specific
except Exception:
msg = 'An unhandled exception occurred while attempting to set policy via NetUserModalSet'
log.exception(msg)
raise CommandExecutionError(msg)
if _admTemplateData:
_ret = False

View File

@ -7,50 +7,56 @@ powercfg.
.. code-block:: bash
# Set monitor to never turn off on Battery power
salt '*' powercfg.set_monitor_timeout 0 power=dc
# Set disk timeout to 120 minutes on AC power
salt '*' powercfg.set_disk_timeout 120 power=ac
'''
# Import Python Libs
from __future__ import absolute_import, unicode_literals, print_function
import re
import logging
# Import Salt Libs
import salt.utils.platform
import salt.utils.versions
log = logging.getLogger(__name__)
__virtualname__ = "powercfg"
__virtualname__ = 'powercfg'
def __virtual__():
'''
Only work on Windows
'''
if __grains__['os'] == 'Windows':
return __virtualname__
return (False, 'Module only works on Windows.')
if not salt.utils.platform.is_windows():
return False, 'PowerCFG: Module only works on Windows'
return __virtualname__
def _get_current_scheme():
cmd = "powercfg /getactivescheme"
cmd = 'powercfg /getactivescheme'
out = __salt__['cmd.run'](cmd, python_shell=False)
matches = re.search(r"GUID: (.*) \(", out)
matches = re.search(r'GUID: (.*) \(', out)
return matches.groups()[0].strip()
def _get_powercfg_minute_values(scheme, guid, subguid, safe_name):
'''
Returns the AC/DC values in an array for a guid and subguid for a the given scheme
Returns the AC/DC values in an dict for a guid and subguid for a the given
scheme
'''
if scheme is None:
scheme = _get_current_scheme()
if __grains__['osrelease'] == '7':
cmd = "powercfg /q {0} {1}".format(scheme, guid)
cmd = 'powercfg /q {0} {1}'.format(scheme, guid)
else:
cmd = "powercfg /q {0} {1} {2}".format(scheme, guid, subguid)
cmd = 'powercfg /q {0} {1} {2}'.format(scheme, guid, subguid)
out = __salt__['cmd.run'](cmd, python_shell=False)
split = out.split("\r\n\r\n")
split = out.split('\r\n\r\n')
if len(split) > 1:
for s in split:
if safe_name in s or subguid in s:
@ -59,172 +65,309 @@ def _get_powercfg_minute_values(scheme, guid, subguid, safe_name):
else:
out = split[0]
raw_settings = re.findall(r"Power Setting Index: ([0-9a-fx]+)", out)
return {"ac": int(raw_settings[0], 0) / 60, "dc": int(raw_settings[1], 0) / 60}
raw_settings = re.findall(r'Power Setting Index: ([0-9a-fx]+)', out)
return {'ac': int(raw_settings[0], 0) / 60,
'dc': int(raw_settings[1], 0) / 60}
def _set_powercfg_value(scheme, sub_group, setting_guid, power, value):
'''
Sets the value of a setting with a given power (ac/dc) to
the given scheme
Sets the AC/DC values of a setting with the given power for the given scheme
'''
salt.utils.versions.warn_until(
'Fluorine',
'This function now expects the timeout value in minutes instead of '
'seconds as stated in the documentation. This warning will be removed '
'in Salt Fluorine.')
if scheme is None:
scheme = _get_current_scheme()
cmd = "powercfg /set{0}valueindex {1} {2} {3} {4}".format(power, scheme, sub_group, setting_guid, value)
return __salt__['cmd.run'](cmd, python_shell=False)
cmd = 'powercfg /set{0}valueindex {1} {2} {3} {4}' \
''.format(power, scheme, sub_group, setting_guid, value * 60)
return __salt__['cmd.retcode'](cmd, python_shell=False) == 0
def set_monitor_timeout(timeout, power="ac", scheme=None):
def set_monitor_timeout(timeout, power='ac', scheme=None):
'''
Set the monitor timeout in minutes for the given power scheme
Args:
timeout (int):
The amount of time in minutes before the monitor will timeout
power (str):
Set the value for AC or DC power. Default is ``ac``. Valid options
are:
- ``ac`` (AC Power)
- ``dc`` (Battery)
scheme (str):
The scheme to use, leave as ``None`` to use the current. Default is
``None``. This can be the GUID or the Alias for the Scheme. Known
Aliases are:
- ``SCHEME_BALANCED`` - Balanced
- ``SCHEME_MAX`` - Power saver
- ``SCHEME_MIN`` - High performance
Returns:
bool: ``True`` if successful, otherwise ``False``
CLI Example:
.. code-block:: bash
salt '*' powercfg.set_monitor_timeout 30 power=ac
timeout
The amount of time in minutes before the monitor will timeout
power
Should we set the value for AC or DC (battery)? Valid options ac,dc.
scheme
The scheme to use, leave as None to use the current.
# Sets the monitor timeout to 30 minutes
salt '*' powercfg.set_monitor_timeout 30
'''
return _set_powercfg_value(scheme, "SUB_VIDEO", "VIDEOIDLE", power, timeout)
return _set_powercfg_value(
scheme=scheme,
sub_group='SUB_VIDEO',
setting_guid='VIDEOIDLE',
power=power,
value=timeout)
def get_monitor_timeout(scheme=None):
'''
Get the current monitor timeout of the given scheme
Args:
scheme (str):
The scheme to use, leave as ``None`` to use the current. Default is
``None``. This can be the GUID or the Alias for the Scheme. Known
Aliases are:
- ``SCHEME_BALANCED`` - Balanced
- ``SCHEME_MAX`` - Power saver
- ``SCHEME_MIN`` - High performance
Returns:
dict: A dictionary of both the AC and DC settings
CLI Example:
.. code-block:: bash
salt '*' powercfg.get_monitor_timeout
scheme
The scheme to use, leave as None to use the current.
'''
return _get_powercfg_minute_values(scheme, "SUB_VIDEO", "VIDEOIDLE", "Turn off display after")
return _get_powercfg_minute_values(
scheme=scheme,
guid='SUB_VIDEO',
subguid='VIDEOIDLE',
safe_name='Turn off display after')
def set_disk_timeout(timeout, power="ac", scheme=None):
def set_disk_timeout(timeout, power='ac', scheme=None):
'''
Set the disk timeout in minutes for the given power scheme
Args:
timeout (int):
The amount of time in minutes before the disk will timeout
power (str):
Set the value for AC or DC power. Default is ``ac``. Valid options
are:
- ``ac`` (AC Power)
- ``dc`` (Battery)
scheme (str):
The scheme to use, leave as ``None`` to use the current. Default is
``None``. This can be the GUID or the Alias for the Scheme. Known
Aliases are:
- ``SCHEME_BALANCED`` - Balanced
- ``SCHEME_MAX`` - Power saver
- ``SCHEME_MIN`` - High performance
Returns:
bool: ``True`` if successful, otherwise ``False``
CLI Example:
.. code-block:: bash
# Sets the disk timeout to 30 minutes on battery
salt '*' powercfg.set_disk_timeout 30 power=dc
timeout
The amount of time in minutes before the disk will timeout
power
Should we set the value for AC or DC (battery)? Valid options ac,dc.
scheme
The scheme to use, leave as None to use the current.
'''
return _set_powercfg_value(scheme, "SUB_DISK", "DISKIDLE", power, timeout)
return _set_powercfg_value(
scheme=scheme,
sub_group='SUB_DISK',
setting_guid='DISKIDLE',
power=power,
value=timeout)
def get_disk_timeout(scheme=None):
'''
Get the current disk timeout of the given scheme
Args:
scheme (str):
The scheme to use, leave as ``None`` to use the current. Default is
``None``. This can be the GUID or the Alias for the Scheme. Known
Aliases are:
- ``SCHEME_BALANCED`` - Balanced
- ``SCHEME_MAX`` - Power saver
- ``SCHEME_MIN`` - High performance
Returns:
dict: A dictionary of both the AC and DC settings
CLI Example:
.. code-block:: bash
salt '*' powercfg.get_disk_timeout
scheme
The scheme to use, leave as None to use the current.
'''
return _get_powercfg_minute_values(scheme, "SUB_DISK", "DISKIDLE", "Turn off hard disk after")
return _get_powercfg_minute_values(
scheme=scheme,
guid='SUB_DISK',
subguid='DISKIDLE',
safe_name='Turn off hard disk after')
def set_standby_timeout(timeout, power="ac", scheme=None):
def set_standby_timeout(timeout, power='ac', scheme=None):
'''
Set the standby timeout in minutes for the given power scheme
Args:
timeout (int):
The amount of time in minutes before the computer sleeps
power (str):
Set the value for AC or DC power. Default is ``ac``. Valid options
are:
- ``ac`` (AC Power)
- ``dc`` (Battery)
scheme (str):
The scheme to use, leave as ``None`` to use the current. Default is
``None``. This can be the GUID or the Alias for the Scheme. Known
Aliases are:
- ``SCHEME_BALANCED`` - Balanced
- ``SCHEME_MAX`` - Power saver
- ``SCHEME_MIN`` - High performance
Returns:
bool: ``True`` if successful, otherwise ``False``
CLI Example:
.. code-block:: bash
# Sets the system standby timeout to 30 minutes on Battery
salt '*' powercfg.set_standby_timeout 30 power=dc
timeout
The amount of time in minutes before the computer sleeps
power
Should we set the value for AC or DC (battery)? Valid options ac,dc.
scheme
The scheme to use, leave as None to use the current.
'''
return _set_powercfg_value(scheme, "SUB_SLEEP", "STANDBYIDLE", power, timeout)
return _set_powercfg_value(
scheme=scheme,
sub_group='SUB_SLEEP',
setting_guid='STANDBYIDLE',
power=power,
value=timeout)
def get_standby_timeout(scheme=None):
'''
Get the current standby timeout of the given scheme
scheme (str):
The scheme to use, leave as ``None`` to use the current. Default is
``None``. This can be the GUID or the Alias for the Scheme. Known
Aliases are:
- ``SCHEME_BALANCED`` - Balanced
- ``SCHEME_MAX`` - Power saver
- ``SCHEME_MIN`` - High performance
Returns:
dict: A dictionary of both the AC and DC settings
CLI Example:
.. code-block:: bash
salt '*' powercfg.get_standby_timeout
scheme
The scheme to use, leave as None to use the current.
'''
return _get_powercfg_minute_values(scheme, "SUB_SLEEP", "STANDBYIDLE", "Sleep after")
return _get_powercfg_minute_values(
scheme=scheme,
guid='SUB_SLEEP',
subguid='STANDBYIDLE',
safe_name='Sleep after')
def set_hibernate_timeout(timeout, power="ac", scheme=None):
def set_hibernate_timeout(timeout, power='ac', scheme=None):
'''
Set the hibernate timeout in minutes for the given power scheme
Args:
timeout (int):
The amount of time in minutes before the computer hibernates
power (str):
Set the value for AC or DC power. Default is ``ac``. Valid options
are:
- ``ac`` (AC Power)
- ``dc`` (Battery)
scheme (str):
The scheme to use, leave as ``None`` to use the current. Default is
``None``. This can be the GUID or the Alias for the Scheme. Known
Aliases are:
- ``SCHEME_BALANCED`` - Balanced
- ``SCHEME_MAX`` - Power saver
- ``SCHEME_MIN`` - High performance
Returns:
bool: ``True`` if successful, otherwise ``False``
CLI Example:
.. code-block:: bash
salt '*' powercfg.set_hibernate_timeout 30 power=pc
timeout
The amount of time in minutes before the computer hibernates
power
Should we set the value for AC or DC (battery)? Valid options ac,dc.
scheme
The scheme to use, leave as None to use the current.
# Sets the hibernate timeout to 30 minutes on Battery
salt '*' powercfg.set_hibernate_timeout 30 power=dc
'''
return _set_powercfg_value(scheme, "SUB_SLEEP", "HIBERNATEIDLE", power, timeout)
return _set_powercfg_value(
scheme=scheme,
sub_group='SUB_SLEEP',
setting_guid='HIBERNATEIDLE',
power=power,
value=timeout)
def get_hibernate_timeout(scheme=None):
'''
Get the current hibernate timeout of the given scheme
scheme (str):
The scheme to use, leave as ``None`` to use the current. Default is
``None``. This can be the GUID or the Alias for the Scheme. Known
Aliases are:
- ``SCHEME_BALANCED`` - Balanced
- ``SCHEME_MAX`` - Power saver
- ``SCHEME_MIN`` - High performance
Returns:
dict: A dictionary of both the AC and DC settings
CLI Example:
.. code-block:: bash
salt '*' powercfg.get_hibernate_timeout
scheme
The scheme to use, leave as None to use the current.
'''
return _get_powercfg_minute_values(scheme, "SUB_SLEEP", "HIBERNATEIDLE", "Hibernate after")
return _get_powercfg_minute_values(
scheme=scheme,
guid='SUB_SLEEP',
subguid='HIBERNATEIDLE',
safe_name='Hibernate after')

View File

@ -705,7 +705,7 @@ def modify(name,
win32service.SERVICE_QUERY_CONFIG)
except pywintypes.error as exc:
raise CommandExecutionError(
'Failed To Open {0}: {1}'.format(name, exc[2]))
'Failed To Open {0}: {1}'.format(name, exc))
config_info = win32service.QueryServiceConfig(handle_svc)

View File

@ -584,6 +584,8 @@ def latest_version(*names, **kwargs):
status = pkg_info.get('status', '').lower()
if status.find('not installed') > -1 or status.find('out-of-date') > -1:
ret[name] = pkg_info.get('version')
else:
ret[name] = ''
# Return a string if only one package name passed
if len(names) == 1 and len(ret):

View File

@ -203,8 +203,6 @@ import tornado.web
import tornado.gen
from tornado.concurrent import Future
# pylint: enable=import-error
import salt.utils
salt.utils.zeromq.install_zmq()
# salt imports
import salt.ext.six as six
@ -214,6 +212,7 @@ import salt.utils.event
import salt.utils.jid
import salt.utils.json
import salt.utils.yaml
import salt.utils.zeromq
from salt.utils.event import tagify
import salt.client
import salt.runner
@ -224,6 +223,7 @@ from salt.exceptions import (
EauthAuthenticationError
)
salt.utils.zeromq.install_zmq()
json = salt.utils.json.import_json()
log = logging.getLogger(__name__)
@ -933,9 +933,11 @@ class SaltAPIHandler(BaseSaltAPIHandler): # pylint: disable=W0223
# Generate jid before triggering a job to subscribe all returns from minions
chunk['jid'] = salt.utils.jid.gen_jid(self.application.opts)
# Subscribe returns from minions before firing a job
minions = set(self.ckminions.check_minions(chunk['tgt'], chunk.get('tgt_type', 'glob')))
future_minion_map = self.subscribe_minion_returns(chunk['jid'], minions)
# start listening for the event before we fire the job to avoid races
events = [
self.application.event_listener.get_event(self, tag='salt/job/'+chunk['jid']),
self.application.event_listener.get_event(self, tag='syndic/job/'+chunk['jid']),
]
f_call = self._format_call_run_job_async(chunk)
# fire a job off
@ -944,88 +946,92 @@ class SaltAPIHandler(BaseSaltAPIHandler): # pylint: disable=W0223
# if the job didn't publish, lets not wait around for nothing
# TODO: set header??
if 'jid' not in pub_data:
for future in future_minion_map:
for future in events:
try:
future.set_result(None)
except Exception:
pass
raise tornado.gen.Return('No minions matched the target. No command was sent, no jid was assigned.')
# Map of minion_id -> returned for all minions we think we need to wait on
minions = {m: False for m in pub_data['minions']}
# minimum time required for return to complete. By default no waiting, if
# we are a syndic then we must wait syndic_wait at a minimum
min_wait_time = Future()
min_wait_time.set_result(True)
# wait syndic a while to avoid missing published events
if self.application.opts['order_masters']:
yield tornado.gen.sleep(self.application.opts['syndic_wait'])
min_wait_time = tornado.gen.sleep(self.application.opts['syndic_wait'])
# To ensure job_not_running and all_return are terminated by each other, communicate using a future
is_finished = Future()
is_finished = tornado.gen.sleep(self.application.opts['gather_job_timeout'])
job_not_running_future = self.job_not_running(pub_data['jid'],
# ping until the job is not running, while doing so, if we see new minions returning
# that they are running the job, add them to the list
tornado.ioloop.IOLoop.current().spawn_callback(self.job_not_running, pub_data['jid'],
chunk['tgt'],
f_call['kwargs']['tgt_type'],
minions,
is_finished)
minion_returns_future = self.sanitize_minion_returns(future_minion_map, pub_data['minions'], is_finished)
yield job_not_running_future
raise tornado.gen.Return((yield minion_returns_future))
def subscribe_minion_returns(self, jid, minions):
# Subscribe each minion event
future_minion_map = {}
for minion in minions:
tag = tagify([jid, 'ret', minion], 'job')
minion_future = self.application.event_listener.get_event(self,
tag=tag,
matcher=EventListener.exact_matcher)
future_minion_map[minion_future] = minion
return future_minion_map
@tornado.gen.coroutine
def sanitize_minion_returns(self, future_minion_map, minions, is_finished):
'''
Return a future which will complete once all returns are completed
(according to minions), or one of the passed in "finish_chunk_ret_future" completes
'''
if minions is None:
minions = []
# Remove redundant minions
redundant_minion_futures = [future for future in future_minion_map.keys() if future_minion_map[future] not in minions]
for redundant_minion_future in redundant_minion_futures:
try:
redundant_minion_future.set_result(None)
except Exception:
pass
del future_minion_map[redundant_minion_future]
def more_todo():
'''Check if there are any more minions we are waiting on returns from
'''
return any(x is False for x in six.itervalues(minions))
# here we want to follow the behavior of LocalClient.get_iter_returns
# namely we want to wait at least syndic_wait (assuming we are a syndic)
# and that there are no more jobs running on minions. We are allowed to exit
# early if gather_job_timeout has been exceeded
chunk_ret = {}
while True:
f = yield Any(list(future_minion_map.keys()) + [is_finished])
to_wait = events+[is_finished]
if not min_wait_time.done():
to_wait += [min_wait_time]
def cancel_inflight_futures():
for event in to_wait:
if not event.done():
event.set_result(None)
f = yield Any(to_wait)
try:
# When finished entire routine, cleanup other futures and return result
if f is is_finished:
for event in future_minion_map.keys():
if not event.done():
event.set_result(None)
cancel_inflight_futures()
raise tornado.gen.Return(chunk_ret)
elif f is min_wait_time:
if not more_todo():
cancel_inflight_futures()
raise tornado.gen.Return(chunk_ret)
continue
f_result = f.result()
chunk_ret[f_result['data']['id']] = f_result['data']['return']
# if this is a start, then we need to add it to the pile
if f_result['tag'].endswith('/new'):
for minion_id in f_result['data']['minions']:
if minion_id not in minions:
minions[minion_id] = False
else:
chunk_ret[f_result['data']['id']] = f_result['data']['return']
# clear finished event future
minions[f_result['data']['id']] = True
# if there are no more minions to wait for, then we are done
if not more_todo() and min_wait_time.done():
cancel_inflight_futures()
raise tornado.gen.Return(chunk_ret)
except TimeoutException:
pass
# clear finished event future
try:
minions.remove(future_minion_map[f])
del future_minion_map[f]
except ValueError:
pass
if not minions:
if not is_finished.done():
is_finished.set_result(True)
raise tornado.gen.Return(chunk_ret)
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
def job_not_running(self, jid, tgt, tgt_type, is_finished):
def job_not_running(self, jid, tgt, tgt_type, minions, is_finished):
'''
Return a future which will complete once jid (passed in) is no longer
running on tgt
@ -1051,8 +1057,6 @@ class SaltAPIHandler(BaseSaltAPIHandler): # pylint: disable=W0223
event = f.result()
except TimeoutException:
if not minion_running:
if not is_finished.done():
is_finished.set_result(True)
raise tornado.gen.Return(True)
else:
ping_pub_data = yield self.saltclients['local'](tgt,
@ -1066,6 +1070,8 @@ class SaltAPIHandler(BaseSaltAPIHandler): # pylint: disable=W0223
# Minions can return, we want to see if the job is running...
if event['data'].get('return', {}) == {}:
continue
if event['data']['id'] not in minions:
minions[event['data']['id']] = False
minion_running = True
@tornado.gen.coroutine

View File

@ -1037,6 +1037,11 @@ class Pillar(object):
decrypt_errors = self.decrypt_pillar(pillar)
if decrypt_errors:
pillar.setdefault('_errors', []).extend(decrypt_errors)
# Reset the file_roots for the renderers
for mod_name in sys.modules:
if mod_name.startswith('salt.loaded.int.render.'):
sys.modules[mod_name].__opts__['file_roots'] = self.actual_file_roots
return pillar
def decrypt_pillar(self, pillar):

View File

@ -328,7 +328,7 @@ def chconfig(cmd, *args, **kwargs):
'''
# Strip the __pub_ keys...is there a better way to do this?
for k in kwargs:
for k in list(kwargs):
if k.startswith('__pub_'):
kwargs.pop(k)

View File

@ -200,7 +200,7 @@ def _walk(path, value, metrics, timestamp, skip):
to a float. Defaults to `False`.
'''
log.trace(
'Carbon return walking path: %s, value: %s, metrics: %s, ',
'Carbon return walking path: %s, value: %s, metrics: %s, '
'timestamp: %s', path, value, metrics, timestamp
)
if isinstance(value, collections.Mapping):

View File

@ -192,14 +192,10 @@ def returner(ret):
logoption = logoption | getattr(syslog, opt)
# Open syslog correctly based on options and tag
try:
if 'tag' in _options:
syslog.openlog(ident=_options['tag'], logoption=logoption)
else:
syslog.openlog(logoption=logoption)
except TypeError:
# Python 2.6 syslog.openlog does not accept keyword args
syslog.openlog(_options.get('tag', 'salt-minion'), logoption)
if 'tag' in _options:
syslog.openlog(ident=salt.utils.stringutils.to_str(_options['tag']), logoption=logoption)
else:
syslog.openlog(logoption=logoption)
# Send log of given level and facility
syslog.syslog(facility | level, salt.utils.json.dumps(ret))

View File

@ -164,7 +164,7 @@ def action(func=None,
instances,
provider,
instance,
**salt.utils.args.clean_kwargs(**kwargs)
salt.utils.args.clean_kwargs(**kwargs)
)
except SaltCloudConfigError as err:
log.error(err)

View File

@ -153,7 +153,7 @@ def lookup_jid(jid,
try:
# Check if the return data has an 'out' key. We'll use that as the
# outputter in the absence of one being passed on the CLI.
outputter = data[next(iter(data))].get('out')
outputter = returns[next(iter(returns))].get('out')
except (StopIteration, AttributeError):
outputter = None

View File

@ -72,7 +72,7 @@ def generate_token(minion_id, signature, impersonated_by_master=False):
payload = {
'policies': _get_policies(minion_id, config),
'num_uses': 1,
'metadata': audit_data
'meta': audit_data
}
if payload['policies'] == []:

View File

@ -172,7 +172,9 @@ def init(
start=True,
disk='default',
saltenv='base',
enable_vnc=False):
enable_vnc=False,
seed_cmd='seed.apply',
enable_qcow=False):
'''
This routine is used to create a new virtual machine. This routines takes
a number of options to determine what the newly created virtual machine
@ -194,14 +196,14 @@ def init(
on the salt fileserver, but http, https and ftp can also be used.
hypervisor
The hypervisor to use for the new virtual machine. Default is 'kvm'.
The hypervisor to use for the new virtual machine. Default is `kvm`.
host
The host to use for the new virtual machine, if this is omitted
Salt will automatically detect what host to use.
seed
Set to False to prevent Salt from seeding the new virtual machine.
Set to `False` to prevent Salt from seeding the new virtual machine.
nic
The nic profile to use, defaults to the "default" nic profile which
@ -217,6 +219,17 @@ def init(
saltenv
The Salt environment to use
enable_vnc
Whether a VNC screen is attached to resulting VM. Default is `False`.
seed_cmd
If seed is `True`, use this execution module function to seed new VM.
Default is `seed.apply`.
enable_qcow
Clone disk image as a copy-on-write qcow2 image, using downloaded
`image` as backing file.
'''
__jid_event__.fire_event({'message': 'Searching for hosts'}, 'progress')
data = query(host, quiet=True)
@ -257,25 +270,29 @@ def init(
)
try:
cmd_ret = client.cmd_iter(
host,
'virt.init',
[
name,
cpu,
mem,
image,
nic,
hypervisor,
start,
disk,
saltenv,
seed,
install,
pub_key,
priv_key,
enable_vnc,
],
timeout=600)
host,
'virt.init',
[
name,
cpu,
mem
],
timeout=600,
kwarg={
'image': image,
'nic': nic,
'hypervisor': hypervisor,
'start': start,
'disk': disk,
'saltenv': saltenv,
'seed': seed,
'install': install,
'pub_key': pub_key,
'priv_key': priv_key,
'seed_cmd': seed_cmd,
'enable_vnc': enable_vnc,
'enable_qcow': enable_qcow,
})
except SaltClientError as client_error:
# Fall through to ret error handling below
print(client_error)

Some files were not shown because too many files have changed in this diff Show More