mirror of
https://github.com/valitydev/salt.git
synced 2024-11-06 16:45:27 +00:00
Merge branch '2018.3' into jid
This commit is contained in:
commit
084e7f0ef4
27
.ci/docs
27
.ci/docs
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
43
.ci/lint
43
.ci/lint
@ -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
1
.github/CODEOWNERS
vendored
@ -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
|
||||
|
||||
|
@ -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:
|
||||
|
8
doc/_themes/saltstack2/layout.html
vendored
8
doc/_themes/saltstack2/layout.html
vendored
@ -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>
|
||||
|
BIN
doc/_themes/saltstack2/static/images/DOCBANNER.jpg
vendored
BIN
doc/_themes/saltstack2/static/images/DOCBANNER.jpg
vendored
Binary file not shown.
Before Width: | Height: | Size: 240 KiB After Width: | Height: | Size: 589 KiB |
1
doc/_themes/saltstack2/static/images/SaltStack_white.svg
vendored
Normal file
1
doc/_themes/saltstack2/static/images/SaltStack_white.svg
vendored
Normal 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 |
@ -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
|
||||
|
23
doc/faq.rst
23
doc/faq.rst
@ -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
|
||||
|
@ -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
|
||||
.
|
||||
|
@ -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
|
||||
.
|
||||
|
@ -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
|
||||
.
|
||||
|
@ -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
|
||||
.
|
||||
|
@ -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
|
||||
.
|
||||
|
@ -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
|
||||
.
|
||||
|
@ -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
|
||||
.
|
||||
|
@ -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
|
||||
.
|
||||
|
@ -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
|
||||
.
|
||||
|
@ -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
|
||||
.
|
||||
|
@ -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
|
||||
.
|
||||
|
@ -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
|
||||
.
|
||||
|
@ -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
|
||||
.
|
||||
|
7897
doc/man/salt.7
7897
doc/man/salt.7
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
.
|
||||
|
@ -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``
|
||||
|
@ -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``
|
||||
-----------
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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!",
|
||||
}
|
||||
)
|
||||
)
|
||||
|
@ -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/
|
||||
|
||||
|
@ -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
|
||||
==================================
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
==================================
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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 }}
|
||||
|
@ -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::
|
||||
|
||||
|
@ -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>`
|
||||
|
@ -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 ----------------------------------------------------------------------
|
||||
|
@ -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"
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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 |
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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'
|
||||
|
@ -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)
|
||||
|
@ -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': [],
|
||||
|
8
salt/cache/mysql_cache.py
vendored
8
salt/cache/mysql_cache.py
vendored
@ -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):
|
||||
|
@ -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
|
||||
)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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',
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
)
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
@ -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).
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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():
|
||||
|
@ -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')
|
||||
|
@ -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):
|
||||
|
@ -37,6 +37,7 @@ def __virtual__():
|
||||
'Devuan',
|
||||
'Arch',
|
||||
'Arch ARM',
|
||||
'Manjaro',
|
||||
'ALT',
|
||||
'SUSE Enterprise Server',
|
||||
'SUSE',
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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(',')
|
||||
|
||||
|
@ -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}
|
||||
|
||||
|
@ -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())
|
||||
|
@ -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
|
||||
|
@ -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')
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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))
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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'] == []:
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user