mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Merge branch '2017.7' into fix-bug-vault-empty-policy
This commit is contained in:
commit
5be463bb46
344 changed files with 12583 additions and 4173 deletions
60
.github/CODEOWNERS
vendored
Normal file
60
.github/CODEOWNERS
vendored
Normal file
|
@ -0,0 +1,60 @@
|
|||
# SALTSTACK CODE OWNERS
|
||||
|
||||
# See https://help.github.com/articles/about-codeowners/
|
||||
# for more info about CODEOWNERS file
|
||||
|
||||
# Lines starting with '#' are comments.
|
||||
# Each line is a file pattern followed by one or more owners.
|
||||
|
||||
# See https://help.github.com/articles/about-codeowners/
|
||||
# for more info about the CODEOWNERS file
|
||||
|
||||
# Team Boto
|
||||
salt/**/*boto* @saltstack/team-boto
|
||||
|
||||
# Team Core
|
||||
salt/auth/ @saltstack/team-core
|
||||
salt/cache/ @saltstack/team-core
|
||||
salt/cli/ @saltstack/team-core
|
||||
salt/client/* @saltstack/team-core
|
||||
salt/config/* @saltstack/team-core
|
||||
salt/daemons/ @saltstack/team-core
|
||||
salt/pillar/ @saltstack/team-core
|
||||
salt/loader.py @saltstack/team-core
|
||||
salt/payload.py @saltstack/team-core
|
||||
salt/**/master* @saltstack/team-core
|
||||
salt/**/minion* @saltstack/team-core
|
||||
|
||||
# Team Cloud
|
||||
salt/cloud/ @saltstack/team-cloud
|
||||
salt/utils/openstack/ @saltstack/team-cloud
|
||||
salt/utils/aws.py @saltstack/team-cloud
|
||||
salt/**/*cloud* @saltstack/team-cloud
|
||||
|
||||
# Team NetAPI
|
||||
salt/cli/api.py @saltstack/team-netapi
|
||||
salt/client/netapi.py @saltstack/team-netapi
|
||||
salt/netapi/ @saltstack/team-netapi
|
||||
|
||||
# Team Network
|
||||
salt/proxy/ @saltstack/team-proxy
|
||||
|
||||
# Team SPM
|
||||
salt/cli/spm.py @saltstack/team-spm
|
||||
salt/spm/ @saltstack/team-spm
|
||||
|
||||
# Team SSH
|
||||
salt/cli/ssh.py @saltstack/team-ssh
|
||||
salt/client/ssh/ @saltstack/team-ssh
|
||||
salt/runners/ssh.py @saltstack/team-ssh
|
||||
salt/**/thin.py @saltstack/team-ssh
|
||||
|
||||
# Team State
|
||||
salt/state.py @saltstack/team-state
|
||||
|
||||
# Team Transport
|
||||
salt/transport/ @saltstack/team-transport
|
||||
salt/utils/zeromq.py @saltstack/team-transport
|
||||
|
||||
# Team Windows
|
||||
salt/**/*win* @saltstack/team-windows
|
6
.github/PULL_REQUEST_TEMPLATE.md
vendored
6
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
@ -12,4 +12,10 @@ Remove this section if not relevant
|
|||
|
||||
Yes/No
|
||||
|
||||
### Commits signed with GPG?
|
||||
|
||||
Yes/No
|
||||
|
||||
Please review [Salt's Contributing Guide](https://docs.saltstack.com/en/latest/topics/development/contributing.html) for best practices.
|
||||
|
||||
See GitHub's [page on GPG signing](https://help.github.com/articles/signing-commits-using-gpg/) for more information about signing commits with GPG.
|
||||
|
|
8
.gitignore
vendored
8
.gitignore
vendored
|
@ -88,3 +88,11 @@ tests/integration/cloud/providers/logs
|
|||
|
||||
# Private keys from the integration tests
|
||||
tests/integration/cloud/providers/pki/minions
|
||||
|
||||
# Ignore tox virtualenvs
|
||||
/.tox/
|
||||
|
||||
# Ignore kitchen stuff
|
||||
.kitchen
|
||||
.bundle
|
||||
Gemfile.lock
|
||||
|
|
193
.kitchen.yml
Normal file
193
.kitchen.yml
Normal file
|
@ -0,0 +1,193 @@
|
|||
---
|
||||
<% vagrant = system('which vagrant 2>/dev/null >/dev/null') %>
|
||||
<% version = '2017.7.1' %>
|
||||
<% platformsfile = ENV['SALT_KITCHEN_PLATFORMS'] || '.kitchen/platforms.yml' %>
|
||||
<% driverfile = ENV['SALT_KITCHEN_DRIVER'] || '.kitchen/driver.yml' %>
|
||||
|
||||
<% if File.exists?(driverfile) %>
|
||||
<%= ERB.new(File.read(driverfile)).result %>
|
||||
<% else %>
|
||||
driver:
|
||||
name: docker
|
||||
use_sudo: false
|
||||
hostname: salt
|
||||
privileged: true
|
||||
username: root
|
||||
volume:
|
||||
- /var/run/docker.sock:/docker.sock
|
||||
cap_add:
|
||||
- sys_admin
|
||||
disable_upstart: false
|
||||
provision_command:
|
||||
- echo 'L /run/docker.sock - - - - /docker.sock' > /etc/tmpfiles.d/docker.conf
|
||||
transport:
|
||||
name: sftp
|
||||
<% end %>
|
||||
|
||||
sudo: false
|
||||
provisioner:
|
||||
name: salt_solo
|
||||
salt_install: bootstrap
|
||||
salt_version: latest
|
||||
salt_bootstrap_url: https://bootstrap.saltstack.com
|
||||
salt_bootstrap_options: -X stable <%= version %>
|
||||
log_level: info
|
||||
require_chef: false
|
||||
remote_states:
|
||||
name: git://github.com/saltstack/salt-jenkins.git
|
||||
branch: 2017.7
|
||||
repo: git
|
||||
testingdir: /testing
|
||||
salt_copy_filter:
|
||||
- .bundle
|
||||
- .git
|
||||
- .gitignore
|
||||
- .kitchen
|
||||
- .kitchen.yml
|
||||
- Gemfile
|
||||
- Gemfile.lock
|
||||
- README.rst
|
||||
- .travis.yml
|
||||
state_top:
|
||||
base:
|
||||
"*":
|
||||
- git.salt
|
||||
- kitchen
|
||||
<% if File.exists?(platformsfile) %>
|
||||
<%= ERB.new(File.read(platformsfile)).result %>
|
||||
<% else %>
|
||||
platforms:
|
||||
- name: fedora
|
||||
driver_config:
|
||||
image: fedora:latest
|
||||
run_command: /usr/lib/systemd/systemd
|
||||
provisioner:
|
||||
salt_bootstrap_options: -X git v<%= version %> >/dev/null
|
||||
- name: centos-7
|
||||
driver_config:
|
||||
run_command: /usr/lib/systemd/systemd
|
||||
- name: centos-6
|
||||
driver_config:
|
||||
run_command: /sbin/init
|
||||
provision_command:
|
||||
- yum install -y upstart
|
||||
provisioner:
|
||||
salt_bootstrap_options: -P -y -x python2.7 -X git v<%= version %> >/dev/null
|
||||
- name: ubuntu-rolling
|
||||
driver_config:
|
||||
image: ubuntu:rolling
|
||||
run_command: /lib/systemd/systemd
|
||||
provisioner:
|
||||
salt_bootstrap_url: https://raw.githubusercontent.com/saltstack/salt-bootstrap/develop/bootstrap-salt.sh
|
||||
- name: ubuntu-16.04
|
||||
driver_config:
|
||||
run_command: /lib/systemd/systemd
|
||||
- name: ubuntu-14.04
|
||||
driver_config:
|
||||
run_command: /sbin/init
|
||||
provision_command:
|
||||
- rm -f /sbin/initctl
|
||||
- dpkg-divert --local --rename --remove /sbin/initctl
|
||||
- name: debian-8
|
||||
driver_config:
|
||||
run_command: /lib/systemd/systemd
|
||||
provision_command:
|
||||
- apt-get install -y dbus
|
||||
- echo 'L /run/docker.sock - - - - /docker.sock' > /etc/tmpfiles.d/docker.conf
|
||||
- name: debian-9
|
||||
driver_config:
|
||||
run_command: /lib/systemd/systemd
|
||||
- name: arch
|
||||
driver_config:
|
||||
image: base/archlinux
|
||||
run_command: /usr/lib/systemd/systemd
|
||||
provision_command:
|
||||
- pacman -Syu --noconfirm systemd
|
||||
- systemctl enable sshd
|
||||
- echo 'L /run/docker.sock - - - - /docker.sock' > /etc/tmpfiles.d/docker.conf
|
||||
provisioner:
|
||||
salt_bootstrap_options: -X git v<%= version %> >/dev/null
|
||||
- name: opensuse
|
||||
driver_config:
|
||||
run_command: /usr/lib/systemd/systemd
|
||||
provision_command:
|
||||
- systemctl enable sshd.service
|
||||
- echo 'L /run/docker.sock - - - - /docker.sock' > /etc/tmpfiles.d/docker.conf
|
||||
provisioner:
|
||||
salt_bootstrap_options: -X git v<%= version %> >/dev/null
|
||||
<% if vagrant != false %>
|
||||
- name: windows-2012r2
|
||||
driver:
|
||||
box: mwrock/Windows2012R2
|
||||
communicator: winrm
|
||||
name: vagrant
|
||||
gui: true
|
||||
username: administrator
|
||||
password: Pass@word1
|
||||
provisioner:
|
||||
init_environment: |
|
||||
Clear-Host
|
||||
$AddedLocation ="c:\salt"
|
||||
$Reg = "Registry::HKLM\System\CurrentControlSet\Control\Session Manager\Environment"
|
||||
$OldPath = (Get-ItemProperty -Path "$Reg" -Name PATH).Path
|
||||
$NewPath= $OldPath + ’;’ + $AddedLocation
|
||||
Set-ItemProperty -Path "$Reg" -Name PATH –Value $NewPath
|
||||
salt_bootstrap_url: https://raw.githubusercontent.com/saltstack/salt-bootstrap/develop/bootstrap-salt.ps1
|
||||
salt_bootstrap_options: ''
|
||||
- name: windows-2016
|
||||
driver:
|
||||
box: mwrock/Windows2016
|
||||
communicator: winrm
|
||||
name: vagrant
|
||||
username: Vagrant
|
||||
password: vagrant
|
||||
gui: true
|
||||
provisioner:
|
||||
init_environment: |
|
||||
Clear-Host
|
||||
$AddedLocation ="c:\salt;c:\salt\bin\Scripts"
|
||||
$Reg = "Registry::HKLM\System\CurrentControlSet\Control\Session Manager\Environment"
|
||||
$OldPath = (Get-ItemProperty -Path "$Reg" -Name PATH).Path
|
||||
$NewPath= $OldPath + ’;’ + $AddedLocation
|
||||
Set-ItemProperty -Path "$Reg" -Name PATH –Value $NewPath
|
||||
salt_bootstrap_url: https://raw.githubusercontent.com/saltstack/salt-bootstrap/develop/bootstrap-salt.ps1
|
||||
salt_bootstrap_options: ''
|
||||
<% end %>
|
||||
<% end %>
|
||||
suites:
|
||||
- name: py2
|
||||
provisioner:
|
||||
pillars:
|
||||
top.sls:
|
||||
base:
|
||||
"*":
|
||||
- jenkins
|
||||
jenkins.sls:
|
||||
testing_dir: /tmp/kitchen/testing
|
||||
clone_repo: false
|
||||
salttesting_namespec: salttesting==2017.6.1
|
||||
- name: py3
|
||||
excludes:
|
||||
- centos-6
|
||||
- ubuntu-14.04
|
||||
provisioner:
|
||||
pillars:
|
||||
top.sls:
|
||||
base:
|
||||
"*":
|
||||
- jenkins
|
||||
jenkins.sls:
|
||||
testing_dir: /tmp/kitchen/testing
|
||||
clone_repo: false
|
||||
py3: true
|
||||
salttesting_namespec: salttesting==2017.6.1
|
||||
verifier:
|
||||
name: shell
|
||||
remote_exec: true
|
||||
sudo: false
|
||||
live_stream: {}
|
||||
<% if ENV['TESTOPTS'].nil? %>
|
||||
command: '$(kitchen) /tmp/kitchen/testing/tests/runtests.py --run-destructive --sysinfo --transport=zeromq --output-columns=80 --ssh --coverage-xml=/tmp/coverage.xml --xml=/tmp/xml-unittests-output'
|
||||
<% else %>
|
||||
command: '$(kitchen) /tmp/kitchen/testing/tests/runtests.py --run-destructive --output-columns 80 <%= ENV["TESTOPTS"] %>'
|
||||
<% end %>
|
14
.mention-bot
14
.mention-bot
|
@ -1,5 +1,17 @@
|
|||
{
|
||||
"alwaysNotifyForPaths": [
|
||||
{
|
||||
"name": "ryan-lane",
|
||||
"files": ["salt/**/*boto*.py"],
|
||||
"skipTeamPrs": false
|
||||
},
|
||||
{
|
||||
"name": "tkwilliams",
|
||||
"files": ["salt/**/*boto*.py"],
|
||||
"skipTeamPrs": false
|
||||
}
|
||||
],
|
||||
"skipTitle": "Merge forward",
|
||||
"userBlacklist": ["cvrebert", "markusgattol", "olliewalsh"]
|
||||
"userBlacklist": ["cvrebert", "markusgattol", "olliewalsh", "basepi"]
|
||||
}
|
||||
|
||||
|
|
|
@ -258,8 +258,8 @@ ignore-imports=no
|
|||
|
||||
|
||||
[BASIC]
|
||||
# Required attributes for module, separated by a comma
|
||||
required-attributes=
|
||||
# Required attributes for module, separated by a comma (will be removed in Pylint 2.0)
|
||||
#required-attributes=
|
||||
|
||||
# List of builtins function names that should not be used, separated by a comma
|
||||
bad-functions=map,filter,apply,input
|
||||
|
@ -365,7 +365,8 @@ spelling-store-unknown-words=no
|
|||
[CLASSES]
|
||||
# List of interface methods to ignore, separated by a comma. This is used for
|
||||
# instance to not check methods defines in Zope's Interface base class.
|
||||
ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
|
||||
# Will be removed in Pylint 2.0
|
||||
#ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
|
||||
|
||||
# List of method names used to declare (i.e. assign) instance attributes.
|
||||
defining-attr-methods=__init__,__new__,setUp
|
||||
|
|
24
Gemfile
Normal file
24
Gemfile
Normal file
|
@ -0,0 +1,24 @@
|
|||
# This file is only used for running the test suite with kitchen-salt.
|
||||
|
||||
source 'https://rubygems.org'
|
||||
|
||||
gem 'test-kitchen'
|
||||
gem 'kitchen-salt', :git => 'https://github.com/saltstack/kitchen-salt.git'
|
||||
gem 'kitchen-sync'
|
||||
gem 'git'
|
||||
|
||||
group :docker do
|
||||
gem 'kitchen-docker', :git => 'https://github.com/test-kitchen/kitchen-docker.git'
|
||||
end
|
||||
|
||||
group :opennebula do
|
||||
gem 'kitchen-opennebula', :git => 'https://github.com/gtmanfred/kitchen-opennebula.git'
|
||||
gem 'xmlrpc'
|
||||
end
|
||||
|
||||
group :windows do
|
||||
gem 'vagrant-wrapper'
|
||||
gem 'kitchen-vagrant'
|
||||
gem 'winrm', '~>2.0'
|
||||
gem 'winrm-fs', '~>1.0'
|
||||
end
|
|
@ -67,8 +67,8 @@ Engage SaltStack
|
|||
|
||||
`SaltConf`_, **User Groups and Meetups** - SaltStack has a vibrant and `global
|
||||
community`_ of customers, users, developers and enthusiasts. Connect with other
|
||||
Salted folks in your area of the world, or join `SaltConf16`_, the SaltStack
|
||||
annual user conference, April 19-21 in Salt Lake City. Please let us know if
|
||||
Salted folks in your area of the world, or join `SaltConf18`_, the SaltStack
|
||||
annual user conference, September 10-14 in Salt Lake City. Please let us know if
|
||||
you would like to start a user group or if we should add your existing
|
||||
SaltStack user group to this list by emailing: info@saltstack.com
|
||||
|
||||
|
@ -91,7 +91,7 @@ services`_ offerings.
|
|||
|
||||
.. _SaltConf: http://www.youtube.com/user/saltstack
|
||||
.. _global community: http://www.meetup.com/pro/saltstack/
|
||||
.. _SaltConf16: http://saltconf.com/
|
||||
.. _SaltConf18: http://saltconf.com/
|
||||
.. _SaltStack education offerings: http://saltstack.com/training/
|
||||
.. _SaltStack Certified Engineer (SSCE): http://saltstack.com/certification/
|
||||
.. _SaltStack professional services: http://saltstack.com/services/
|
||||
|
|
|
@ -373,7 +373,7 @@
|
|||
# interface: eth0
|
||||
# cidr: '10.0.0.0/8'
|
||||
|
||||
# The number of seconds a mine update runs.
|
||||
# The number of minutes between mine updates.
|
||||
#mine_interval: 60
|
||||
|
||||
# Windows platforms lack posix IPC and must rely on slower TCP based inter-
|
||||
|
|
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: 790 KiB After Width: | Height: | Size: 438 KiB |
|
@ -245,8 +245,8 @@ on_saltstack = 'SALT_ON_SALTSTACK' in os.environ
|
|||
project = 'Salt'
|
||||
|
||||
version = salt.version.__version__
|
||||
latest_release = '2017.7.1' # latest release
|
||||
previous_release = '2016.11.7' # latest release from previous branch
|
||||
latest_release = '2017.7.2' # latest release
|
||||
previous_release = '2016.11.8' # latest release from previous branch
|
||||
previous_release_dir = '2016.11' # path on web server for previous branch
|
||||
next_release = '' # next release
|
||||
next_release_dir = '' # path on web server for next release branch
|
||||
|
|
|
@ -10795,6 +10795,7 @@ cmd_whitelist_glob:
|
|||
.UNINDENT
|
||||
.UNINDENT
|
||||
.SS Thread Settings
|
||||
.SS \fBmultiprocessing\fP
|
||||
.sp
|
||||
Default: \fBTrue\fP
|
||||
.sp
|
||||
|
|
|
@ -19,14 +19,18 @@ Salt SSH allows for salt routines to be executed using only SSH for transport
|
|||
Options
|
||||
=======
|
||||
|
||||
.. program:: salt-ssh
|
||||
|
||||
.. include:: _includes/common-options.rst
|
||||
|
||||
.. option:: --hard-crash
|
||||
|
||||
Raise any original exception rather than exiting gracefully. Default: False.
|
||||
|
||||
.. option:: -r, --raw, --raw-shell
|
||||
|
||||
Execute a raw shell command.
|
||||
|
||||
.. option:: --priv
|
||||
|
||||
Specify the SSH private key file to be used for authentication.
|
||||
|
||||
.. option:: --roster
|
||||
|
||||
Define which roster system to use, this defines if a database backend,
|
||||
|
@ -53,38 +57,117 @@ Options
|
|||
the more running process the faster communication should be, default
|
||||
is 25.
|
||||
|
||||
.. option:: --extra-filerefs=EXTRA_FILEREFS
|
||||
|
||||
Pass in extra files to include in the state tarball.
|
||||
|
||||
.. option:: --min-extra-modules=MIN_EXTRA_MODS
|
||||
|
||||
One or comma-separated list of extra Python modulesto be included
|
||||
into Minimal Salt.
|
||||
|
||||
.. option:: --thin-extra-modules=THIN_EXTRA_MODS
|
||||
|
||||
One or comma-separated list of extra Python modulesto be included
|
||||
into Thin Salt.
|
||||
|
||||
.. option:: -v, --verbose
|
||||
|
||||
Turn on command verbosity, display jid.
|
||||
|
||||
.. option:: -s, --static
|
||||
|
||||
Return the data from minions as a group after they all return.
|
||||
|
||||
.. option:: -w, --wipe
|
||||
|
||||
Remove the deployment of the salt files when done executing.
|
||||
|
||||
.. option:: -W, --rand-thin-dir
|
||||
|
||||
Select a random temp dir to deploy on the remote system. The dir
|
||||
will be cleaned after the execution.
|
||||
|
||||
.. option:: -t, --regen-thin, --thin
|
||||
|
||||
Trigger a thin tarball regeneration. This is needed if custom
|
||||
grains/modules/states have been added or updated.
|
||||
|
||||
.. option:: --python2-bin=PYTHON2_BIN
|
||||
|
||||
Path to a python2 binary which has salt installed.
|
||||
|
||||
.. option:: --python3-bin=PYTHON3_BIN
|
||||
|
||||
Path to a python3 binary which has salt installed.
|
||||
|
||||
.. option:: --jid=JID
|
||||
|
||||
Pass a JID to be used instead of generating one.
|
||||
|
||||
Authentication Options
|
||||
----------------------
|
||||
|
||||
.. option:: --priv=SSH_PRIV
|
||||
|
||||
Specify the SSH private key file to be used for authentication.
|
||||
|
||||
.. option:: -i, --ignore-host-keys
|
||||
|
||||
Disables StrictHostKeyChecking to relax acceptance of new and unknown
|
||||
host keys.
|
||||
By default ssh host keys are honored and connections will ask for
|
||||
approval. Use this option to disable StrictHostKeyChecking.
|
||||
|
||||
.. option:: --no-host-keys
|
||||
|
||||
Fully ignores ssh host keys which by default are honored and connections
|
||||
would ask for approval. Useful if the host key of a remote server has
|
||||
would ask for approval. Useful if the host key of a remote server has
|
||||
changed and would still error with --ignore-host-keys.
|
||||
|
||||
.. option:: --user=SSH_USER
|
||||
|
||||
Set the default user to attempt to use when authenticating.
|
||||
|
||||
.. option:: --passwd
|
||||
|
||||
Set the default password to attempt to use when authenticating.
|
||||
|
||||
.. option:: --askpass
|
||||
|
||||
Interactively ask for the SSH password with no echo - avoids password
|
||||
in process args and stored in history.
|
||||
|
||||
.. option:: --key-deploy
|
||||
|
||||
Set this flag to attempt to deploy the authorized ssh key with all
|
||||
minions. This combined with --passwd can make initial deployment of keys
|
||||
very fast and easy.
|
||||
|
||||
.. program:: salt
|
||||
.. option:: --identities-only
|
||||
|
||||
.. include:: _includes/common-options.rst
|
||||
Use the only authentication identity files configured in the ssh_config
|
||||
files. See IdentitiesOnly flag in man ssh_config.
|
||||
|
||||
.. include:: _includes/target-selection-ssh.rst
|
||||
.. option:: --sudo
|
||||
|
||||
Run command via sudo.
|
||||
|
||||
Scan Roster Options
|
||||
-------------------
|
||||
|
||||
.. option:: --scan-ports=SSH_SCAN_PORTS
|
||||
|
||||
Comma-separated list of ports to scan in the scan roster.
|
||||
|
||||
.. option:: --scan-timeout=SSH_SCAN_TIMEOUT
|
||||
|
||||
Scanning socket timeout for the scan roster.
|
||||
|
||||
.. include:: _includes/logging-options.rst
|
||||
.. |logfile| replace:: /var/log/salt/ssh
|
||||
.. |loglevel| replace:: ``warning``
|
||||
|
||||
.. include:: _includes/target-selection-ssh.rst
|
||||
|
||||
.. include:: _includes/output-options.rst
|
||||
|
||||
|
||||
|
|
|
@ -225,15 +225,16 @@ enclosing brackets ``[`` and ``]``:
|
|||
|
||||
Default: ``{}``
|
||||
|
||||
This can be used to control logging levels more specifically. The example sets
|
||||
the main salt library at the 'warning' level, but sets ``salt.modules`` to log
|
||||
at the ``debug`` level:
|
||||
This can be used to control logging levels more specifically, based on log call name. The example sets
|
||||
the main salt library at the 'warning' level, sets ``salt.modules`` to log
|
||||
at the ``debug`` level, and sets a custom module to the ``all`` level:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
log_granular_levels:
|
||||
'salt': 'warning'
|
||||
'salt.modules': 'debug'
|
||||
'salt.loader.saltmaster.ext.module.custom_module': 'all'
|
||||
|
||||
External Logging Handlers
|
||||
-------------------------
|
||||
|
|
|
@ -303,6 +303,20 @@ option on the Salt master.
|
|||
|
||||
master_port: 4506
|
||||
|
||||
.. conf_minion:: publish_port
|
||||
|
||||
``publish_port``
|
||||
---------------
|
||||
|
||||
Default: ``4505``
|
||||
|
||||
The port of the master publish server, this needs to coincide with the publish_port
|
||||
option on the Salt master.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
publish_port: 4505
|
||||
|
||||
.. conf_minion:: user
|
||||
|
||||
``user``
|
||||
|
@ -674,7 +688,7 @@ Note these can be defined in the pillar for a minion as well.
|
|||
|
||||
Default: ``60``
|
||||
|
||||
The number of seconds a mine update runs.
|
||||
The number of minutes between mine updates.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
|
@ -1164,7 +1178,7 @@ be able to execute a certain module. The ``sys`` module is built into the minion
|
|||
and cannot be disabled.
|
||||
|
||||
This setting can also tune the minion. Because all modules are loaded into system
|
||||
memory, disabling modules will lover the minion's memory footprint.
|
||||
memory, disabling modules will lower the minion's memory footprint.
|
||||
|
||||
Modules should be specified according to their file name on the system and not by
|
||||
their virtual name. For example, to disable ``cmd``, use the string ``cmdmod`` which
|
||||
|
@ -2337,11 +2351,14 @@ Thread Settings
|
|||
|
||||
.. conf_minion:: multiprocessing
|
||||
|
||||
``multiprocessing``
|
||||
-------
|
||||
|
||||
Default: ``True``
|
||||
|
||||
If `multiprocessing` is enabled when a minion receives a
|
||||
If ``multiprocessing`` is enabled when a minion receives a
|
||||
publication a new process is spawned and the command is executed therein.
|
||||
Conversely, if `multiprocessing` is disabled the new publication will be run
|
||||
Conversely, if ``multiprocessing`` is disabled the new publication will be run
|
||||
executed in a thread.
|
||||
|
||||
|
||||
|
|
|
@ -118,3 +118,53 @@ has to be closed after every command.
|
|||
.. code-block:: yaml
|
||||
|
||||
proxy_always_alive: False
|
||||
|
||||
``proxy_merge_pillar_in_opts``
|
||||
------------------------------
|
||||
|
||||
.. versionadded:: 2017.7.3
|
||||
|
||||
Default: ``False``.
|
||||
|
||||
Wheter the pillar data to be merged into the proxy configuration options.
|
||||
As multiple proxies can run on the same server, we may need different
|
||||
configuration options for each, while there's one single configuration file.
|
||||
The solution is merging the pillar data of each proxy minion into the opts.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
proxy_merge_pillar_in_opts: True
|
||||
|
||||
``proxy_deep_merge_pillar_in_opts``
|
||||
-----------------------------------
|
||||
|
||||
.. versionadded:: 2017.7.3
|
||||
|
||||
Default: ``False``.
|
||||
|
||||
Deep merge of pillar data into configuration opts.
|
||||
This option is evaluated only when :conf_proxy:`proxy_merge_pillar_in_opts` is
|
||||
enabled.
|
||||
|
||||
``proxy_merge_pillar_in_opts_strategy``
|
||||
---------------------------------------
|
||||
|
||||
.. versionadded:: 2017.7.3
|
||||
|
||||
Default: ``smart``.
|
||||
|
||||
The strategy used when merging pillar configuration into opts.
|
||||
This option is evaluated only when :conf_proxy:`proxy_merge_pillar_in_opts` is
|
||||
enabled.
|
||||
|
||||
``proxy_mines_pillar``
|
||||
----------------------
|
||||
|
||||
.. versionadded:: 2017.7.3
|
||||
|
||||
Default: ``True``.
|
||||
|
||||
Allow enabling mine details using pillar data. This evaluates the mine
|
||||
configuration under the pillar, for the following regular minion options that
|
||||
are also equally available on the proxy minion: :conf_minion:`mine_interval`,
|
||||
and :conf_minion:`mine_functions`.
|
||||
|
|
|
@ -376,6 +376,22 @@ The above example will force the minion to use the :py:mod:`systemd
|
|||
|
||||
.. __: https://github.com/saltstack/salt/issues/new
|
||||
|
||||
Logging Restrictions
|
||||
--------------------
|
||||
|
||||
As a rule, logging should not be done anywhere in a Salt module before it is
|
||||
loaded. This rule apples to all code that would run before the ``__virtual__()``
|
||||
function, as well as the code within the ``__virtual__()`` function itself.
|
||||
|
||||
If logging statements are made before the virtual function determines if
|
||||
the module should be loaded, then those logging statements will be called
|
||||
repeatedly. This clutters up log files unnecessarily.
|
||||
|
||||
Exceptions may be considered for logging statements made at the ``trace`` level.
|
||||
However, it is better to provide the necessary information by another means.
|
||||
One method is to :ref:`return error information <modules-error-info>` in the
|
||||
``__virtual__()`` function.
|
||||
|
||||
.. _modules-virtual-name:
|
||||
|
||||
``__virtualname__``
|
||||
|
|
|
@ -25,6 +25,9 @@ configuration:
|
|||
- web*:
|
||||
- test.*
|
||||
- pkg.*
|
||||
# Allow managers to use saltutil module functions
|
||||
manager_.*:
|
||||
- saltutil.*
|
||||
|
||||
Permission Issues
|
||||
-----------------
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
salt.runners.auth module
|
||||
========================
|
||||
salt.runners.auth
|
||||
=================
|
||||
|
||||
.. automodule:: salt.runners.auth
|
||||
:members:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
salt.runners.digicertapi module
|
||||
===============================
|
||||
salt.runners.digicertapi
|
||||
========================
|
||||
|
||||
.. automodule:: salt.runners.digicertapi
|
||||
:members:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
salt.runners.event module
|
||||
=========================
|
||||
salt.runners.event
|
||||
==================
|
||||
|
||||
.. automodule:: salt.runners.event
|
||||
:members:
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
salt.runners.mattermost module
|
||||
==============================
|
||||
salt.runners.mattermost
|
||||
=======================
|
||||
|
||||
**Note for 2017.7 releases!**
|
||||
|
||||
Due to the `salt.runners.config <https://github.com/saltstack/salt/blob/develop/salt/runners/config.py>`_ module not being available in this release series, importing the `salt.runners.config <https://github.com/saltstack/salt/blob/develop/salt/runners/config.py>`_ module from the develop branch is required to make this module work.
|
||||
|
||||
Ref: `Mattermost runner failing to retrieve config values due to unavailable config runner #43479 <https://github.com/saltstack/salt/issues/43479>`_
|
||||
|
||||
.. automodule:: salt.runners.mattermost
|
||||
:members:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
salt.runners.smartos_vmadm module
|
||||
=================================
|
||||
salt.runners.smartos_vmadm
|
||||
==========================
|
||||
|
||||
.. automodule:: salt.runners.smartos_vmadm
|
||||
:members:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
salt.runners.vault module
|
||||
=========================
|
||||
salt.runners.vault
|
||||
==================
|
||||
|
||||
.. automodule:: salt.runners.vault
|
||||
:members:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
salt.runners.venafiapi module
|
||||
=============================
|
||||
salt.runners.venafiapi
|
||||
======================
|
||||
|
||||
.. automodule:: salt.runners.venafiapi
|
||||
:members:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
salt.runners.vistara module
|
||||
===========================
|
||||
salt.runners.vistara
|
||||
====================
|
||||
|
||||
.. automodule:: salt.runners.vistara
|
||||
:members:
|
||||
|
|
|
@ -6,7 +6,7 @@ Introduced in Salt version ``2017.7.0`` it is now possible to run select states
|
|||
in parallel. This is accomplished very easily by adding the ``parallel: True``
|
||||
option to your state declaration:
|
||||
|
||||
.. code_block:: yaml
|
||||
.. code-block:: yaml
|
||||
|
||||
nginx:
|
||||
service.running:
|
||||
|
@ -24,7 +24,7 @@ state to finish.
|
|||
|
||||
Given this example:
|
||||
|
||||
.. code_block:: yaml
|
||||
.. code-block:: yaml
|
||||
|
||||
sleep 10:
|
||||
cmd.run:
|
||||
|
@ -74,16 +74,16 @@ also complete.
|
|||
Things to be Careful of
|
||||
=======================
|
||||
|
||||
Parallel States does not prevent you from creating parallel conflicts on your
|
||||
Parallel States do not prevent you from creating parallel conflicts on your
|
||||
system. This means that if you start multiple package installs using Salt then
|
||||
the package manager will block or fail. If you attempt to manage the same file
|
||||
with multiple states in parallel then the result can produce an unexpected
|
||||
file.
|
||||
|
||||
Make sure that the states you choose to run in parallel do not conflict, or
|
||||
else, like in and parallel programming environment, the outcome may not be
|
||||
else, like in any parallel programming environment, the outcome may not be
|
||||
what you expect. Doing things like just making all states run in parallel
|
||||
will almost certinly result in unexpected behavior.
|
||||
will almost certainly result in unexpected behavior.
|
||||
|
||||
With that said, running states in parallel should be safe the vast majority
|
||||
of the time and the most likely culprit for unexpected behavior is running
|
||||
|
|
|
@ -40,7 +40,7 @@ Set up an initial profile at /etc/salt/cloud.profiles or in the /etc/salt/cloud.
|
|||
|
||||
.. code-block:: yaml
|
||||
|
||||
scalewa-ubuntu:
|
||||
scaleway-ubuntu:
|
||||
provider: my-scaleway-config
|
||||
image: Ubuntu Trusty (14.04 LTS)
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ documentation for more information.
|
|||
.. _github-pull-request:
|
||||
|
||||
Sending a GitHub pull request
|
||||
=============================
|
||||
-----------------------------
|
||||
|
||||
Sending pull requests on GitHub is the preferred method for receiving
|
||||
contributions. The workflow advice below mirrors `GitHub's own guide <GitHub
|
||||
|
@ -66,7 +66,7 @@ Fork a Repo Guide_>`_ and is well worth reading.
|
|||
.. code-block:: bash
|
||||
|
||||
git fetch upstream
|
||||
git checkout -b fix-broken-thing upstream/2016.3
|
||||
git checkout -b fix-broken-thing upstream/2016.11
|
||||
|
||||
If you're working on a feature, create your branch from the develop branch.
|
||||
|
||||
|
@ -130,7 +130,7 @@ Fork a Repo Guide_>`_ and is well worth reading.
|
|||
.. code-block:: bash
|
||||
|
||||
git fetch upstream
|
||||
git rebase upstream/2016.3 fix-broken-thing
|
||||
git rebase upstream/2016.11 fix-broken-thing
|
||||
git push -u origin fix-broken-thing
|
||||
|
||||
or
|
||||
|
@ -170,9 +170,9 @@ Fork a Repo Guide_>`_ and is well worth reading.
|
|||
https://github.com/my-account/salt/pull/new/fix-broken-thing
|
||||
|
||||
#. If your branch is a fix for a release branch, choose that as the base
|
||||
branch (e.g. ``2016.3``),
|
||||
branch (e.g. ``2016.11``),
|
||||
|
||||
https://github.com/my-account/salt/compare/saltstack:2016.3...fix-broken-thing
|
||||
https://github.com/my-account/salt/compare/saltstack:2016.11...fix-broken-thing
|
||||
|
||||
If your branch is a feature, choose ``develop`` as the base branch,
|
||||
|
||||
|
@ -205,72 +205,206 @@ Fork a Repo Guide_>`_ and is well worth reading.
|
|||
|
||||
.. _which-salt-branch:
|
||||
|
||||
Which Salt branch?
|
||||
==================
|
||||
|
||||
GitHub will open pull requests against Salt's main branch, ``develop``, by
|
||||
default. Ideally, features should go into ``develop`` and bug fixes and
|
||||
documentation changes should go into the oldest supported release branch
|
||||
affected by the bug or documentation update. See
|
||||
:ref:`Sending a GitHub pull request <github-pull-request>`.
|
||||
|
||||
If you have a bug fix or doc change and have already forked your working
|
||||
branch from ``develop`` and do not know how to rebase your commits against
|
||||
another branch, then submit it to ``develop`` anyway and we'll be sure to
|
||||
back-port it to the correct place.
|
||||
|
||||
The current release branch
|
||||
--------------------------
|
||||
|
||||
The current release branch is the most recent stable release. Pull requests
|
||||
containing bug fixes should be made against the release branch.
|
||||
|
||||
The branch name will be a date-based name such as ``2016.3``.
|
||||
|
||||
Bug fixes are made on this branch so that minor releases can be cut from this
|
||||
branch without introducing surprises and new features. This approach maximizes
|
||||
stability.
|
||||
|
||||
The Salt development team will "merge-forward" any fixes made on the release
|
||||
branch to the ``develop`` branch once the pull request has been accepted. This
|
||||
keeps the fix in isolation on the release branch and also keeps the ``develop``
|
||||
branch up-to-date.
|
||||
|
||||
.. note:: Closing GitHub issues from commits
|
||||
|
||||
This "merge-forward" strategy requires that `the magic keywords to close a
|
||||
GitHub issue <Closing issues via commit message_>`_ appear in the commit
|
||||
message text directly. Only including the text in a pull request will not
|
||||
close the issue.
|
||||
|
||||
GitHub will close the referenced issue once the *commit* containing the
|
||||
magic text is merged into the default branch (``develop``). Any magic text
|
||||
input only into the pull request description will not be seen at the
|
||||
Git-level when those commits are merged-forward. In other words, only the
|
||||
commits are merged-forward and not the pull request.
|
||||
|
||||
The ``develop`` branch
|
||||
Salt's Branch Topology
|
||||
----------------------
|
||||
|
||||
There are three different kinds of branches in use: develop, main release
|
||||
branches, and dot release branches.
|
||||
|
||||
- All feature work should go into the ``develop`` branch.
|
||||
- Bug fixes and documentation changes should go into the oldest supported
|
||||
**main** release branch affected by the the bug or documentation change.
|
||||
Main release branches are named after a year and month, such as
|
||||
``2016.11`` and ``2017.7``.
|
||||
- Hot fixes, as determined by SaltStack's release team, should be submitted
|
||||
against **dot** release branches. Dot release branches are named after a
|
||||
year, month, and version. Examples include ``2016.11.8`` and ``2017.7.2``.
|
||||
|
||||
.. note::
|
||||
|
||||
GitHub will open pull requests against Salt's main branch, ``develop``,
|
||||
byndefault. Be sure to check which branch is selected when creating the
|
||||
pull request.
|
||||
|
||||
The Develop Branch
|
||||
==================
|
||||
|
||||
The ``develop`` branch is unstable and bleeding-edge. Pull requests containing
|
||||
feature additions or non-bug-fix changes should be made against the ``develop``
|
||||
branch.
|
||||
|
||||
The Salt development team will back-port bug fixes made to ``develop`` to the
|
||||
current release branch if the contributor cannot create the pull request
|
||||
against that branch.
|
||||
.. note::
|
||||
|
||||
Release Branches
|
||||
----------------
|
||||
If you have a bug fix or documentation change and have already forked your
|
||||
working branch from ``develop`` and do not know how to rebase your commits
|
||||
against another branch, then submit it to ``develop`` anyway. SaltStack's
|
||||
development team will be happy to back-port it to the correct branch.
|
||||
|
||||
For each release a branch will be created when we are ready to tag. The branch will be the same name as the tag minus the v. For example, the v2017.7.1 release was created from the 2017.7.1 branch. This branching strategy will allow for more stability when there is a need for a re-tag during the testing phase of our releases.
|
||||
**Please make sure you let the maintainers know that the pull request needs
|
||||
to be back-ported.**
|
||||
|
||||
Once the branch is created, the fixes required for a given release, as determined by the SaltStack release team, will be added to this branch. All commits in this branch will be merged forward into the parent branch as well.
|
||||
Main Release Branches
|
||||
=====================
|
||||
|
||||
The current release branch is the most recent stable release. Pull requests
|
||||
containing bug fixes or documentation changes should be made against the main
|
||||
release branch that is affected.
|
||||
|
||||
The branch name will be a date-based name such as ``2016.11``.
|
||||
|
||||
Bug fixes are made on this branch so that dot release branches can be cut from
|
||||
the main release branch without introducing surprises and new features. This
|
||||
approach maximizes stability.
|
||||
|
||||
Dot Release Branches
|
||||
====================
|
||||
|
||||
Prior to tagging an official release, a branch will be created when the SaltStack
|
||||
release team is ready to tag. The dot release branch is created from a main release
|
||||
branch. The dot release branch will be the same name as the tag minus the ``v``.
|
||||
For example, the ``2017.7.1`` dot release branch was created from the ``2017.7``
|
||||
main release branch. The ``v2017.7.1`` release was tagged at the ``HEAD`` of the
|
||||
``2017.7.1`` branch.
|
||||
|
||||
This branching strategy will allow for more stability when there is a need for
|
||||
a re-tag during the testing phase of the release process and further increases
|
||||
stability.
|
||||
|
||||
Once the dot release branch is created, the fixes required for a given release,
|
||||
as determined by the SaltStack release team, will be added to this branch. All
|
||||
commits in this branch will be merged forward into the main release branch as
|
||||
well.
|
||||
|
||||
Merge Forward Process
|
||||
=====================
|
||||
|
||||
The Salt repository follows a "Merge Forward" policy. The merge-forward
|
||||
behavior means that changes submitted to older main release branches will
|
||||
automatically be "merged-forward" into the newer branches.
|
||||
|
||||
For example, a pull request is merged into ``2016.11``. Then, the entire
|
||||
``2016.11`` branch is merged-forward into the ``2017.7`` branch, and the
|
||||
``2017.7`` branch is merged-forward into the ``develop`` branch.
|
||||
|
||||
This process makes is easy for contributors to make only one pull-request
|
||||
against an older branch, but allows the change to propagate to all **main**
|
||||
release branches.
|
||||
|
||||
The merge-forward work-flow applies to all main release branches and the
|
||||
operation runs continuously.
|
||||
|
||||
Merge-Forwards for Dot Release Branches
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The merge-forward policy applies to dot release branches as well, but has a
|
||||
slightly different behavior. If a change is submitted to a **dot** release
|
||||
branch, the dot release branch will be merged into its parent **main**
|
||||
release branch.
|
||||
|
||||
For example, a pull request is merged into the ``2017.7.2`` release branch.
|
||||
Then, the entire ``2017.7.2`` branch is merged-forward into the ``2017.7``
|
||||
branch. From there, the merge forward process continues as normal.
|
||||
|
||||
The only way in which dot release branches differ from main release branches
|
||||
in regard to merge-forwards, is that once a dot release branch is created
|
||||
from the main release branch, the dot release branch does not receive merge
|
||||
forwards.
|
||||
|
||||
.. note::
|
||||
|
||||
The merge forward process for dot release branches is one-way:
|
||||
dot release branch --> main release branch.
|
||||
|
||||
Closing GitHub issues from commits
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This "merge-forward" strategy requires that `the magic keywords to close a
|
||||
GitHub issue <Closing issues via commit message_>`_ appear in the commit
|
||||
message text directly. Only including the text in a pull request will not
|
||||
close the issue.
|
||||
|
||||
GitHub will close the referenced issue once the *commit* containing the
|
||||
magic text is merged into the default branch (``develop``). Any magic text
|
||||
input only into the pull request description will not be seen at the
|
||||
Git-level when those commits are merged-forward. In other words, only the
|
||||
commits are merged-forward and not the pull request text.
|
||||
|
||||
.. _backporting-pull-requests:
|
||||
|
||||
Backporting Pull Requests
|
||||
=========================
|
||||
|
||||
If a bug is fixed on ``develop`` and the bug is also present on a
|
||||
currently-supported release branch, it will need to be back-ported to an
|
||||
applicable branch.
|
||||
|
||||
.. note:: Most Salt contributors can skip these instructions
|
||||
|
||||
These instructions do not need to be read in order to contribute to the
|
||||
Salt project! The SaltStack team will back-port fixes on behalf of
|
||||
contributors in order to keep the contribution process easy.
|
||||
|
||||
These instructions are intended for frequent Salt contributors, advanced
|
||||
Git users, SaltStack employees, or independent souls who wish to back-port
|
||||
changes themselves.
|
||||
|
||||
It is often easiest to fix a bug on the oldest supported release branch and
|
||||
then merge that branch forward into ``develop`` (as described earlier in this
|
||||
document). When that is not possible the fix must be back-ported, or copied,
|
||||
into any other affected branches.
|
||||
|
||||
These steps assume a pull request ``#1234`` has been merged into ``develop``.
|
||||
And ``upstream`` is the name of the remote pointing to the main Salt repo.
|
||||
|
||||
#. Identify the oldest supported release branch that is affected by the bug.
|
||||
|
||||
#. Create a new branch for the back-port by reusing the same branch from the
|
||||
original pull request.
|
||||
|
||||
Name the branch ``bp-<NNNN>`` and use the number of the original pull
|
||||
request.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
git fetch upstream refs/pull/1234/head:bp-1234
|
||||
git checkout bp-1234
|
||||
|
||||
#. Find the parent commit of the original pull request.
|
||||
|
||||
The parent commit of the original pull request must be known in order to
|
||||
rebase onto a release branch. The easiest way to find this is on GitHub.
|
||||
|
||||
Open the original pull request on GitHub and find the first commit in the
|
||||
list of commits. Select and copy the SHA for that commit. The parent of
|
||||
that commit can be specified by appending ``~1`` to the end.
|
||||
|
||||
#. Rebase the new branch on top of the release branch.
|
||||
|
||||
* ``<release-branch>`` is the branch identified in step #1.
|
||||
|
||||
* ``<orig-base>`` is the SHA identified in step #3 -- don't forget to add
|
||||
``~1`` to the end!
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
git rebase --onto <release-branch> <orig-base> bp-1234
|
||||
|
||||
Note, release branches prior to ``2016.11`` will not be able to make use of
|
||||
rebase and must use cherry-picking instead.
|
||||
|
||||
#. Push the back-port branch to GitHub and open a new pull request.
|
||||
|
||||
Opening a pull request for the back-port allows for the test suite and
|
||||
normal code-review process.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
git push -u origin bp-1234
|
||||
|
||||
Keeping Salt Forks in Sync
|
||||
==========================
|
||||
--------------------------
|
||||
|
||||
Salt is advancing quickly. It is therefore critical to pull upstream changes
|
||||
Salt advances quickly. It is therefore critical to pull upstream changes
|
||||
from upstream into your fork on a regular basis. Nothing is worse than putting
|
||||
hard work into a pull request only to see bunches of merge conflicts because it
|
||||
has diverged too far from upstream.
|
||||
|
@ -332,92 +466,53 @@ the name of the main `saltstack/salt`_ repository.
|
|||
the current release branch.
|
||||
|
||||
Posting patches to the mailing list
|
||||
===================================
|
||||
-----------------------------------
|
||||
|
||||
Patches will also be accepted by email. Format patches using `git
|
||||
format-patch`_ and send them to the `salt-users`_ mailing list. The contributor
|
||||
will then get credit for the patch, and the Salt community will have an archive
|
||||
of the patch and a place for discussion.
|
||||
|
||||
.. _backporting-pull-requests:
|
||||
|
||||
Backporting Pull Requests
|
||||
=========================
|
||||
|
||||
If a bug is fixed on ``develop`` and the bug is also present on a
|
||||
currently-supported release branch it will need to be back-ported to all
|
||||
applicable branches.
|
||||
|
||||
.. note:: Most Salt contributors can skip these instructions
|
||||
|
||||
These instructions do not need to be read in order to contribute to the
|
||||
Salt project! The SaltStack team will back-port fixes on behalf of
|
||||
contributors in order to keep the contribution process easy.
|
||||
|
||||
These instructions are intended for frequent Salt contributors, advanced
|
||||
Git users, SaltStack employees, or independent souls who wish to back-port
|
||||
changes themselves.
|
||||
|
||||
It is often easiest to fix a bug on the oldest supported release branch and
|
||||
then merge that branch forward into ``develop`` (as described earlier in this
|
||||
document). When that is not possible the fix must be back-ported, or copied,
|
||||
into any other affected branches.
|
||||
|
||||
These steps assume a pull request ``#1234`` has been merged into ``develop``.
|
||||
And ``upstream`` is the name of the remote pointing to the main Salt repo.
|
||||
|
||||
1. Identify the oldest supported release branch that is affected by the bug.
|
||||
|
||||
2. Create a new branch for the back-port by reusing the same branch from the
|
||||
original pull request.
|
||||
|
||||
Name the branch ``bp-<NNNN>`` and use the number of the original pull
|
||||
request.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
git fetch upstream refs/pull/1234/head:bp-1234
|
||||
git checkout bp-1234
|
||||
|
||||
3. Find the parent commit of the original pull request.
|
||||
|
||||
The parent commit of the original pull request must be known in order to
|
||||
rebase onto a release branch. The easiest way to find this is on GitHub.
|
||||
|
||||
Open the original pull request on GitHub and find the first commit in the
|
||||
list of commits. Select and copy the SHA for that commit. The parent of
|
||||
that commit can be specified by appending ``~1`` to the end.
|
||||
|
||||
4. Rebase the new branch on top of the release branch.
|
||||
|
||||
* ``<release-branch>`` is the branch identified in step #1.
|
||||
|
||||
* ``<orig-base>`` is the SHA identified in step #3 -- don't forget to add
|
||||
``~1`` to the end!
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
git rebase --onto <release-branch> <orig-base> bp-1234
|
||||
|
||||
Note, release branches prior to ``2016.3`` will not be able to make use of
|
||||
rebase and must use cherry-picking instead.
|
||||
|
||||
5. Push the back-port branch to GitHub and open a new pull request.
|
||||
|
||||
Opening a pull request for the back-port allows for the test suite and
|
||||
normal code-review process.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
git push -u origin bp-1234
|
||||
|
||||
Issue and Pull Request Labeling System
|
||||
======================================
|
||||
--------------------------------------
|
||||
|
||||
SaltStack uses several labeling schemes to help facilitate code contributions
|
||||
and bug resolution. See the :ref:`Labels and Milestones
|
||||
<labels-and-milestones>` documentation for more information.
|
||||
|
||||
Mentionbot
|
||||
----------
|
||||
|
||||
SaltStack runs a mention-bot which notifies contributors who might be able
|
||||
to help review incoming pull-requests based on their past contribution to
|
||||
files which are being changed.
|
||||
|
||||
If you do not wish to receive these notifications, please add your GitHub
|
||||
handle to the blacklist line in the ``.mention-bot`` file located in the
|
||||
root of the Salt repository.
|
||||
|
||||
.. _probot-gpg-verification:
|
||||
|
||||
GPG Verification
|
||||
----------------
|
||||
|
||||
SaltStack has enabled `GPG Probot`_ to enforce GPG signatures for all
|
||||
commits included in a Pull Request.
|
||||
|
||||
In order for the GPG verification status check to pass, *every* contributor in
|
||||
the pull request must:
|
||||
|
||||
- Set up a GPG key on local machine
|
||||
- Sign all commits in the pull request with key
|
||||
- Link key with GitHub account
|
||||
|
||||
This applies to all commits in the pull request.
|
||||
|
||||
GitHub hosts a number of `help articles`_ for creating a GPG key, using the
|
||||
GPG key with ``git`` locally, and linking the GPG key to your GitHub account.
|
||||
Once these steps are completed, the commit signing verification will look like
|
||||
the example in GitHub's `GPG Signature Verification feature announcement`_.
|
||||
|
||||
.. _`saltstack/salt`: https://github.com/saltstack/salt
|
||||
.. _`GitHub Fork a Repo Guide`: https://help.github.com/articles/fork-a-repo
|
||||
.. _`GitHub issue tracker`: https://github.com/saltstack/salt/issues
|
||||
|
@ -426,14 +521,6 @@ and bug resolution. See the :ref:`Labels and Milestones
|
|||
.. _`Closing issues via commit message`: https://help.github.com/articles/closing-issues-via-commit-messages
|
||||
.. _`git format-patch`: https://www.kernel.org/pub/software/scm/git/docs/git-format-patch.html
|
||||
.. _salt-users: https://groups.google.com/forum/#!forum/salt-users
|
||||
|
||||
Mentionbot
|
||||
==========
|
||||
|
||||
SaltStack runs a mention-bot which notifies contributors who might be able
|
||||
to help review incoming pull-requests based on their past contribution to
|
||||
files which are being changed.
|
||||
|
||||
If you do not wish to receive these notifications, please add your GitHub
|
||||
handle to the blacklist line in the `.mention-bot` file located in the
|
||||
root of the Salt repository.
|
||||
.. _GPG Probot: https://probot.github.io/apps/gpg/
|
||||
.. _help articles: https://help.github.com/articles/signing-commits-with-gpg/
|
||||
.. _GPG Signature Verification feature announcement: https://github.com/blog/2144-gpg-signature-verification
|
||||
|
|
|
@ -95,19 +95,19 @@ globally available or passed in through function arguments, file data, etc.
|
|||
Mocking Loader Modules
|
||||
----------------------
|
||||
|
||||
Salt loader modules use a series of globally available dunder variables,
|
||||
``__salt__``, ``__opts__``, ``__pillar__``, etc. To facilitate testing these
|
||||
modules a mixin class was created, ``LoaderModuleMockMixin`` which can be found
|
||||
in ``tests/support/mixins.py``. The reason for the exitance of this class is
|
||||
because, historycally, and because it was easier, one would add these dunder
|
||||
variables directly on the imported module. This however, introduces unexpected
|
||||
behavior when running the full test suite since those attributes would not be
|
||||
removed once we were done testing the module and would therefor leak to other
|
||||
modules being tested with unpredictable results. This is the kind of work that
|
||||
should be defered to mock, and that's exactly what this mixin class does.
|
||||
Salt loader modules use a series of globally available dunder variables,
|
||||
``__salt__``, ``__opts__``, ``__pillar__``, etc. To facilitate testing these
|
||||
modules a mixin class was created, ``LoaderModuleMockMixin`` which can be found
|
||||
in ``tests/support/mixins.py``. The reason for the existance of this class is
|
||||
because historiclly and because it was easier, one would add these dunder
|
||||
variables directly on the imported module. This however, introduces unexpected
|
||||
behavior when running the full test suite since those attributes would not be
|
||||
removed once we were done testing the module and would therefore leak to other
|
||||
modules being tested with unpredictable results. This is the kind of work that
|
||||
should be deferred to mock, and that's exactly what this mixin class does.
|
||||
|
||||
As an example, if one needs to specify some options which should be available
|
||||
to the module being tests one should do:
|
||||
As an example, if one needs to specify some options which should be available
|
||||
to the module being tested one should do:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
|
@ -122,8 +122,8 @@ to the module being tests one should do:
|
|||
}
|
||||
}
|
||||
|
||||
Consider this more extensive example from
|
||||
``tests/unit/modules/test_libcloud_dns.py``::
|
||||
Consider this more extensive example from
|
||||
``tests/unit/modules/test_libcloud_dns.py``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
|
@ -173,10 +173,10 @@ Consider this more extensive example from
|
|||
return {libcloud_dns: module_globals}
|
||||
|
||||
|
||||
What happens on the above example is that, we mock a call to
|
||||
`__salt__['config.option']` to return the configuration needed for the
|
||||
execution of the tests. Additionally, if the ``libcloud`` library is not
|
||||
available, since that's not actually part of whats being tested, we mocked that
|
||||
What happens in the above example is we mock a call to
|
||||
`__salt__['config.option']` to return the configuration needed for the
|
||||
execution of the tests. Additionally, if the ``libcloud`` library is not
|
||||
available, since that's not actually part of what's being tested, we mocked that
|
||||
import by patching ``sys.modules`` when tests are running.
|
||||
|
||||
|
||||
|
@ -245,7 +245,7 @@ To understand how one might integrate Mock into writing a unit test for Salt,
|
|||
let's imagine a scenario in which we're testing an execution module that's
|
||||
designed to operate on a database. Furthermore, let's imagine two separate
|
||||
methods, here presented in pseduo-code in an imaginary execution module called
|
||||
'db.py.
|
||||
'db.py'.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
|
@ -319,7 +319,7 @@ function into ``__salt__`` that's actually a MagicMock instance.
|
|||
|
||||
def show_patch(self):
|
||||
with patch.dict(my_module.__salt__,
|
||||
{'function.to_replace': MagicMock()}:
|
||||
{'function.to_replace': MagicMock()}):
|
||||
# From this scope, carry on with testing, with a modified __salt__!
|
||||
|
||||
|
||||
|
|
|
@ -218,6 +218,7 @@ Server configuration values and their defaults:
|
|||
|
||||
# Bind to LDAP anonymously to determine group membership
|
||||
# Active Directory does not allow anonymous binds without special configuration
|
||||
# In addition, if auth.ldap.anonymous is True, empty bind passwords are not permitted.
|
||||
auth.ldap.anonymous: False
|
||||
|
||||
# FOR TESTING ONLY, this is a VERY insecure setting.
|
||||
|
@ -257,7 +258,11 @@ and groups, it re-authenticates as the user running the Salt commands.
|
|||
|
||||
If you are already aware of the structure of your DNs and permissions in your LDAP store are set such that
|
||||
users can look up their own group memberships, then the first and second users can be the same. To tell Salt this is
|
||||
the case, omit the ``auth.ldap.bindpw`` parameter. You can template the ``binddn`` like this:
|
||||
the case, omit the ``auth.ldap.bindpw`` parameter. Note this is not the same thing as using an anonymous bind.
|
||||
Most LDAP servers will not permit anonymous bind, and as mentioned above, if `auth.ldap.anonymous` is False you
|
||||
cannot use an empty password.
|
||||
|
||||
You can template the ``binddn`` like this:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
|
|
|
@ -27,7 +27,12 @@ Salt engines are configured under an ``engines`` top-level section in your Salt
|
|||
port: 5959
|
||||
proto: tcp
|
||||
|
||||
Salt engines must be in the Salt path, or you can add the ``engines_dirs`` option in your Salt master configuration with a list of directories under which Salt attempts to find Salt engines.
|
||||
Salt engines must be in the Salt path, or you can add the ``engines_dirs`` option in your Salt master configuration with a list of directories under which Salt attempts to find Salt engines. This option should be formatted as a list of directories to search, such as:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
engines_dirs:
|
||||
- /home/bob/engines
|
||||
|
||||
Writing an Engine
|
||||
=================
|
||||
|
|
|
@ -80,12 +80,21 @@ same way as in the above example, only without a top-level ``grains:`` key:
|
|||
|
||||
.. note::
|
||||
|
||||
The content of ``/etc/salt/grains`` is ignored if you specify grains in the minion config.
|
||||
Grains in ``/etc/salt/grains`` are ignored if you specify the same grains in the minion config.
|
||||
|
||||
.. note::
|
||||
|
||||
Grains are static, and since they are not often changed, they will need a grains refresh when they are updated. You can do this by calling: ``salt minion saltutil.refresh_modules``
|
||||
|
||||
.. note::
|
||||
|
||||
You can equally configure static grains for Proxy Minions.
|
||||
As multiple Proxy Minion processes can run on the same machine, you need
|
||||
to index the files using the Minion ID, under ``/etc/salt/proxy.d/<minion ID>/grains``.
|
||||
For example, the grains for the Proxy Minion ``router1`` can be defined
|
||||
under ``/etc/salt/proxy.d/router1/grains``, while the grains for the
|
||||
Proxy Minion ``switch7`` can be put in ``/etc/salt/proxy.d/switch7/grains``.
|
||||
|
||||
Matching Grains in the Top File
|
||||
===============================
|
||||
|
||||
|
@ -305,3 +314,9 @@ Syncing grains can be done a number of ways, they are automatically synced when
|
|||
above) the grains can be manually synced and reloaded by calling the
|
||||
:mod:`saltutil.sync_grains <salt.modules.saltutil.sync_grains>` or
|
||||
:mod:`saltutil.sync_all <salt.modules.saltutil.sync_all>` functions.
|
||||
|
||||
.. note::
|
||||
|
||||
When the :conf_minion:`grains_cache` is set to False, the grains dictionary is built
|
||||
and stored in memory on the minion. Every time the minion restarts or
|
||||
``saltutil.refresh_grains`` is run, the grain dictionary is rebuilt from scratch.
|
||||
|
|
|
@ -18,7 +18,7 @@ Installation from official Debian and Raspbian repositories is described
|
|||
Installation from the Official SaltStack Repository
|
||||
===================================================
|
||||
|
||||
Packages for Debian 8 (Jessie) and Debian 7 (Wheezy) are available in the
|
||||
Packages for Debian 9 (Stretch) and Debian 8 (Jessie) are available in the
|
||||
Official SaltStack repository.
|
||||
|
||||
Instructions are at https://repo.saltstack.com/#debian.
|
||||
|
|
|
@ -1526,6 +1526,54 @@ Returns:
|
|||
|
||||
.. jinja_ref:: jinja-in-files
|
||||
|
||||
Escape filters
|
||||
--------------
|
||||
|
||||
.. jinja_ref:: regex_escape
|
||||
|
||||
``regex_escape``
|
||||
----------------
|
||||
|
||||
.. versionadded:: 2017.7.0
|
||||
|
||||
Allows escaping of strings so they can be interpreted literally by another function.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
regex_escape = {{ 'https://example.com?foo=bar%20baz' | regex_escape }}
|
||||
|
||||
will be rendered as:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
regex_escape = https\:\/\/example\.com\?foo\=bar\%20baz
|
||||
|
||||
Set Theory Filters
|
||||
------------------
|
||||
|
||||
.. jinja_ref:: unique
|
||||
|
||||
``unique``
|
||||
----------
|
||||
|
||||
.. versionadded:: 2017.7.0
|
||||
|
||||
Performs set math using Jinja filters.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
unique = {{ ['foo', 'foo', 'bar'] | unique }}
|
||||
|
||||
will be rendered as:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
unique = ['foo', 'bar']
|
||||
|
||||
Jinja in Files
|
||||
==============
|
||||
|
||||
|
|
|
@ -202,7 +202,7 @@ this.
|
|||
|
||||
# /srv/salt/orch/deploy.sls
|
||||
{% set servers = salt['pillar.get']('servers', 'test') %}
|
||||
{% set master = salt['pillat.get']('master', 'salt') %}
|
||||
{% set master = salt['pillar.get']('master', 'salt') %}
|
||||
create_instance:
|
||||
salt.runner:
|
||||
- name: cloud.profile
|
||||
|
|
1731
doc/topics/releases/2016.11.8.rst
Normal file
1731
doc/topics/releases/2016.11.8.rst
Normal file
File diff suppressed because it is too large
Load diff
6
doc/topics/releases/2016.11.9.rst
Normal file
6
doc/topics/releases/2016.11.9.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
============================
|
||||
Salt 2016.11.9 Release Notes
|
||||
============================
|
||||
|
||||
Version 2016.11.9 is a bugfix release for :ref:`2016.11.0 <release-2016-11-0>`.]
|
||||
|
|
@ -7,23 +7,9 @@ Version 2016.3.8 is a bugfix release for :ref:`2016.3.0 <release-2016-3-0>`.
|
|||
Changes for v2016.3.7..v2016.3.8
|
||||
--------------------------------
|
||||
|
||||
New master configuration option `allow_minion_key_revoke`, defaults to True. This option
|
||||
controls whether a minion can request that the master revoke its key. When True, a minion
|
||||
can request a key revocation and the master will comply. If it is False, the key will not
|
||||
be revoked by the msater.
|
||||
Security Fix
|
||||
============
|
||||
|
||||
New master configuration option `require_minion_sign_messages`
|
||||
This requires that minions cryptographically sign the messages they
|
||||
publish to the master. If minions are not signing, then log this information
|
||||
at loglevel 'INFO' and drop the message without acting on it.
|
||||
CVE-2017-14695 Directory traversal vulnerability in minion id validation in SaltStack. Allows remote minions with incorrect credentials to authenticate to a master via a crafted minion ID. Credit for discovering the security flaw goes to: Julian Brost (julian@0x4a42.net)
|
||||
|
||||
New master configuration option `drop_messages_signature_fail`
|
||||
Drop messages from minions when their signatures do not validate.
|
||||
Note that when this option is False but `require_minion_sign_messages` is True
|
||||
minions MUST sign their messages but the validity of their signatures
|
||||
is ignored.
|
||||
|
||||
New minion configuration option `minion_sign_messages`
|
||||
Causes the minion to cryptographically sign the payload of messages it places
|
||||
on the event bus for the master. The payloads are signed with the minion's
|
||||
private key so the master can verify the signature with its public key.
|
||||
CVE-2017-14696 Remote Denial of Service with a specially crafted authentication request. Credit for discovering the security flaw goes to: Julian Brost (julian@0x4a42.net)
|
||||
|
|
29
doc/topics/releases/2016.3.9.rst
Normal file
29
doc/topics/releases/2016.3.9.rst
Normal file
|
@ -0,0 +1,29 @@
|
|||
===========================
|
||||
Salt 2016.3.9 Release Notes
|
||||
===========================
|
||||
|
||||
Version 2016.3.9 is a bugfix release for :ref:`2016.3.0 <release-2016-3-0>`.
|
||||
|
||||
Changes for v2016.3.7..v2016.3.9
|
||||
--------------------------------
|
||||
|
||||
New master configuration option `allow_minion_key_revoke`, defaults to True. This option
|
||||
controls whether a minion can request that the master revoke its key. When True, a minion
|
||||
can request a key revocation and the master will comply. If it is False, the key will not
|
||||
be revoked by the msater.
|
||||
|
||||
New master configuration option `require_minion_sign_messages`
|
||||
This requires that minions cryptographically sign the messages they
|
||||
publish to the master. If minions are not signing, then log this information
|
||||
at loglevel 'INFO' and drop the message without acting on it.
|
||||
|
||||
New master configuration option `drop_messages_signature_fail`
|
||||
Drop messages from minions when their signatures do not validate.
|
||||
Note that when this option is False but `require_minion_sign_messages` is True
|
||||
minions MUST sign their messages but the validity of their signatures
|
||||
is ignored.
|
||||
|
||||
New minion configuration option `minion_sign_messages`
|
||||
Causes the minion to cryptographically sign the payload of messages it places
|
||||
on the event bus for the master. The payloads are signed with the minion's
|
||||
private key so the master can verify the signature with its public key.
|
|
@ -21,6 +21,9 @@ Salt will no longer support Python 2.6. We will provide python2.7 packages on ou
|
|||
|
||||
.. _repo: https://repo.saltstack.com/
|
||||
|
||||
As this will impact the installation of additional dependencies for salt modules please use pip packages if there is not a package available in a repository. You will need to install the python27-pip package to get access to the correct pip27 executable: ``yum install python27-pip``
|
||||
|
||||
|
||||
============
|
||||
Known Issues
|
||||
============
|
||||
|
|
File diff suppressed because it is too large
Load diff
6
doc/topics/releases/2017.7.3.rst
Normal file
6
doc/topics/releases/2017.7.3.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
============================
|
||||
Salt 2017.7.3 Release Notes
|
||||
============================
|
||||
|
||||
Version 2017.7.3 is a bugfix release for :ref:`2017.7.0 <release-2017-7-0>`.
|
||||
|
|
@ -57,7 +57,15 @@ Writing Thorium Formulas
|
|||
========================
|
||||
Like some other Salt subsystems, Thorium uses its own directory structure. The
|
||||
default location for this structure is ``/srv/thorium/``, but it can be changed
|
||||
using the ``thorium_roots_dir`` setting in the ``master`` configuration file.
|
||||
using the ``thorium_roots`` setting in the ``master`` configuration file.
|
||||
|
||||
Example ``thorium_roots`` configuration:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
thorium_roots:
|
||||
base:
|
||||
- /etc/salt/thorium
|
||||
|
||||
|
||||
The Thorium top.sls File
|
||||
|
|
|
@ -27,7 +27,7 @@ Installing Dependencies
|
|||
=======================
|
||||
|
||||
Both pygit2_ and GitPython_ are supported Python interfaces to git. If
|
||||
compatible versions of both are installed, pygit2_ will preferred. In these
|
||||
compatible versions of both are installed, pygit2_ will be preferred. In these
|
||||
cases, GitPython_ can be forced using the :conf_master:`gitfs_provider`
|
||||
parameter in the master config file.
|
||||
|
||||
|
|
|
@ -33,3 +33,5 @@ Tutorials Index
|
|||
* :ref:`The macOS (Maverick) Developer Step By Step Guide To Salt Installation <tutorial-macos-walk-through>`
|
||||
* :ref:`SaltStack Walk-through <tutorial-salt-walk-through>`
|
||||
* :ref:`Writing Salt Tests <tutorial-salt-testing>`
|
||||
* :ref:`Running Salt States and Commands in Docker Containers <docker-sls>`
|
||||
* :ref:`Preseed Minion with Accepted Key <tutorial-preseed-key>`
|
||||
|
|
|
@ -23,7 +23,7 @@ Supported Operating Systems
|
|||
.. note::
|
||||
|
||||
In the event you do not see your distribution or version available please
|
||||
review the develop branch on GitHub as it main contain updates that are
|
||||
review the develop branch on GitHub as it may contain updates that are
|
||||
not present in the stable release:
|
||||
https://github.com/saltstack/salt-bootstrap/tree/develop
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ _su_cmd() {
|
|||
|
||||
|
||||
_get_pid() {
|
||||
netstat $NS_NOTRIM -ap --protocol=unix 2>$ERROR_TO_DEVNULL \
|
||||
netstat -n $NS_NOTRIM -ap --protocol=unix 2>$ERROR_TO_DEVNULL \
|
||||
| sed -r -e "\|\s${SOCK_DIR}/minion_event_${MINION_ID_HASH}_pub\.ipc$|"'!d; s|/.*||; s/.*\s//;' \
|
||||
| uniq
|
||||
}
|
||||
|
@ -155,7 +155,7 @@ start() {
|
|||
printf "\nPROCESSES:\n" >&2
|
||||
ps wwwaxu | grep '[s]alt-minion' >&2
|
||||
printf "\nSOCKETS:\n" >&2
|
||||
netstat $NS_NOTRIM -ap --protocol=unix | grep 'salt.*minion' >&2
|
||||
netstat -n $NS_NOTRIM -ap --protocol=unix | grep 'salt.*minion' >&2
|
||||
printf "\nLOG_FILE:\n" >&2
|
||||
tail -n 20 "$LOG_FILE" >&2
|
||||
printf "\nENVIRONMENT:\n" >&2
|
||||
|
|
|
@ -35,7 +35,8 @@ _salt_get_keys(){
|
|||
}
|
||||
|
||||
_salt(){
|
||||
local _salt_cache_functions=${SALT_COMP_CACHE_FUNCTIONS:='~/.cache/salt-comp-cache_functions'}
|
||||
CACHE_DIR="$HOME/.cache/salt-comp-cache_functions"
|
||||
local _salt_cache_functions=${SALT_COMP_CACHE_FUNCTIONS:=$CACHE_DIR}
|
||||
local _salt_cache_timeout=${SALT_COMP_CACHE_TIMEOUT:='last hour'}
|
||||
|
||||
if [ ! -d "$(dirname ${_salt_cache_functions})" ]; then
|
||||
|
|
|
@ -89,7 +89,7 @@ if Defined x (
|
|||
if %Python%==2 (
|
||||
Set "PyDir=C:\Python27"
|
||||
) else (
|
||||
Set "PyDir=C:\Program Files\Python35"
|
||||
Set "PyDir=C:\Python35"
|
||||
)
|
||||
Set "PATH=%PATH%;%PyDir%;%PyDir%\Scripts"
|
||||
|
||||
|
|
|
@ -175,7 +175,7 @@ If (Test-Path "$($ini['Settings']['Python2Dir'])\python.exe") {
|
|||
DownloadFileWithProgress $url $file
|
||||
|
||||
Write-Output " - $script_name :: Installing $($ini[$bitPrograms]['Python2']) . . ."
|
||||
$p = Start-Process msiexec -ArgumentList "/i $file /qb ADDLOCAL=DefaultFeature,SharedCRT,Extensions,pip_feature,PrependPath TARGETDIR=$($ini['Settings']['Python2Dir'])" -Wait -NoNewWindow -PassThru
|
||||
$p = Start-Process msiexec -ArgumentList "/i $file /qb ADDLOCAL=DefaultFeature,SharedCRT,Extensions,pip_feature,PrependPath TARGETDIR=`"$($ini['Settings']['Python2Dir'])`"" -Wait -NoNewWindow -PassThru
|
||||
}
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
@ -191,7 +191,7 @@ If (!($Path.ToLower().Contains("$($ini['Settings']['Scripts2Dir'])".ToLower())))
|
|||
|
||||
#==============================================================================
|
||||
# Update PIP and SetupTools
|
||||
# caching depends on environmant variable SALT_PIP_LOCAL_CACHE
|
||||
# caching depends on environment variable SALT_PIP_LOCAL_CACHE
|
||||
#==============================================================================
|
||||
Write-Output " ----------------------------------------------------------------"
|
||||
Write-Output " - $script_name :: Updating PIP and SetupTools . . ."
|
||||
|
@ -212,7 +212,7 @@ if ( ! [bool]$Env:SALT_PIP_LOCAL_CACHE) {
|
|||
|
||||
#==============================================================================
|
||||
# Install pypi resources using pip
|
||||
# caching depends on environmant variable SALT_REQ_LOCAL_CACHE
|
||||
# caching depends on environment variable SALT_REQ_LOCAL_CACHE
|
||||
#==============================================================================
|
||||
Write-Output " ----------------------------------------------------------------"
|
||||
Write-Output " - $script_name :: Installing pypi resources using pip . . ."
|
||||
|
@ -230,6 +230,24 @@ if ( ! [bool]$Env:SALT_REQ_LOCAL_CACHE) {
|
|||
Start_Process_and_test_exitcode "$($ini['Settings']['Python2Dir'])\python.exe" "-m pip install --no-index --find-links=$Env:SALT_REQ_LOCAL_CACHE -r $($script_path)\req_2.txt" "pip install"
|
||||
}
|
||||
|
||||
#==============================================================================
|
||||
# Move PyWin32 DLL's to site-packages\win32
|
||||
#==============================================================================
|
||||
Write-Output " - $script_name :: Moving PyWin32 DLLs . . ."
|
||||
Move-Item "$($ini['Settings']['SitePkgs2Dir'])\pywin32_system32\*.dll" "$($ini['Settings']['SitePkgs2Dir'])\win32" -Force
|
||||
|
||||
# Remove pywin32_system32 directory
|
||||
Write-Output " - $script_name :: Removing pywin32_system32 Directory . . ."
|
||||
Remove-Item "$($ini['Settings']['SitePkgs2Dir'])\pywin32_system32"
|
||||
|
||||
# Remove pythonwin directory
|
||||
Write-Output " - $script_name :: Removing pythonwin Directory . . ."
|
||||
Remove-Item "$($ini['Settings']['SitePkgs2Dir'])\pythonwin" -Force -Recurse
|
||||
|
||||
# Remove PyWin32 PostInstall and testall Scripts
|
||||
Write-Output " - $script_name :: Removing PyWin32 scripts . . ."
|
||||
Remove-Item "$($ini['Settings']['Scripts2Dir'])\pywin32_*" -Force -Recurse
|
||||
|
||||
#==============================================================================
|
||||
# Install PyYAML with CLoader
|
||||
# This has to be a compiled binary to get the CLoader
|
||||
|
|
|
@ -175,7 +175,7 @@ If (Test-Path "$($ini['Settings']['Python3Dir'])\python.exe") {
|
|||
DownloadFileWithProgress $url $file
|
||||
|
||||
Write-Output " - $script_name :: Installing $($ini[$bitPrograms]['Python3']) . . ."
|
||||
$p = Start-Process $file -ArgumentList '/passive InstallAllUsers=1 TargetDir="C:\Program Files\Python35" Include_doc=0 Include_tcltk=0 Include_test=0 Include_launcher=0 PrependPath=1 Shortcuts=0' -Wait -NoNewWindow -PassThru
|
||||
$p = Start-Process $file -ArgumentList "/passive InstallAllUsers=1 TargetDir=`"$($ini['Settings']['Python3Dir'])`" Include_doc=0 Include_tcltk=0 Include_test=0 Include_launcher=0 PrependPath=1 Shortcuts=0" -Wait -NoNewWindow -PassThru
|
||||
}
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
@ -247,7 +247,7 @@ Start_Process_and_test_exitcode "$($ini['Settings']['Scripts3Dir'])\pip.exe" "i
|
|||
|
||||
# Move DLL's to Python Root
|
||||
Write-Output " - $script_name :: Moving PyWin32 DLLs . . ."
|
||||
Move-Item "$($ini['Settings']['SitePkgs3Dir'])\pywin32_system32\*.dll" "$($ini['Settings']['Python3Dir'])" -Force
|
||||
Move-Item "$($ini['Settings']['SitePkgs3Dir'])\pywin32_system32\*.dll" "$($ini['Settings']['SitePkgs3Dir'])\win32" -Force
|
||||
|
||||
# Remove pywin32_system32 directory
|
||||
Write-Output " - $script_name :: Removing pywin32_system32 Directory . . ."
|
||||
|
@ -257,6 +257,10 @@ Remove-Item "$($ini['Settings']['SitePkgs3Dir'])\pywin32_system32"
|
|||
Write-Output " - $script_name :: Removing pythonwin Directory . . ."
|
||||
Remove-Item "$($ini['Settings']['SitePkgs3Dir'])\pythonwin" -Force -Recurse
|
||||
|
||||
# Remove PyWin32 PostInstall and testall Scripts
|
||||
Write-Output " - $script_name :: Removing PyWin32 scripts . . ."
|
||||
Remove-Item "$($ini['Settings']['Scripts3Dir'])\pywin32_*" -Force -Recurse
|
||||
|
||||
#==============================================================================
|
||||
# Fix PyCrypto
|
||||
#==============================================================================
|
||||
|
|
|
@ -56,7 +56,7 @@ if %Python%==2 (
|
|||
Set "PyVerMajor=2"
|
||||
Set "PyVerMinor=7"
|
||||
) else (
|
||||
Set "PyDir=C:\Program Files\Python35"
|
||||
Set "PyDir=C:\Python35"
|
||||
Set "PyVerMajor=3"
|
||||
Set "PyVerMinor=5"
|
||||
)
|
||||
|
|
|
@ -16,9 +16,10 @@ if %errorLevel%==0 (
|
|||
)
|
||||
echo.
|
||||
|
||||
:CheckPython2
|
||||
if exist "\Python27" goto RemovePython2
|
||||
if exist "\Program Files\Python35" goto RemovePython3
|
||||
goto eof
|
||||
|
||||
goto CheckPython3
|
||||
|
||||
:RemovePython2
|
||||
rem Uninstall Python 2.7
|
||||
|
@ -47,25 +48,30 @@ goto eof
|
|||
|
||||
goto eof
|
||||
|
||||
:CheckPython3
|
||||
if exist "\Python35" goto RemovePython3
|
||||
|
||||
goto eof
|
||||
|
||||
:RemovePython3
|
||||
echo %0 :: Uninstalling Python 3 ...
|
||||
echo ---------------------------------------------------------------------
|
||||
:: 64 bit
|
||||
if exist "%LOCALAPPDATA%\Package Cache\{b94f45d6-8461-440c-aa4d-bf197b2c2499}" (
|
||||
echo %0 :: - 3.5.3 64bit
|
||||
"%LOCALAPPDATA%\Package Cache\{b94f45d6-8461-440c-aa4d-bf197b2c2499}\python-3.5.3-amd64.exe" /uninstall
|
||||
"%LOCALAPPDATA%\Package Cache\{b94f45d6-8461-440c-aa4d-bf197b2c2499}\python-3.5.3-amd64.exe" /uninstall /passive
|
||||
)
|
||||
|
||||
:: 32 bit
|
||||
if exist "%LOCALAPPDATA%\Package Cache\{a10037e1-4247-47c9-935b-c5ca049d0299}" (
|
||||
echo %0 :: - 3.5.3 32bit
|
||||
"%LOCALAPPDATA%\Package Cache\{a10037e1-4247-47c9-935b-c5ca049d0299}\python-3.5.3" /uninstall
|
||||
"%LOCALAPPDATA%\Package Cache\{a10037e1-4247-47c9-935b-c5ca049d0299}\python-3.5.3" /uninstall /passive
|
||||
)
|
||||
|
||||
rem wipe the Python directory
|
||||
echo %0 :: Removing the C:\Program Files\Python35 Directory ...
|
||||
echo %0 :: Removing the C:\Python35 Directory ...
|
||||
echo ---------------------------------------------------------------------
|
||||
rd /s /q "C:\Program Files\Python35"
|
||||
rd /s /q "C:\Python35"
|
||||
if %errorLevel%==0 (
|
||||
echo Successful
|
||||
) else (
|
||||
|
|
|
@ -44,7 +44,7 @@ ${StrStrAdv}
|
|||
!define CPUARCH "x86"
|
||||
!endif
|
||||
|
||||
; Part of the Trim function for Strings
|
||||
# Part of the Trim function for Strings
|
||||
!define Trim "!insertmacro Trim"
|
||||
!macro Trim ResultVar String
|
||||
Push "${String}"
|
||||
|
@ -61,27 +61,27 @@ ${StrStrAdv}
|
|||
!define MUI_UNICON "salt.ico"
|
||||
!define MUI_WELCOMEFINISHPAGE_BITMAP "panel.bmp"
|
||||
|
||||
; Welcome page
|
||||
# Welcome page
|
||||
!insertmacro MUI_PAGE_WELCOME
|
||||
|
||||
; License page
|
||||
# License page
|
||||
!insertmacro MUI_PAGE_LICENSE "LICENSE.txt"
|
||||
|
||||
; Configure Minion page
|
||||
# Configure Minion page
|
||||
Page custom pageMinionConfig pageMinionConfig_Leave
|
||||
|
||||
; Instfiles page
|
||||
# Instfiles page
|
||||
!insertmacro MUI_PAGE_INSTFILES
|
||||
|
||||
; Finish page (Customized)
|
||||
# Finish page (Customized)
|
||||
!define MUI_PAGE_CUSTOMFUNCTION_SHOW pageFinish_Show
|
||||
!define MUI_PAGE_CUSTOMFUNCTION_LEAVE pageFinish_Leave
|
||||
!insertmacro MUI_PAGE_FINISH
|
||||
|
||||
; Uninstaller pages
|
||||
# Uninstaller pages
|
||||
!insertmacro MUI_UNPAGE_INSTFILES
|
||||
|
||||
; Language files
|
||||
# Language files
|
||||
!insertmacro MUI_LANGUAGE "English"
|
||||
|
||||
|
||||
|
@ -201,8 +201,8 @@ ShowInstDetails show
|
|||
ShowUnInstDetails show
|
||||
|
||||
|
||||
; Check and install Visual C++ redist packages
|
||||
; See http://blogs.msdn.com/b/astebner/archive/2009/01/29/9384143.aspx for more info
|
||||
# Check and install Visual C++ redist packages
|
||||
# See http://blogs.msdn.com/b/astebner/archive/2009/01/29/9384143.aspx for more info
|
||||
Section -Prerequisites
|
||||
|
||||
Var /GLOBAL VcRedistName
|
||||
|
@ -211,12 +211,12 @@ Section -Prerequisites
|
|||
Var /Global CheckVcRedist
|
||||
StrCpy $CheckVcRedist "False"
|
||||
|
||||
; Visual C++ 2015 redist packages
|
||||
# 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
|
||||
# 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}"
|
||||
|
@ -239,7 +239,7 @@ Section -Prerequisites
|
|||
StrCpy $VcRedistGuid ${PY2_VC_REDIST_X86_GUID}
|
||||
${EndIf}
|
||||
|
||||
; VCRedist 2008 only needed on Windows Server 2008R2/Windows 7 and below
|
||||
# VCRedist 2008 only needed on Windows Server 2008R2/Windows 7 and below
|
||||
${If} ${AtMostWin2008R2}
|
||||
StrCpy $CheckVcRedist "True"
|
||||
${EndIf}
|
||||
|
@ -255,20 +255,41 @@ Section -Prerequisites
|
|||
"$VcRedistName is currently not installed. Would you like to install?" \
|
||||
/SD IDYES IDNO endVcRedist
|
||||
|
||||
ClearErrors
|
||||
; The Correct version of VCRedist is copied over by "build_pkg.bat"
|
||||
# The Correct version of VCRedist is copied over by "build_pkg.bat"
|
||||
SetOutPath "$INSTDIR\"
|
||||
File "..\prereqs\vcredist.exe"
|
||||
; /passive used by 2015 installer
|
||||
; /qb! used by 2008 installer
|
||||
; It just ignores the unrecognized switches...
|
||||
ExecWait "$INSTDIR\vcredist.exe /qb! /passive"
|
||||
IfErrors 0 endVcRedist
|
||||
# If an output variable is specified ($0 in the case below),
|
||||
# ExecWait sets the variable with the exit code (and only sets the
|
||||
# error flag if an error occurs; if an error occurs, the contents
|
||||
# of the user variable are undefined).
|
||||
# http://nsis.sourceforge.net/Reference/ExecWait
|
||||
# /passive used by 2015 installer
|
||||
# /qb! used by 2008 installer
|
||||
# It just ignores the unrecognized switches...
|
||||
ClearErrors
|
||||
ExecWait '"$INSTDIR\vcredist.exe" /qb! /passive /norestart' $0
|
||||
IfErrors 0 CheckVcRedistErrorCode
|
||||
MessageBox MB_OK \
|
||||
"$VcRedistName failed to install. Try installing the package manually." \
|
||||
/SD IDOK
|
||||
Goto endVcRedist
|
||||
|
||||
CheckVcRedistErrorCode:
|
||||
# Check for Reboot Error Code (3010)
|
||||
${If} $0 == 3010
|
||||
MessageBox MB_OK \
|
||||
"$VcRedistName installed but requires a restart to complete." \
|
||||
/SD IDOK
|
||||
|
||||
# Check for any other errors
|
||||
${ElseIfNot} $0 == 0
|
||||
MessageBox MB_OK \
|
||||
"$VcRedistName failed with ErrorCode: $0. Try installing the package manually." \
|
||||
/SD IDOK
|
||||
${EndIf}
|
||||
|
||||
endVcRedist:
|
||||
|
||||
${EndIf}
|
||||
|
||||
${EndIf}
|
||||
|
@ -294,12 +315,12 @@ Function .onInit
|
|||
|
||||
Call parseCommandLineSwitches
|
||||
|
||||
; Check for existing installation
|
||||
# Check for existing installation
|
||||
ReadRegStr $R0 HKLM \
|
||||
"Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
|
||||
"UninstallString"
|
||||
StrCmp $R0 "" checkOther
|
||||
; Found existing installation, prompt to uninstall
|
||||
# Found existing installation, prompt to uninstall
|
||||
MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION \
|
||||
"${PRODUCT_NAME} is already installed.$\n$\n\
|
||||
Click `OK` to remove the existing installation." \
|
||||
|
@ -307,12 +328,12 @@ Function .onInit
|
|||
Abort
|
||||
|
||||
checkOther:
|
||||
; Check for existing installation of full salt
|
||||
# Check for existing installation of full salt
|
||||
ReadRegStr $R0 HKLM \
|
||||
"Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME_OTHER}" \
|
||||
"UninstallString"
|
||||
StrCmp $R0 "" skipUninstall
|
||||
; Found existing installation, prompt to uninstall
|
||||
# Found existing installation, prompt to uninstall
|
||||
MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION \
|
||||
"${PRODUCT_NAME_OTHER} is already installed.$\n$\n\
|
||||
Click `OK` to remove the existing installation." \
|
||||
|
@ -321,22 +342,22 @@ Function .onInit
|
|||
|
||||
uninst:
|
||||
|
||||
; Get current Silent status
|
||||
# Get current Silent status
|
||||
StrCpy $R0 0
|
||||
${If} ${Silent}
|
||||
StrCpy $R0 1
|
||||
${EndIf}
|
||||
|
||||
; Turn on Silent mode
|
||||
# Turn on Silent mode
|
||||
SetSilent silent
|
||||
|
||||
; Don't remove all directories
|
||||
# Don't remove all directories
|
||||
StrCpy $DeleteInstallDir 0
|
||||
|
||||
; Uninstall silently
|
||||
# Uninstall silently
|
||||
Call uninstallSalt
|
||||
|
||||
; Set it back to Normal mode, if that's what it was before
|
||||
# Set it back to Normal mode, if that's what it was before
|
||||
${If} $R0 == 0
|
||||
SetSilent normal
|
||||
${EndIf}
|
||||
|
@ -350,7 +371,7 @@ Section -Post
|
|||
|
||||
WriteUninstaller "$INSTDIR\uninst.exe"
|
||||
|
||||
; Uninstall Registry Entries
|
||||
# Uninstall Registry Entries
|
||||
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" \
|
||||
"DisplayName" "$(^Name)"
|
||||
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" \
|
||||
|
@ -366,19 +387,19 @@ Section -Post
|
|||
WriteRegStr HKLM "SYSTEM\CurrentControlSet\services\salt-minion" \
|
||||
"DependOnService" "nsi"
|
||||
|
||||
; Set the estimated size
|
||||
# Set the estimated size
|
||||
${GetSize} "$INSTDIR\bin" "/S=OK" $0 $1 $2
|
||||
IntFmt $0 "0x%08X" $0
|
||||
WriteRegDWORD ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" \
|
||||
"EstimatedSize" "$0"
|
||||
|
||||
; Commandline Registry Entries
|
||||
# Commandline Registry Entries
|
||||
WriteRegStr HKLM "${PRODUCT_CALL_REGKEY}" "" "$INSTDIR\salt-call.bat"
|
||||
WriteRegStr HKLM "${PRODUCT_CALL_REGKEY}" "Path" "$INSTDIR\bin\"
|
||||
WriteRegStr HKLM "${PRODUCT_MINION_REGKEY}" "" "$INSTDIR\salt-minion.bat"
|
||||
WriteRegStr HKLM "${PRODUCT_MINION_REGKEY}" "Path" "$INSTDIR\bin\"
|
||||
|
||||
; Register the Salt-Minion Service
|
||||
# Register the Salt-Minion Service
|
||||
nsExec::Exec "nssm.exe install salt-minion $INSTDIR\bin\python.exe -E -s $INSTDIR\bin\Scripts\salt-minion -c $INSTDIR\conf -l quiet"
|
||||
nsExec::Exec "nssm.exe set salt-minion Description Salt Minion from saltstack.com"
|
||||
nsExec::Exec "nssm.exe set salt-minion Start SERVICE_AUTO_START"
|
||||
|
@ -398,12 +419,12 @@ SectionEnd
|
|||
|
||||
Function .onInstSuccess
|
||||
|
||||
; If StartMinionDelayed is 1, then set the service to start delayed
|
||||
# If StartMinionDelayed is 1, then set the service to start delayed
|
||||
${If} $StartMinionDelayed == 1
|
||||
nsExec::Exec "nssm.exe set salt-minion Start SERVICE_DELAYED_AUTO_START"
|
||||
${EndIf}
|
||||
|
||||
; If start-minion is 1, then start the service
|
||||
# If start-minion is 1, then start the service
|
||||
${If} $StartMinion == 1
|
||||
nsExec::Exec 'net start salt-minion'
|
||||
${EndIf}
|
||||
|
@ -413,10 +434,11 @@ FunctionEnd
|
|||
|
||||
Function un.onInit
|
||||
|
||||
; Load the parameters
|
||||
# Load the parameters
|
||||
${GetParameters} $R0
|
||||
|
||||
# Uninstaller: Remove Installation Directory
|
||||
ClearErrors
|
||||
${GetOptions} $R0 "/delete-install-dir" $R1
|
||||
IfErrors delete_install_dir_not_found
|
||||
StrCpy $DeleteInstallDir 1
|
||||
|
@ -434,7 +456,7 @@ Section Uninstall
|
|||
|
||||
Call un.uninstallSalt
|
||||
|
||||
; Remove C:\salt from the Path
|
||||
# Remove C:\salt from the Path
|
||||
Push "C:\salt"
|
||||
Call un.RemoveFromPath
|
||||
|
||||
|
@ -444,27 +466,27 @@ SectionEnd
|
|||
!macro uninstallSalt un
|
||||
Function ${un}uninstallSalt
|
||||
|
||||
; Make sure we're in the right directory
|
||||
# Make sure we're in the right directory
|
||||
${If} $INSTDIR == "c:\salt\bin\Scripts"
|
||||
StrCpy $INSTDIR "C:\salt"
|
||||
${EndIf}
|
||||
|
||||
; Stop and Remove salt-minion service
|
||||
# Stop and Remove salt-minion service
|
||||
nsExec::Exec 'net stop salt-minion'
|
||||
nsExec::Exec 'sc delete salt-minion'
|
||||
|
||||
; Stop and remove the salt-master service
|
||||
# Stop and remove the salt-master service
|
||||
nsExec::Exec 'net stop salt-master'
|
||||
nsExec::Exec 'sc delete salt-master'
|
||||
|
||||
; Remove files
|
||||
# Remove files
|
||||
Delete "$INSTDIR\uninst.exe"
|
||||
Delete "$INSTDIR\nssm.exe"
|
||||
Delete "$INSTDIR\salt*"
|
||||
Delete "$INSTDIR\vcredist.exe"
|
||||
RMDir /r "$INSTDIR\bin"
|
||||
|
||||
; Remove Registry entries
|
||||
# Remove Registry entries
|
||||
DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}"
|
||||
DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY_OTHER}"
|
||||
DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_CALL_REGKEY}"
|
||||
|
@ -474,17 +496,17 @@ Function ${un}uninstallSalt
|
|||
DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_MINION_REGKEY}"
|
||||
DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_RUN_REGKEY}"
|
||||
|
||||
; Automatically close when finished
|
||||
# Automatically close when finished
|
||||
SetAutoClose true
|
||||
|
||||
; Prompt to remove the Installation directory
|
||||
# Prompt to remove the Installation directory
|
||||
${IfNot} $DeleteInstallDir == 1
|
||||
MessageBox MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2 \
|
||||
"Would you like to completely remove $INSTDIR and all of its contents?" \
|
||||
/SD IDNO IDNO finished
|
||||
${EndIf}
|
||||
|
||||
; Make sure you're not removing Program Files
|
||||
# Make sure you're not removing Program Files
|
||||
${If} $INSTDIR != 'Program Files'
|
||||
${AndIf} $INSTDIR != 'Program Files (x86)'
|
||||
RMDir /r "$INSTDIR"
|
||||
|
@ -526,7 +548,7 @@ FunctionEnd
|
|||
|
||||
Function Trim
|
||||
|
||||
Exch $R1 ; Original string
|
||||
Exch $R1 # Original string
|
||||
Push $R2
|
||||
|
||||
Loop:
|
||||
|
@ -558,36 +580,36 @@ Function Trim
|
|||
FunctionEnd
|
||||
|
||||
|
||||
;------------------------------------------------------------------------------
|
||||
; StrStr Function
|
||||
; - find substring in a string
|
||||
;
|
||||
; Usage:
|
||||
; Push "this is some string"
|
||||
; Push "some"
|
||||
; Call StrStr
|
||||
; Pop $0 ; "some string"
|
||||
;------------------------------------------------------------------------------
|
||||
#------------------------------------------------------------------------------
|
||||
# StrStr Function
|
||||
# - find substring in a string
|
||||
#
|
||||
# Usage:
|
||||
# Push "this is some string"
|
||||
# Push "some"
|
||||
# Call StrStr
|
||||
# Pop $0 ; "some string"
|
||||
#------------------------------------------------------------------------------
|
||||
!macro StrStr un
|
||||
Function ${un}StrStr
|
||||
|
||||
Exch $R1 ; $R1=substring, stack=[old$R1,string,...]
|
||||
Exch ; stack=[string,old$R1,...]
|
||||
Exch $R2 ; $R2=string, stack=[old$R2,old$R1,...]
|
||||
Push $R3 ; $R3=strlen(substring)
|
||||
Push $R4 ; $R4=count
|
||||
Push $R5 ; $R5=tmp
|
||||
StrLen $R3 $R1 ; Get the length of the Search String
|
||||
StrCpy $R4 0 ; Set the counter to 0
|
||||
Exch $R1 # $R1=substring, stack=[old$R1,string,...]
|
||||
Exch # stack=[string,old$R1,...]
|
||||
Exch $R2 # $R2=string, stack=[old$R2,old$R1,...]
|
||||
Push $R3 # $R3=strlen(substring)
|
||||
Push $R4 # $R4=count
|
||||
Push $R5 # $R5=tmp
|
||||
StrLen $R3 $R1 # Get the length of the Search String
|
||||
StrCpy $R4 0 # Set the counter to 0
|
||||
|
||||
loop:
|
||||
StrCpy $R5 $R2 $R3 $R4 ; Create a moving window of the string that is
|
||||
; the size of the length of the search string
|
||||
StrCmp $R5 $R1 done ; Is the contents of the window the same as
|
||||
; search string, then done
|
||||
StrCmp $R5 "" done ; Is the window empty, then done
|
||||
IntOp $R4 $R4 + 1 ; Shift the windows one character
|
||||
Goto loop ; Repeat
|
||||
StrCpy $R5 $R2 $R3 $R4 # Create a moving window of the string that is
|
||||
# the size of the length of the search string
|
||||
StrCmp $R5 $R1 done # Is the contents of the window the same as
|
||||
# search string, then done
|
||||
StrCmp $R5 "" done # Is the window empty, then done
|
||||
IntOp $R4 $R4 + 1 # Shift the windows one character
|
||||
Goto loop # Repeat
|
||||
|
||||
done:
|
||||
StrCpy $R1 $R2 "" $R4
|
||||
|
@ -595,7 +617,7 @@ Function ${un}StrStr
|
|||
Pop $R4
|
||||
Pop $R3
|
||||
Pop $R2
|
||||
Exch $R1 ; $R1=old$R1, stack=[result,...]
|
||||
Exch $R1 # $R1=old$R1, stack=[result,...]
|
||||
|
||||
FunctionEnd
|
||||
!macroend
|
||||
|
@ -603,74 +625,74 @@ FunctionEnd
|
|||
!insertmacro StrStr "un."
|
||||
|
||||
|
||||
;------------------------------------------------------------------------------
|
||||
; AddToPath Function
|
||||
; - Adds item to Path for All Users
|
||||
; - Overcomes NSIS ReadRegStr limitation of 1024 characters by using Native
|
||||
; Windows Commands
|
||||
;
|
||||
; Usage:
|
||||
; Push "C:\path\to\add"
|
||||
; Call AddToPath
|
||||
;------------------------------------------------------------------------------
|
||||
#------------------------------------------------------------------------------
|
||||
# AddToPath Function
|
||||
# - Adds item to Path for All Users
|
||||
# - Overcomes NSIS ReadRegStr limitation of 1024 characters by using Native
|
||||
# Windows Commands
|
||||
#
|
||||
# Usage:
|
||||
# Push "C:\path\to\add"
|
||||
# Call AddToPath
|
||||
#------------------------------------------------------------------------------
|
||||
!define Environ 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"'
|
||||
Function AddToPath
|
||||
|
||||
Exch $0 ; Path to add
|
||||
Push $1 ; Current Path
|
||||
Push $2 ; Results of StrStr / Length of Path + Path to Add
|
||||
Push $3 ; Handle to Reg / Length of Path
|
||||
Push $4 ; Result of Registry Call
|
||||
Exch $0 # Path to add
|
||||
Push $1 # Current Path
|
||||
Push $2 # Results of StrStr / Length of Path + Path to Add
|
||||
Push $3 # Handle to Reg / Length of Path
|
||||
Push $4 # Result of Registry Call
|
||||
|
||||
; Open a handle to the key in the registry, handle in $3, Error in $4
|
||||
# Open a handle to the key in the registry, handle in $3, Error in $4
|
||||
System::Call "advapi32::RegOpenKey(i 0x80000002, t'SYSTEM\CurrentControlSet\Control\Session Manager\Environment', *i.r3) i.r4"
|
||||
; Make sure registry handle opened successfully (returned 0)
|
||||
# Make sure registry handle opened successfully (returned 0)
|
||||
IntCmp $4 0 0 done done
|
||||
|
||||
; Load the contents of path into $1, Error Code into $4, Path length into $2
|
||||
# Load the contents of path into $1, Error Code into $4, Path length into $2
|
||||
System::Call "advapi32::RegQueryValueEx(i $3, t'PATH', i 0, i 0, t.r1, *i ${NSIS_MAX_STRLEN} r2) i.r4"
|
||||
|
||||
; Close the handle to the registry ($3)
|
||||
# Close the handle to the registry ($3)
|
||||
System::Call "advapi32::RegCloseKey(i $3)"
|
||||
|
||||
; Check for Error Code 234, Path too long for the variable
|
||||
IntCmp $4 234 0 +4 +4 ; $4 == ERROR_MORE_DATA
|
||||
# Check for Error Code 234, Path too long for the variable
|
||||
IntCmp $4 234 0 +4 +4 # $4 == ERROR_MORE_DATA
|
||||
DetailPrint "AddToPath Failed: original length $2 > ${NSIS_MAX_STRLEN}"
|
||||
MessageBox MB_OK \
|
||||
"You may add C:\salt to the %PATH% for convenience when issuing local salt commands from the command line." \
|
||||
/SD IDOK
|
||||
Goto done
|
||||
|
||||
; If no error, continue
|
||||
IntCmp $4 0 +5 ; $4 != NO_ERROR
|
||||
; Error 2 means the Key was not found
|
||||
IntCmp $4 2 +3 ; $4 != ERROR_FILE_NOT_FOUND
|
||||
# If no error, continue
|
||||
IntCmp $4 0 +5 # $4 != NO_ERROR
|
||||
# Error 2 means the Key was not found
|
||||
IntCmp $4 2 +3 # $4 != ERROR_FILE_NOT_FOUND
|
||||
DetailPrint "AddToPath: unexpected error code $4"
|
||||
Goto done
|
||||
StrCpy $1 ""
|
||||
|
||||
; Check if already in PATH
|
||||
Push "$1;" ; The string to search
|
||||
Push "$0;" ; The string to find
|
||||
# Check if already in PATH
|
||||
Push "$1;" # The string to search
|
||||
Push "$0;" # The string to find
|
||||
Call StrStr
|
||||
Pop $2 ; The result of the search
|
||||
StrCmp $2 "" 0 done ; String not found, try again with ';' at the end
|
||||
; Otherwise, it's already in the path
|
||||
Push "$1;" ; The string to search
|
||||
Push "$0\;" ; The string to find
|
||||
Pop $2 # The result of the search
|
||||
StrCmp $2 "" 0 done # String not found, try again with ';' at the end
|
||||
# Otherwise, it's already in the path
|
||||
Push "$1;" # The string to search
|
||||
Push "$0\;" # The string to find
|
||||
Call StrStr
|
||||
Pop $2 ; The result
|
||||
StrCmp $2 "" 0 done ; String not found, continue (add)
|
||||
; Otherwise, it's already in the path
|
||||
Pop $2 # The result
|
||||
StrCmp $2 "" 0 done # String not found, continue (add)
|
||||
# Otherwise, it's already in the path
|
||||
|
||||
; Prevent NSIS string overflow
|
||||
StrLen $2 $0 ; Length of path to add ($2)
|
||||
StrLen $3 $1 ; Length of current path ($3)
|
||||
IntOp $2 $2 + $3 ; Length of current path + path to add ($2)
|
||||
IntOp $2 $2 + 2 ; Account for the additional ';'
|
||||
; $2 = strlen(dir) + strlen(PATH) + sizeof(";")
|
||||
# Prevent NSIS string overflow
|
||||
StrLen $2 $0 # Length of path to add ($2)
|
||||
StrLen $3 $1 # Length of current path ($3)
|
||||
IntOp $2 $2 + $3 # Length of current path + path to add ($2)
|
||||
IntOp $2 $2 + 2 # Account for the additional ';'
|
||||
# $2 = strlen(dir) + strlen(PATH) + sizeof(";")
|
||||
|
||||
; Make sure the new length isn't over the NSIS_MAX_STRLEN
|
||||
# Make sure the new length isn't over the NSIS_MAX_STRLEN
|
||||
IntCmp $2 ${NSIS_MAX_STRLEN} +4 +4 0
|
||||
DetailPrint "AddToPath: new length $2 > ${NSIS_MAX_STRLEN}"
|
||||
MessageBox MB_OK \
|
||||
|
@ -678,18 +700,18 @@ Function AddToPath
|
|||
/SD IDOK
|
||||
Goto done
|
||||
|
||||
; Append dir to PATH
|
||||
# Append dir to PATH
|
||||
DetailPrint "Add to PATH: $0"
|
||||
StrCpy $2 $1 1 -1 ; Copy the last character of the existing path
|
||||
StrCmp $2 ";" 0 +2 ; Check for trailing ';'
|
||||
StrCpy $1 $1 -1 ; remove trailing ';'
|
||||
StrCmp $1 "" +2 ; Make sure Path is not empty
|
||||
StrCpy $0 "$1;$0" ; Append new path at the end ($0)
|
||||
StrCpy $2 $1 1 -1 # Copy the last character of the existing path
|
||||
StrCmp $2 ";" 0 +2 # Check for trailing ';'
|
||||
StrCpy $1 $1 -1 # remove trailing ';'
|
||||
StrCmp $1 "" +2 # Make sure Path is not empty
|
||||
StrCpy $0 "$1;$0" # Append new path at the end ($0)
|
||||
|
||||
; We can use the NSIS command here. Only 'ReadRegStr' is affected
|
||||
# We can use the NSIS command here. Only 'ReadRegStr' is affected
|
||||
WriteRegExpandStr ${Environ} "PATH" $0
|
||||
|
||||
; Broadcast registry change to open programs
|
||||
# Broadcast registry change to open programs
|
||||
SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
|
||||
|
||||
done:
|
||||
|
@ -702,16 +724,16 @@ Function AddToPath
|
|||
FunctionEnd
|
||||
|
||||
|
||||
;------------------------------------------------------------------------------
|
||||
; RemoveFromPath Function
|
||||
; - Removes item from Path for All Users
|
||||
; - Overcomes NSIS ReadRegStr limitation of 1024 characters by using Native
|
||||
; Windows Commands
|
||||
;
|
||||
; Usage:
|
||||
; Push "C:\path\to\add"
|
||||
; Call un.RemoveFromPath
|
||||
;------------------------------------------------------------------------------
|
||||
#------------------------------------------------------------------------------
|
||||
# RemoveFromPath Function
|
||||
# - Removes item from Path for All Users
|
||||
# - Overcomes NSIS ReadRegStr limitation of 1024 characters by using Native
|
||||
# Windows Commands
|
||||
#
|
||||
# Usage:
|
||||
# Push "C:\path\to\add"
|
||||
# Call un.RemoveFromPath
|
||||
#------------------------------------------------------------------------------
|
||||
Function un.RemoveFromPath
|
||||
|
||||
Exch $0
|
||||
|
@ -722,59 +744,59 @@ Function un.RemoveFromPath
|
|||
Push $5
|
||||
Push $6
|
||||
|
||||
; Open a handle to the key in the registry, handle in $3, Error in $4
|
||||
# Open a handle to the key in the registry, handle in $3, Error in $4
|
||||
System::Call "advapi32::RegOpenKey(i 0x80000002, t'SYSTEM\CurrentControlSet\Control\Session Manager\Environment', *i.r3) i.r4"
|
||||
; Make sure registry handle opened successfully (returned 0)
|
||||
# Make sure registry handle opened successfully (returned 0)
|
||||
IntCmp $4 0 0 done done
|
||||
|
||||
; Load the contents of path into $1, Error Code into $4, Path length into $2
|
||||
# Load the contents of path into $1, Error Code into $4, Path length into $2
|
||||
System::Call "advapi32::RegQueryValueEx(i $3, t'PATH', i 0, i 0, t.r1, *i ${NSIS_MAX_STRLEN} r2) i.r4"
|
||||
|
||||
; Close the handle to the registry ($3)
|
||||
# Close the handle to the registry ($3)
|
||||
System::Call "advapi32::RegCloseKey(i $3)"
|
||||
|
||||
; Check for Error Code 234, Path too long for the variable
|
||||
IntCmp $4 234 0 +4 +4 ; $4 == ERROR_MORE_DATA
|
||||
# Check for Error Code 234, Path too long for the variable
|
||||
IntCmp $4 234 0 +4 +4 # $4 == ERROR_MORE_DATA
|
||||
DetailPrint "AddToPath: original length $2 > ${NSIS_MAX_STRLEN}"
|
||||
Goto done
|
||||
|
||||
; If no error, continue
|
||||
IntCmp $4 0 +5 ; $4 != NO_ERROR
|
||||
; Error 2 means the Key was not found
|
||||
IntCmp $4 2 +3 ; $4 != ERROR_FILE_NOT_FOUND
|
||||
# If no error, continue
|
||||
IntCmp $4 0 +5 # $4 != NO_ERROR
|
||||
# Error 2 means the Key was not found
|
||||
IntCmp $4 2 +3 # $4 != ERROR_FILE_NOT_FOUND
|
||||
DetailPrint "AddToPath: unexpected error code $4"
|
||||
Goto done
|
||||
StrCpy $1 ""
|
||||
|
||||
; Ensure there's a trailing ';'
|
||||
StrCpy $5 $1 1 -1 ; Copy the last character of the path
|
||||
StrCmp $5 ";" +2 ; Check for trailing ';', if found continue
|
||||
StrCpy $1 "$1;" ; ensure trailing ';'
|
||||
# Ensure there's a trailing ';'
|
||||
StrCpy $5 $1 1 -1 # Copy the last character of the path
|
||||
StrCmp $5 ";" +2 # Check for trailing ';', if found continue
|
||||
StrCpy $1 "$1;" # ensure trailing ';'
|
||||
|
||||
; Check for our directory inside the path
|
||||
Push $1 ; String to Search
|
||||
Push "$0;" ; Dir to Find
|
||||
# Check for our directory inside the path
|
||||
Push $1 # String to Search
|
||||
Push "$0;" # Dir to Find
|
||||
Call un.StrStr
|
||||
Pop $2 ; The results of the search
|
||||
StrCmp $2 "" done ; If results are empty, we're done, otherwise continue
|
||||
Pop $2 # The results of the search
|
||||
StrCmp $2 "" done # If results are empty, we're done, otherwise continue
|
||||
|
||||
; Remove our Directory from the Path
|
||||
# Remove our Directory from the Path
|
||||
DetailPrint "Remove from PATH: $0"
|
||||
StrLen $3 "$0;" ; Get the length of our dir ($3)
|
||||
StrLen $4 $2 ; Get the length of the return from StrStr ($4)
|
||||
StrCpy $5 $1 -$4 ; $5 is now the part before the path to remove
|
||||
StrCpy $6 $2 "" $3 ; $6 is now the part after the path to remove
|
||||
StrCpy $3 "$5$6" ; Combine $5 and $6
|
||||
StrLen $3 "$0;" # Get the length of our dir ($3)
|
||||
StrLen $4 $2 # Get the length of the return from StrStr ($4)
|
||||
StrCpy $5 $1 -$4 # $5 is now the part before the path to remove
|
||||
StrCpy $6 $2 "" $3 # $6 is now the part after the path to remove
|
||||
StrCpy $3 "$5$6" # Combine $5 and $6
|
||||
|
||||
; Check for Trailing ';'
|
||||
StrCpy $5 $3 1 -1 ; Load the last character of the string
|
||||
StrCmp $5 ";" 0 +2 ; Check for ';'
|
||||
StrCpy $3 $3 -1 ; remove trailing ';'
|
||||
# Check for Trailing ';'
|
||||
StrCpy $5 $3 1 -1 # Load the last character of the string
|
||||
StrCmp $5 ";" 0 +2 # Check for ';'
|
||||
StrCpy $3 $3 -1 # remove trailing ';'
|
||||
|
||||
; Write the new path to the registry
|
||||
# Write the new path to the registry
|
||||
WriteRegExpandStr ${Environ} "PATH" $3
|
||||
|
||||
; Broadcast the change to all open applications
|
||||
# Broadcast the change to all open applications
|
||||
SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
|
||||
|
||||
done:
|
||||
|
@ -808,6 +830,7 @@ Function getMinionConfig
|
|||
confFound:
|
||||
FileOpen $0 "$INSTDIR\conf\minion" r
|
||||
|
||||
ClearErrors
|
||||
confLoop:
|
||||
FileRead $0 $1
|
||||
IfErrors EndOfFile
|
||||
|
@ -838,68 +861,69 @@ FunctionEnd
|
|||
Function updateMinionConfig
|
||||
|
||||
ClearErrors
|
||||
FileOpen $0 "$INSTDIR\conf\minion" "r" ; open target file for reading
|
||||
GetTempFileName $R0 ; get new temp file name
|
||||
FileOpen $1 $R0 "w" ; open temp file for writing
|
||||
FileOpen $0 "$INSTDIR\conf\minion" "r" # open target file for reading
|
||||
GetTempFileName $R0 # get new temp file name
|
||||
FileOpen $1 $R0 "w" # open temp file for writing
|
||||
|
||||
loop: ; loop through each line
|
||||
FileRead $0 $2 ; read line from target file
|
||||
IfErrors done ; end if errors are encountered (end of line)
|
||||
loop: # loop through each line
|
||||
FileRead $0 $2 # read line from target file
|
||||
IfErrors done # end if errors are encountered (end of line)
|
||||
|
||||
${If} $MasterHost_State != "" ; if master is empty
|
||||
${AndIf} $MasterHost_State != "salt" ; and if master is not 'salt'
|
||||
${StrLoc} $3 $2 "master:" ">" ; where is 'master:' in this line
|
||||
${If} $3 == 0 ; is it in the first...
|
||||
${OrIf} $3 == 1 ; or second position (account for comments)
|
||||
StrCpy $2 "master: $MasterHost_State$\r$\n" ; write the master
|
||||
${EndIf} ; close if statement
|
||||
${EndIf} ; close if statement
|
||||
${If} $MasterHost_State != "" # if master is empty
|
||||
${AndIf} $MasterHost_State != "salt" # and if master is not 'salt'
|
||||
${StrLoc} $3 $2 "master:" ">" # where is 'master:' in this line
|
||||
${If} $3 == 0 # is it in the first...
|
||||
${OrIf} $3 == 1 # or second position (account for comments)
|
||||
StrCpy $2 "master: $MasterHost_State$\r$\n" # write the master
|
||||
${EndIf} # close if statement
|
||||
${EndIf} # close if statement
|
||||
|
||||
${If} $MinionName_State != "" ; if minion is empty
|
||||
${AndIf} $MinionName_State != "hostname" ; and if minion is not 'hostname'
|
||||
${StrLoc} $3 $2 "id:" ">" ; where is 'id:' in this line
|
||||
${If} $3 == 0 ; is it in the first...
|
||||
${OrIf} $3 == 1 ; or the second position (account for comments)
|
||||
StrCpy $2 "id: $MinionName_State$\r$\n" ; change line
|
||||
${EndIf} ; close if statement
|
||||
${EndIf} ; close if statement
|
||||
${If} $MinionName_State != "" # if minion is empty
|
||||
${AndIf} $MinionName_State != "hostname" # and if minion is not 'hostname'
|
||||
${StrLoc} $3 $2 "id:" ">" # where is 'id:' in this line
|
||||
${If} $3 == 0 # is it in the first...
|
||||
${OrIf} $3 == 1 # or the second position (account for comments)
|
||||
StrCpy $2 "id: $MinionName_State$\r$\n" # change line
|
||||
${EndIf} # close if statement
|
||||
${EndIf} # close if statement
|
||||
|
||||
FileWrite $1 $2 ; write changed or unchanged line to temp file
|
||||
FileWrite $1 $2 # write changed or unchanged line to temp file
|
||||
Goto loop
|
||||
|
||||
done:
|
||||
FileClose $0 ; close target file
|
||||
FileClose $1 ; close temp file
|
||||
Delete "$INSTDIR\conf\minion" ; delete target file
|
||||
CopyFiles /SILENT $R0 "$INSTDIR\conf\minion" ; copy temp file to target file
|
||||
Delete $R0 ; delete temp file
|
||||
FileClose $0 # close target file
|
||||
FileClose $1 # close temp file
|
||||
Delete "$INSTDIR\conf\minion" # delete target file
|
||||
CopyFiles /SILENT $R0 "$INSTDIR\conf\minion" # copy temp file to target file
|
||||
Delete $R0 # delete temp file
|
||||
|
||||
FunctionEnd
|
||||
|
||||
|
||||
Function parseCommandLineSwitches
|
||||
|
||||
; Load the parameters
|
||||
# Load the parameters
|
||||
${GetParameters} $R0
|
||||
|
||||
; Check for start-minion switches
|
||||
; /start-service is to be deprecated, so we must check for both
|
||||
# Check for start-minion switches
|
||||
# /start-service is to be deprecated, so we must check for both
|
||||
${GetOptions} $R0 "/start-service=" $R1
|
||||
${GetOptions} $R0 "/start-minion=" $R2
|
||||
|
||||
# Service: Start Salt Minion
|
||||
${IfNot} $R2 == ""
|
||||
; If start-minion was passed something, then set it
|
||||
# If start-minion was passed something, then set it
|
||||
StrCpy $StartMinion $R2
|
||||
${ElseIfNot} $R1 == ""
|
||||
; If start-service was passed something, then set StartMinion to that
|
||||
# If start-service was passed something, then set StartMinion to that
|
||||
StrCpy $StartMinion $R1
|
||||
${Else}
|
||||
; Otherwise default to 1
|
||||
# Otherwise default to 1
|
||||
StrCpy $StartMinion 1
|
||||
${EndIf}
|
||||
|
||||
# Service: Minion Startup Type Delayed
|
||||
ClearErrors
|
||||
${GetOptions} $R0 "/start-minion-delayed" $R1
|
||||
IfErrors start_minion_delayed_not_found
|
||||
StrCpy $StartMinionDelayed 1
|
||||
|
|
|
@ -19,9 +19,9 @@ Function Get-Settings {
|
|||
"Python2Dir" = "C:\Python27"
|
||||
"Scripts2Dir" = "C:\Python27\Scripts"
|
||||
"SitePkgs2Dir" = "C:\Python27\Lib\site-packages"
|
||||
"Python3Dir" = "C:\Program Files\Python35"
|
||||
"Scripts3Dir" = "C:\Program Files\Python35\Scripts"
|
||||
"SitePkgs3Dir" = "C:\Program Files\Python35\Lib\site-packages"
|
||||
"Python3Dir" = "C:\Python35"
|
||||
"Scripts3Dir" = "C:\Python35\Scripts"
|
||||
"SitePkgs3Dir" = "C:\Python35\Lib\site-packages"
|
||||
"DownloadDir" = "$env:Temp\DevSalt"
|
||||
}
|
||||
# The script deletes the DownLoadDir (above) for each install.
|
||||
|
|
|
@ -377,46 +377,13 @@ class LoadAuth(object):
|
|||
eauth_config = self.opts['external_auth'][eauth]
|
||||
if not groups:
|
||||
groups = []
|
||||
group_perm_keys = [item for item in eauth_config if item.endswith('%')] # The configured auth groups
|
||||
|
||||
# First we need to know if the user is allowed to proceed via any of their group memberships.
|
||||
group_auth_match = False
|
||||
for group_config in group_perm_keys:
|
||||
if group_config.rstrip('%') in groups:
|
||||
group_auth_match = True
|
||||
break
|
||||
# If a group_auth_match is set it means only that we have a
|
||||
# user which matches at least one or more of the groups defined
|
||||
# in the configuration file.
|
||||
|
||||
external_auth_in_db = False
|
||||
for entry in eauth_config:
|
||||
if entry.startswith('^'):
|
||||
external_auth_in_db = True
|
||||
break
|
||||
|
||||
# If neither a catchall, a named membership or a group
|
||||
# membership is found, there is no need to continue. Simply
|
||||
# deny the user access.
|
||||
if not ((name in eauth_config) |
|
||||
('*' in eauth_config) |
|
||||
group_auth_match | external_auth_in_db):
|
||||
# Auth successful, but no matching user found in config
|
||||
log.warning('Authorization failure occurred.')
|
||||
return None
|
||||
|
||||
# We now have an authenticated session and it is time to determine
|
||||
# what the user has access to.
|
||||
auth_list = []
|
||||
if name in eauth_config:
|
||||
auth_list = eauth_config[name]
|
||||
elif '*' in eauth_config:
|
||||
auth_list = eauth_config['*']
|
||||
if group_auth_match:
|
||||
auth_list = self.ckminions.fill_auth_list_from_groups(
|
||||
eauth_config,
|
||||
groups,
|
||||
auth_list)
|
||||
auth_list = self.ckminions.fill_auth_list(
|
||||
eauth_config,
|
||||
name,
|
||||
groups)
|
||||
|
||||
auth_list = self.__process_acl(load, auth_list)
|
||||
|
||||
|
|
|
@ -110,6 +110,10 @@ class _LDAPConnection(object):
|
|||
self.ldap.set_option(ldap.OPT_REFERRALS, 0) # Needed for AD
|
||||
|
||||
if not anonymous:
|
||||
if self.bindpw is None or len(self.bindpw) < 1:
|
||||
raise CommandExecutionError(
|
||||
'LDAP bind password is not set: password cannot be empty if auth.ldap.anonymous is False'
|
||||
)
|
||||
self.ldap.simple_bind_s(self.binddn, self.bindpw)
|
||||
except Exception as ldap_error:
|
||||
raise CommandExecutionError(
|
||||
|
|
|
@ -186,19 +186,60 @@ class Beacon(object):
|
|||
else:
|
||||
self.opts['beacons'][name].append({'enabled': enabled_value})
|
||||
|
||||
def list_beacons(self):
|
||||
def _get_beacons(self,
|
||||
include_opts=True,
|
||||
include_pillar=True):
|
||||
'''
|
||||
Return the beacons data structure
|
||||
'''
|
||||
beacons = {}
|
||||
if include_pillar:
|
||||
pillar_beacons = self.opts.get('pillar', {}).get('beacons', {})
|
||||
if not isinstance(pillar_beacons, dict):
|
||||
raise ValueError('Beacons must be of type dict.')
|
||||
beacons.update(pillar_beacons)
|
||||
if include_opts:
|
||||
opts_beacons = self.opts.get('beacons', {})
|
||||
if not isinstance(opts_beacons, dict):
|
||||
raise ValueError('Beacons must be of type dict.')
|
||||
beacons.update(opts_beacons)
|
||||
return beacons
|
||||
|
||||
def list_beacons(self,
|
||||
include_pillar=True,
|
||||
include_opts=True):
|
||||
'''
|
||||
List the beacon items
|
||||
|
||||
include_pillar: Whether to include beacons that are
|
||||
configured in pillar, default is True.
|
||||
|
||||
include_opts: Whether to include beacons that are
|
||||
configured in opts, default is True.
|
||||
'''
|
||||
beacons = self._get_beacons(include_pillar, include_opts)
|
||||
|
||||
# Fire the complete event back along with the list of beacons
|
||||
evt = salt.utils.event.get_event('minion', opts=self.opts)
|
||||
b_conf = self.functions['config.merge']('beacons')
|
||||
self.opts['beacons'].update(b_conf)
|
||||
evt.fire_event({'complete': True, 'beacons': self.opts['beacons']},
|
||||
evt.fire_event({'complete': True, 'beacons': beacons},
|
||||
tag='/salt/minion/minion_beacons_list_complete')
|
||||
|
||||
return True
|
||||
|
||||
def list_available_beacons(self):
|
||||
'''
|
||||
List the available beacons
|
||||
'''
|
||||
_beacons = ['{0}'.format(_beacon.replace('.beacon', ''))
|
||||
for _beacon in self.beacons if '.beacon' in _beacon]
|
||||
|
||||
# Fire the complete event back along with the list of beacons
|
||||
evt = salt.utils.event.get_event('minion', opts=self.opts)
|
||||
evt.fire_event({'complete': True, 'beacons': _beacons},
|
||||
tag='/salt/minion/minion_beacons_list_available_complete')
|
||||
|
||||
return True
|
||||
|
||||
def add_beacon(self, name, beacon_data):
|
||||
'''
|
||||
Add a beacon item
|
||||
|
@ -207,16 +248,23 @@ class Beacon(object):
|
|||
data = {}
|
||||
data[name] = beacon_data
|
||||
|
||||
if name in self.opts['beacons']:
|
||||
log.info('Updating settings for beacon '
|
||||
'item: {0}'.format(name))
|
||||
if name in self._get_beacons(include_opts=False):
|
||||
comment = 'Cannot update beacon item {0}, ' \
|
||||
'because it is configured in pillar.'.format(name)
|
||||
complete = False
|
||||
else:
|
||||
log.info('Added new beacon item {0}'.format(name))
|
||||
self.opts['beacons'].update(data)
|
||||
if name in self.opts['beacons']:
|
||||
comment = 'Updating settings for beacon ' \
|
||||
'item: {0}'.format(name)
|
||||
else:
|
||||
comment = 'Added new beacon item: {0}'.format(name)
|
||||
complete = True
|
||||
self.opts['beacons'].update(data)
|
||||
|
||||
# Fire the complete event back along with updated list of beacons
|
||||
evt = salt.utils.event.get_event('minion', opts=self.opts)
|
||||
evt.fire_event({'complete': True, 'beacons': self.opts['beacons']},
|
||||
evt.fire_event({'complete': complete, 'comment': comment,
|
||||
'beacons': self.opts['beacons']},
|
||||
tag='/salt/minion/minion_beacon_add_complete')
|
||||
|
||||
return True
|
||||
|
@ -229,15 +277,21 @@ class Beacon(object):
|
|||
data = {}
|
||||
data[name] = beacon_data
|
||||
|
||||
log.info('Updating settings for beacon '
|
||||
'item: {0}'.format(name))
|
||||
self.opts['beacons'].update(data)
|
||||
if name in self._get_beacons(include_opts=False):
|
||||
comment = 'Cannot modify beacon item {0}, ' \
|
||||
'it is configured in pillar.'.format(name)
|
||||
complete = False
|
||||
else:
|
||||
comment = 'Updating settings for beacon ' \
|
||||
'item: {0}'.format(name)
|
||||
complete = True
|
||||
self.opts['beacons'].update(data)
|
||||
|
||||
# Fire the complete event back along with updated list of beacons
|
||||
evt = salt.utils.event.get_event('minion', opts=self.opts)
|
||||
evt.fire_event({'complete': True, 'beacons': self.opts['beacons']},
|
||||
evt.fire_event({'complete': complete, 'comment': comment,
|
||||
'beacons': self.opts['beacons']},
|
||||
tag='/salt/minion/minion_beacon_modify_complete')
|
||||
|
||||
return True
|
||||
|
||||
def delete_beacon(self, name):
|
||||
|
@ -245,13 +299,22 @@ class Beacon(object):
|
|||
Delete a beacon item
|
||||
'''
|
||||
|
||||
if name in self.opts['beacons']:
|
||||
log.info('Deleting beacon item {0}'.format(name))
|
||||
del self.opts['beacons'][name]
|
||||
if name in self._get_beacons(include_opts=False):
|
||||
comment = 'Cannot delete beacon item {0}, ' \
|
||||
'it is configured in pillar.'.format(name)
|
||||
complete = False
|
||||
else:
|
||||
if name in self.opts['beacons']:
|
||||
del self.opts['beacons'][name]
|
||||
comment = 'Deleting beacon item: {0}'.format(name)
|
||||
else:
|
||||
comment = 'Beacon item {0} not found.'.format(name)
|
||||
complete = True
|
||||
|
||||
# Fire the complete event back along with updated list of beacons
|
||||
evt = salt.utils.event.get_event('minion', opts=self.opts)
|
||||
evt.fire_event({'complete': True, 'beacons': self.opts['beacons']},
|
||||
evt.fire_event({'complete': complete, 'comment': comment,
|
||||
'beacons': self.opts['beacons']},
|
||||
tag='/salt/minion/minion_beacon_delete_complete')
|
||||
|
||||
return True
|
||||
|
@ -289,11 +352,19 @@ class Beacon(object):
|
|||
Enable a beacon
|
||||
'''
|
||||
|
||||
self._update_enabled(name, True)
|
||||
if name in self._get_beacons(include_opts=False):
|
||||
comment = 'Cannot enable beacon item {0}, ' \
|
||||
'it is configured in pillar.'.format(name)
|
||||
complete = False
|
||||
else:
|
||||
self._update_enabled(name, True)
|
||||
comment = 'Enabling beacon item {0}'.format(name)
|
||||
complete = True
|
||||
|
||||
# Fire the complete event back along with updated list of beacons
|
||||
evt = salt.utils.event.get_event('minion', opts=self.opts)
|
||||
evt.fire_event({'complete': True, 'beacons': self.opts['beacons']},
|
||||
evt.fire_event({'complete': complete, 'comment': comment,
|
||||
'beacons': self.opts['beacons']},
|
||||
tag='/salt/minion/minion_beacon_enabled_complete')
|
||||
|
||||
return True
|
||||
|
@ -303,11 +374,19 @@ class Beacon(object):
|
|||
Disable a beacon
|
||||
'''
|
||||
|
||||
self._update_enabled(name, False)
|
||||
if name in self._get_beacons(include_opts=False):
|
||||
comment = 'Cannot disable beacon item {0}, ' \
|
||||
'it is configured in pillar.'.format(name)
|
||||
complete = False
|
||||
else:
|
||||
self._update_enabled(name, False)
|
||||
comment = 'Disabling beacon item {0}'.format(name)
|
||||
complete = True
|
||||
|
||||
# Fire the complete event back along with updated list of beacons
|
||||
evt = salt.utils.event.get_event('minion', opts=self.opts)
|
||||
evt.fire_event({'complete': True, 'beacons': self.opts['beacons']},
|
||||
evt.fire_event({'complete': complete, 'comment': comment,
|
||||
'beacons': self.opts['beacons']},
|
||||
tag='/salt/minion/minion_beacon_disabled_complete')
|
||||
|
||||
return True
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
Beacon to monitor temperature, humidity and pressure using the SenseHat
|
||||
of a Raspberry Pi.
|
||||
|
||||
.. versionadded:: 2017.7.0
|
||||
|
||||
:maintainer: Benedikt Werner <1benediktwerner@gmail.com>
|
||||
:maturity: new
|
||||
:depends: sense_hat Python module
|
||||
|
|
2
salt/cache/__init__.py
vendored
2
salt/cache/__init__.py
vendored
|
@ -73,7 +73,7 @@ class Cache(object):
|
|||
self.cachedir = opts.get('cachedir', salt.syspaths.CACHE_DIR)
|
||||
else:
|
||||
self.cachedir = cachedir
|
||||
self.driver = opts.get('cache', salt.config.DEFAULT_MASTER_OPTS)
|
||||
self.driver = opts.get('cache', salt.config.DEFAULT_MASTER_OPTS['cache'])
|
||||
self.serial = Serial(opts)
|
||||
self._modules = None
|
||||
self._kwargs = kwargs
|
||||
|
|
9
salt/cache/consul.py
vendored
9
salt/cache/consul.py
vendored
|
@ -4,6 +4,8 @@ Minion data cache plugin for Consul key/value data store.
|
|||
|
||||
.. versionadded:: 2016.11.2
|
||||
|
||||
:depends: python-consul >= 0.2.0
|
||||
|
||||
It is up to the system administrator to set up and configure the Consul
|
||||
infrastructure. All is needed for this plugin is a working Consul agent
|
||||
with a read-write access to the key-value store.
|
||||
|
@ -81,8 +83,11 @@ def __virtual__():
|
|||
'verify': __opts__.get('consul.verify', True),
|
||||
}
|
||||
|
||||
global api
|
||||
api = consul.Consul(**consul_kwargs)
|
||||
try:
|
||||
global api
|
||||
api = consul.Consul(**consul_kwargs)
|
||||
except AttributeError:
|
||||
return (False, "Failed to invoke consul.Consul, please make sure you have python-consul >= 0.2.0 installed")
|
||||
|
||||
return __virtualname__
|
||||
|
||||
|
|
21
salt/cache/redis_cache.py
vendored
21
salt/cache/redis_cache.py
vendored
|
@ -421,18 +421,17 @@ def list_(bank):
|
|||
Lists entries stored in the specified bank.
|
||||
'''
|
||||
redis_server = _get_redis_server()
|
||||
bank_keys_redis_key = _get_bank_keys_redis_key(bank)
|
||||
bank_keys = None
|
||||
bank_redis_key = _get_bank_redis_key(bank)
|
||||
try:
|
||||
bank_keys = redis_server.smembers(bank_keys_redis_key)
|
||||
banks = redis_server.smembers(bank_redis_key)
|
||||
except (RedisConnectionError, RedisResponseError) as rerr:
|
||||
mesg = 'Cannot list the Redis cache key {rkey}: {rerr}'.format(rkey=bank_keys_redis_key,
|
||||
mesg = 'Cannot list the Redis cache key {rkey}: {rerr}'.format(rkey=bank_redis_key,
|
||||
rerr=rerr)
|
||||
log.error(mesg)
|
||||
raise SaltCacheError(mesg)
|
||||
if not bank_keys:
|
||||
if not banks:
|
||||
return []
|
||||
return list(bank_keys)
|
||||
return list(banks)
|
||||
|
||||
|
||||
def contains(bank, key):
|
||||
|
@ -440,15 +439,11 @@ def contains(bank, key):
|
|||
Checks if the specified bank contains the specified key.
|
||||
'''
|
||||
redis_server = _get_redis_server()
|
||||
bank_keys_redis_key = _get_bank_keys_redis_key(bank)
|
||||
bank_keys = None
|
||||
bank_redis_key = _get_bank_redis_key(bank)
|
||||
try:
|
||||
bank_keys = redis_server.smembers(bank_keys_redis_key)
|
||||
return redis_server.sismember(bank_redis_key, key)
|
||||
except (RedisConnectionError, RedisResponseError) as rerr:
|
||||
mesg = 'Cannot retrieve the Redis cache key {rkey}: {rerr}'.format(rkey=bank_keys_redis_key,
|
||||
mesg = 'Cannot retrieve the Redis cache key {rkey}: {rerr}'.format(rkey=bank_redis_key,
|
||||
rerr=rerr)
|
||||
log.error(mesg)
|
||||
raise SaltCacheError(mesg)
|
||||
if not bank_keys:
|
||||
return False
|
||||
return key in bank_keys
|
||||
|
|
|
@ -140,10 +140,11 @@ class Batch(object):
|
|||
# sure that the main while loop finishes even with unresp minions
|
||||
minion_tracker = {}
|
||||
|
||||
# We already know some minions didn't respond to the ping, so inform
|
||||
# the user we won't be attempting to run a job on them
|
||||
for down_minion in self.down_minions:
|
||||
print_cli('Minion {0} did not respond. No job will be sent.'.format(down_minion))
|
||||
if not self.quiet:
|
||||
# We already know some minions didn't respond to the ping, so inform
|
||||
# the user we won't be attempting to run a job on them
|
||||
for down_minion in self.down_minions:
|
||||
print_cli('Minion {0} did not respond. No job will be sent.'.format(down_minion))
|
||||
|
||||
# Iterate while we still have things to execute
|
||||
while len(ret) < len(self.minions):
|
||||
|
|
|
@ -160,6 +160,7 @@ class Master(parsers.MasterOptionParser, DaemonsMixin): # pylint: disable=no-in
|
|||
self.config['user'],
|
||||
permissive=self.config['permissive_pki_access'],
|
||||
pki_dir=self.config['pki_dir'],
|
||||
root_dir=self.config['root_dir'],
|
||||
)
|
||||
# Clear out syndics from cachedir
|
||||
for syndic_file in os.listdir(self.config['syndic_dir']):
|
||||
|
@ -280,6 +281,7 @@ class Minion(parsers.MinionOptionParser, DaemonsMixin): # pylint: disable=no-in
|
|||
self.config['user'],
|
||||
permissive=self.config['permissive_pki_access'],
|
||||
pki_dir=self.config['pki_dir'],
|
||||
root_dir=self.config['root_dir'],
|
||||
)
|
||||
except OSError as error:
|
||||
self.environment_failure(error)
|
||||
|
@ -467,6 +469,7 @@ class ProxyMinion(parsers.ProxyMinionOptionParser, DaemonsMixin): # pylint: dis
|
|||
self.config['user'],
|
||||
permissive=self.config['permissive_pki_access'],
|
||||
pki_dir=self.config['pki_dir'],
|
||||
root_dir=self.config['root_dir'],
|
||||
)
|
||||
except OSError as error:
|
||||
self.environment_failure(error)
|
||||
|
@ -575,6 +578,7 @@ class Syndic(parsers.SyndicOptionParser, DaemonsMixin): # pylint: disable=no-in
|
|||
self.config['user'],
|
||||
permissive=self.config['permissive_pki_access'],
|
||||
pki_dir=self.config['pki_dir'],
|
||||
root_dir=self.config['root_dir'],
|
||||
)
|
||||
except OSError as error:
|
||||
self.environment_failure(error)
|
||||
|
|
|
@ -14,7 +14,7 @@ from __future__ import absolute_import
|
|||
# Import Salt libs
|
||||
import salt.spm
|
||||
import salt.utils.parsers as parsers
|
||||
from salt.utils.verify import verify_log
|
||||
from salt.utils.verify import verify_log, verify_env
|
||||
|
||||
|
||||
class SPM(parsers.SPMParser):
|
||||
|
@ -29,6 +29,13 @@ class SPM(parsers.SPMParser):
|
|||
ui = salt.spm.SPMCmdlineInterface()
|
||||
self.parse_args()
|
||||
self.setup_logfile_logger()
|
||||
v_dirs = [
|
||||
self.config['cachedir'],
|
||||
]
|
||||
verify_env(v_dirs,
|
||||
self.config['user'],
|
||||
root_dir=self.config['root_dir'],
|
||||
)
|
||||
verify_log(self.config)
|
||||
client = salt.spm.SPMClient(ui, self.config)
|
||||
client.run(self.args)
|
||||
|
|
|
@ -1582,7 +1582,10 @@ class LocalClient(object):
|
|||
timeout=timeout,
|
||||
tgt=tgt,
|
||||
tgt_type=tgt_type,
|
||||
expect_minions=(verbose or show_timeout),
|
||||
# (gtmanfred) expect_minions is popped here incase it is passed from a client
|
||||
# call. If this is not popped, then it would be passed twice to
|
||||
# get_iter_returns.
|
||||
expect_minions=(kwargs.pop('expect_minions', False) or verbose or show_timeout),
|
||||
**kwargs
|
||||
):
|
||||
log.debug('return event: %s', ret)
|
||||
|
|
|
@ -240,7 +240,7 @@ class SyncClientMixin(object):
|
|||
|
||||
def low(self, fun, low, print_event=True, full_return=False):
|
||||
'''
|
||||
Check for deprecated usage and allow until Salt Oxygen.
|
||||
Check for deprecated usage and allow until Salt Fluorine.
|
||||
'''
|
||||
msg = []
|
||||
if 'args' in low:
|
||||
|
@ -251,7 +251,7 @@ class SyncClientMixin(object):
|
|||
low['kwarg'] = low.pop('kwargs')
|
||||
|
||||
if msg:
|
||||
salt.utils.warn_until('Oxygen', ' '.join(msg))
|
||||
salt.utils.warn_until('Fluorine', ' '.join(msg))
|
||||
|
||||
return self._low(fun, low, print_event=print_event, full_return=full_return)
|
||||
|
||||
|
|
|
@ -723,6 +723,7 @@ class Single(object):
|
|||
self.thin_dir = kwargs['thin_dir']
|
||||
elif self.winrm:
|
||||
saltwinshell.set_winvars(self)
|
||||
self.python_env = kwargs.get('ssh_python_env')
|
||||
else:
|
||||
if user:
|
||||
thin_dir = DEFAULT_THIN_DIR.replace('%%USER%%', user)
|
||||
|
@ -782,6 +783,10 @@ class Single(object):
|
|||
self.serial = salt.payload.Serial(opts)
|
||||
self.wfuncs = salt.loader.ssh_wrapper(opts, None, self.context)
|
||||
self.shell = salt.client.ssh.shell.gen_shell(opts, **args)
|
||||
if self.winrm:
|
||||
# Determine if Windows client is x86 or AMD64
|
||||
arch, _, _ = self.shell.exec_cmd('powershell $ENV:PROCESSOR_ARCHITECTURE')
|
||||
self.arch = arch.strip()
|
||||
self.thin = thin if thin else salt.utils.thin.thin_path(opts['cachedir'])
|
||||
|
||||
def __arg_comps(self):
|
||||
|
@ -903,6 +908,8 @@ class Single(object):
|
|||
ret = json.dumps({'local': opts_pkg})
|
||||
return ret, retcode
|
||||
|
||||
if 'known_hosts_file' in self.opts:
|
||||
opts_pkg['known_hosts_file'] = self.opts['known_hosts_file']
|
||||
opts_pkg['file_roots'] = self.opts['file_roots']
|
||||
opts_pkg['pillar_roots'] = self.opts['pillar_roots']
|
||||
opts_pkg['ext_pillar'] = self.opts['ext_pillar']
|
||||
|
|
|
@ -6,6 +6,7 @@ Create ssh executor system
|
|||
from __future__ import absolute_import
|
||||
# Import python libs
|
||||
import os
|
||||
import time
|
||||
import copy
|
||||
import json
|
||||
import logging
|
||||
|
@ -21,6 +22,8 @@ import salt.loader
|
|||
import salt.minion
|
||||
import salt.log
|
||||
from salt.ext.six import string_types
|
||||
import salt.ext.six as six
|
||||
from salt.exceptions import SaltInvocationError
|
||||
|
||||
__func_alias__ = {
|
||||
'apply_': 'apply'
|
||||
|
@ -28,6 +31,47 @@ __func_alias__ = {
|
|||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _set_retcode(ret, highstate=None):
|
||||
'''
|
||||
Set the return code based on the data back from the state system
|
||||
'''
|
||||
|
||||
# Set default retcode to 0
|
||||
__context__['retcode'] = 0
|
||||
|
||||
if isinstance(ret, list):
|
||||
__context__['retcode'] = 1
|
||||
return
|
||||
if not salt.utils.check_state_result(ret, highstate=highstate):
|
||||
|
||||
__context__['retcode'] = 2
|
||||
|
||||
|
||||
def _check_pillar(kwargs, pillar=None):
|
||||
'''
|
||||
Check the pillar for errors, refuse to run the state if there are errors
|
||||
in the pillar and return the pillar errors
|
||||
'''
|
||||
if kwargs.get('force'):
|
||||
return True
|
||||
pillar_dict = pillar if pillar is not None else __pillar__
|
||||
if '_errors' in pillar_dict:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def _wait(jid):
|
||||
'''
|
||||
Wait for all previously started state jobs to finish running
|
||||
'''
|
||||
if jid is None:
|
||||
jid = salt.utils.jid.gen_jid()
|
||||
states = _prior_running_states(jid)
|
||||
while states:
|
||||
time.sleep(1)
|
||||
states = _prior_running_states(jid)
|
||||
|
||||
|
||||
def _merge_extra_filerefs(*args):
|
||||
'''
|
||||
Takes a list of filerefs and returns a merged list
|
||||
|
@ -127,6 +171,100 @@ def sls(mods, saltenv='base', test=None, exclude=None, **kwargs):
|
|||
return stdout
|
||||
|
||||
|
||||
def running(concurrent=False):
|
||||
'''
|
||||
Return a list of strings that contain state return data if a state function
|
||||
is already running. This function is used to prevent multiple state calls
|
||||
from being run at the same time.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' state.running
|
||||
'''
|
||||
ret = []
|
||||
if concurrent:
|
||||
return ret
|
||||
active = __salt__['saltutil.is_running']('state.*')
|
||||
for data in active:
|
||||
err = (
|
||||
'The function "{0}" is running as PID {1} and was started at '
|
||||
'{2} with jid {3}'
|
||||
).format(
|
||||
data['fun'],
|
||||
data['pid'],
|
||||
salt.utils.jid.jid_to_time(data['jid']),
|
||||
data['jid'],
|
||||
)
|
||||
ret.append(err)
|
||||
return ret
|
||||
|
||||
|
||||
def _prior_running_states(jid):
|
||||
'''
|
||||
Return a list of dicts of prior calls to state functions. This function is
|
||||
used to queue state calls so only one is run at a time.
|
||||
'''
|
||||
|
||||
ret = []
|
||||
active = __salt__['saltutil.is_running']('state.*')
|
||||
for data in active:
|
||||
try:
|
||||
data_jid = int(data['jid'])
|
||||
except ValueError:
|
||||
continue
|
||||
if data_jid < int(jid):
|
||||
ret.append(data)
|
||||
return ret
|
||||
|
||||
|
||||
def _check_queue(queue, kwargs):
|
||||
'''
|
||||
Utility function to queue the state run if requested
|
||||
and to check for conflicts in currently running states
|
||||
'''
|
||||
if queue:
|
||||
_wait(kwargs.get('__pub_jid'))
|
||||
else:
|
||||
conflict = running(concurrent=kwargs.get('concurrent', False))
|
||||
if conflict:
|
||||
__context__['retcode'] = 1
|
||||
return conflict
|
||||
|
||||
|
||||
def _get_opts(**kwargs):
|
||||
'''
|
||||
Return a copy of the opts for use, optionally load a local config on top
|
||||
'''
|
||||
opts = copy.deepcopy(__opts__)
|
||||
|
||||
if 'localconfig' in kwargs:
|
||||
return salt.config.minion_config(kwargs['localconfig'], defaults=opts)
|
||||
|
||||
if 'saltenv' in kwargs:
|
||||
saltenv = kwargs['saltenv']
|
||||
if saltenv is not None and not isinstance(saltenv, six.string_types):
|
||||
opts['environment'] = str(kwargs['saltenv'])
|
||||
else:
|
||||
opts['environment'] = kwargs['saltenv']
|
||||
|
||||
if 'pillarenv' in kwargs:
|
||||
pillarenv = kwargs['pillarenv']
|
||||
if pillarenv is not None and not isinstance(pillarenv, six.string_types):
|
||||
opts['pillarenv'] = str(kwargs['pillarenv'])
|
||||
else:
|
||||
opts['pillarenv'] = kwargs['pillarenv']
|
||||
|
||||
return opts
|
||||
|
||||
|
||||
def _get_initial_pillar(opts):
|
||||
return __pillar__ if __opts__['__cli'] == 'salt-call' \
|
||||
and opts['pillarenv'] == __opts__['pillarenv'] \
|
||||
else None
|
||||
|
||||
|
||||
def low(data, **kwargs):
|
||||
'''
|
||||
Execute a single low data call
|
||||
|
@ -199,6 +337,21 @@ def low(data, **kwargs):
|
|||
return stdout
|
||||
|
||||
|
||||
def _get_test_value(test=None, **kwargs):
|
||||
'''
|
||||
Determine the correct value for the test flag.
|
||||
'''
|
||||
ret = True
|
||||
if test is None:
|
||||
if salt.utils.test_mode(test=test, **kwargs):
|
||||
ret = True
|
||||
else:
|
||||
ret = __opts__.get('test', None)
|
||||
else:
|
||||
ret = test
|
||||
return ret
|
||||
|
||||
|
||||
def high(data, **kwargs):
|
||||
'''
|
||||
Execute the compound calls stored in a single set of high data
|
||||
|
@ -289,6 +442,143 @@ def apply_(mods=None,
|
|||
return highstate(**kwargs)
|
||||
|
||||
|
||||
def request(mods=None,
|
||||
**kwargs):
|
||||
'''
|
||||
.. versionadded:: 2017.7.3
|
||||
|
||||
Request that the local admin execute a state run via
|
||||
`salt-call state.run_request`
|
||||
All arguments match state.apply
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' state.request
|
||||
salt '*' state.request test
|
||||
salt '*' state.request test,pkgs
|
||||
'''
|
||||
kwargs['test'] = True
|
||||
ret = apply_(mods, **kwargs)
|
||||
notify_path = os.path.join(__opts__['cachedir'], 'req_state.p')
|
||||
serial = salt.payload.Serial(__opts__)
|
||||
req = check_request()
|
||||
req.update({kwargs.get('name', 'default'): {
|
||||
'test_run': ret,
|
||||
'mods': mods,
|
||||
'kwargs': kwargs
|
||||
}
|
||||
})
|
||||
cumask = os.umask(0o77)
|
||||
try:
|
||||
if salt.utils.is_windows():
|
||||
# Make sure cache file isn't read-only
|
||||
__salt__['cmd.run']('attrib -R "{0}"'.format(notify_path))
|
||||
with salt.utils.fopen(notify_path, 'w+b') as fp_:
|
||||
serial.dump(req, fp_)
|
||||
except (IOError, OSError):
|
||||
msg = 'Unable to write state request file {0}. Check permission.'
|
||||
log.error(msg.format(notify_path))
|
||||
os.umask(cumask)
|
||||
return ret
|
||||
|
||||
|
||||
def check_request(name=None):
|
||||
'''
|
||||
.. versionadded:: 2017.7.3
|
||||
|
||||
Return the state request information, if any
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' state.check_request
|
||||
'''
|
||||
notify_path = os.path.join(__opts__['cachedir'], 'req_state.p')
|
||||
serial = salt.payload.Serial(__opts__)
|
||||
if os.path.isfile(notify_path):
|
||||
with salt.utils.fopen(notify_path, 'rb') as fp_:
|
||||
req = serial.load(fp_)
|
||||
if name:
|
||||
return req[name]
|
||||
return req
|
||||
return {}
|
||||
|
||||
|
||||
def clear_request(name=None):
|
||||
'''
|
||||
.. versionadded:: 2017.7.3
|
||||
|
||||
Clear out the state execution request without executing it
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' state.clear_request
|
||||
'''
|
||||
notify_path = os.path.join(__opts__['cachedir'], 'req_state.p')
|
||||
serial = salt.payload.Serial(__opts__)
|
||||
if not os.path.isfile(notify_path):
|
||||
return True
|
||||
if not name:
|
||||
try:
|
||||
os.remove(notify_path)
|
||||
except (IOError, OSError):
|
||||
pass
|
||||
else:
|
||||
req = check_request()
|
||||
if name in req:
|
||||
req.pop(name)
|
||||
else:
|
||||
return False
|
||||
cumask = os.umask(0o77)
|
||||
try:
|
||||
if salt.utils.is_windows():
|
||||
# Make sure cache file isn't read-only
|
||||
__salt__['cmd.run']('attrib -R "{0}"'.format(notify_path))
|
||||
with salt.utils.fopen(notify_path, 'w+b') as fp_:
|
||||
serial.dump(req, fp_)
|
||||
except (IOError, OSError):
|
||||
msg = 'Unable to write state request file {0}. Check permission.'
|
||||
log.error(msg.format(notify_path))
|
||||
os.umask(cumask)
|
||||
return True
|
||||
|
||||
|
||||
def run_request(name='default', **kwargs):
|
||||
'''
|
||||
.. versionadded:: 2017.7.3
|
||||
|
||||
Execute the pending state request
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' state.run_request
|
||||
'''
|
||||
req = check_request()
|
||||
if name not in req:
|
||||
return {}
|
||||
n_req = req[name]
|
||||
if 'mods' not in n_req or 'kwargs' not in n_req:
|
||||
return {}
|
||||
req[name]['kwargs'].update(kwargs)
|
||||
if 'test' in n_req['kwargs']:
|
||||
n_req['kwargs'].pop('test')
|
||||
if req:
|
||||
ret = apply_(n_req['mods'], **n_req['kwargs'])
|
||||
try:
|
||||
os.remove(os.path.join(__opts__['cachedir'], 'req_state.p'))
|
||||
except (IOError, OSError):
|
||||
pass
|
||||
return ret
|
||||
return {}
|
||||
|
||||
|
||||
def highstate(test=None, **kwargs):
|
||||
'''
|
||||
Retrieve the state data from the salt master for this minion and execute it
|
||||
|
@ -478,6 +768,99 @@ def show_lowstate():
|
|||
return st_.compile_low_chunks()
|
||||
|
||||
|
||||
def sls_id(id_, mods, test=None, queue=False, **kwargs):
|
||||
'''
|
||||
Call a single ID from the named module(s) and handle all requisites
|
||||
|
||||
The state ID comes *before* the module ID(s) on the command line.
|
||||
|
||||
id
|
||||
ID to call
|
||||
|
||||
mods
|
||||
Comma-delimited list of modules to search for given id and its requisites
|
||||
|
||||
.. versionadded:: 2017.7.3
|
||||
|
||||
saltenv : base
|
||||
Specify a salt fileserver environment to be used when applying states
|
||||
|
||||
pillarenv
|
||||
Specify a Pillar environment to be used when applying states. This
|
||||
can also be set in the minion config file using the
|
||||
:conf_minion:`pillarenv` option. When neither the
|
||||
:conf_minion:`pillarenv` minion config option nor this CLI argument is
|
||||
used, all Pillar environments will be merged together.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' state.sls_id my_state my_module
|
||||
|
||||
salt '*' state.sls_id my_state my_module,a_common_module
|
||||
'''
|
||||
conflict = _check_queue(queue, kwargs)
|
||||
if conflict is not None:
|
||||
return conflict
|
||||
orig_test = __opts__.get('test', None)
|
||||
opts = _get_opts(**kwargs)
|
||||
opts['test'] = _get_test_value(test, **kwargs)
|
||||
|
||||
# Since this is running a specific ID within a specific SLS file, fall back
|
||||
# to the 'base' saltenv if none is configured and none was passed.
|
||||
if opts['environment'] is None:
|
||||
opts['environment'] = '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))
|
||||
|
||||
if not _check_pillar(kwargs, st_.opts['pillar']):
|
||||
__context__['retcode'] = 5
|
||||
err = ['Pillar failed to render with the following messages:']
|
||||
err += __pillar__['_errors']
|
||||
return err
|
||||
|
||||
if isinstance(mods, six.string_types):
|
||||
split_mods = mods.split(',')
|
||||
st_.push_active()
|
||||
try:
|
||||
high_, errors = st_.render_highstate({opts['environment']: split_mods})
|
||||
finally:
|
||||
st_.pop_active()
|
||||
errors += st_.state.verify_high(high_)
|
||||
# Apply requisites to high data
|
||||
high_, req_in_errors = st_.state.requisite_in(high_)
|
||||
if req_in_errors:
|
||||
# This if statement should not be necessary if there were no errors,
|
||||
# but it is required to get the unit tests to pass.
|
||||
errors.extend(req_in_errors)
|
||||
if errors:
|
||||
__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))
|
||||
|
||||
_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
|
||||
if not ret:
|
||||
raise SaltInvocationError(
|
||||
'No matches for ID \'{0}\' found in SLS \'{1}\' within saltenv '
|
||||
'\'{2}\''.format(id_, mods, opts['environment'])
|
||||
)
|
||||
return ret
|
||||
|
||||
|
||||
def show_sls(mods, saltenv='base', test=None, **kwargs):
|
||||
'''
|
||||
Display the state data from a specific sls or list of sls files on the
|
||||
|
|
|
@ -234,7 +234,7 @@ class CloudClient(object):
|
|||
if a.get('provider', '')]
|
||||
if providers:
|
||||
_providers = opts.get('providers', {})
|
||||
for provider in list(_providers):
|
||||
for provider in list(_providers).copy():
|
||||
if provider not in providers:
|
||||
_providers.pop(provider)
|
||||
return opts
|
||||
|
|
|
@ -66,7 +66,8 @@ class SaltCloud(parsers.SaltCloudParser):
|
|||
if self.config['verify_env']:
|
||||
verify_env(
|
||||
[os.path.dirname(self.config['conf_file'])],
|
||||
salt_master_user
|
||||
salt_master_user,
|
||||
root_dir=self.config['root_dir'],
|
||||
)
|
||||
logfile = self.config['log_file']
|
||||
if logfile is not None and not logfile.startswith('tcp://') \
|
||||
|
|
|
@ -1990,7 +1990,7 @@ def request_instance(vm_=None, call=None):
|
|||
params[termination_key] = str(set_del_root_vol_on_destroy).lower()
|
||||
|
||||
# Use default volume type if not specified
|
||||
if ex_blockdevicemappings and 'Ebs.VolumeType' not in ex_blockdevicemappings[dev_index]:
|
||||
if ex_blockdevicemappings and dev_index < len(ex_blockdevicemappings) and 'Ebs.VolumeType' not in ex_blockdevicemappings[dev_index]:
|
||||
type_key = '{0}BlockDeviceMapping.{1}.Ebs.VolumeType'.format(spot_prefix, dev_index)
|
||||
params[type_key] = rd_type
|
||||
|
||||
|
@ -3539,16 +3539,15 @@ def list_nodes_min(location=None, call=None):
|
|||
|
||||
for instance in instances:
|
||||
if isinstance(instance['instancesSet']['item'], list):
|
||||
for item in instance['instancesSet']['item']:
|
||||
state = item['instanceState']['name']
|
||||
name = _extract_name_tag(item)
|
||||
id = item['instanceId']
|
||||
items = instance['instancesSet']['item']
|
||||
else:
|
||||
item = instance['instancesSet']['item']
|
||||
items = [instance['instancesSet']['item']]
|
||||
|
||||
for item in items:
|
||||
state = item['instanceState']['name']
|
||||
name = _extract_name_tag(item)
|
||||
id = item['instanceId']
|
||||
ret[name] = {'state': state, 'id': id}
|
||||
ret[name] = {'state': state, 'id': id}
|
||||
return ret
|
||||
|
||||
|
||||
|
|
|
@ -2400,9 +2400,10 @@ def create_attach_volumes(name, kwargs, call=None):
|
|||
'-a or --action.'
|
||||
)
|
||||
|
||||
volumes = kwargs['volumes']
|
||||
volumes = literal_eval(kwargs['volumes'])
|
||||
node = kwargs['node']
|
||||
node_data = _expand_node(node)
|
||||
conn = get_conn()
|
||||
node_data = _expand_node(conn.ex_get_node(node))
|
||||
letter = ord('a') - 1
|
||||
|
||||
for idx, volume in enumerate(volumes):
|
||||
|
@ -2412,9 +2413,9 @@ def create_attach_volumes(name, kwargs, call=None):
|
|||
'disk_name': volume_name,
|
||||
'location': node_data['extra']['zone']['name'],
|
||||
'size': volume['size'],
|
||||
'type': volume['type'],
|
||||
'image': volume['image'],
|
||||
'snapshot': volume['snapshot']
|
||||
'type': volume.get('type', 'pd-standard'),
|
||||
'image': volume.get('image', None),
|
||||
'snapshot': volume.get('snapshot', None)
|
||||
}
|
||||
|
||||
create_disk(volume_dict, 'function')
|
||||
|
@ -2580,7 +2581,10 @@ def create(vm_=None, call=None):
|
|||
ssh_user, ssh_key = __get_ssh_credentials(vm_)
|
||||
vm_['ssh_host'] = __get_host(node_data, vm_)
|
||||
vm_['key_filename'] = ssh_key
|
||||
__utils__['cloud.bootstrap'](vm_, __opts__)
|
||||
|
||||
ret = __utils__['cloud.bootstrap'](vm_, __opts__)
|
||||
|
||||
ret.update(node_dict)
|
||||
|
||||
log.info('Created Cloud VM \'{0[name]}\''.format(vm_))
|
||||
log.trace(
|
||||
|
@ -2598,7 +2602,7 @@ def create(vm_=None, call=None):
|
|||
transport=__opts__['transport']
|
||||
)
|
||||
|
||||
return node_dict
|
||||
return ret
|
||||
|
||||
|
||||
def update_pricing(kwargs=None, call=None):
|
||||
|
|
|
@ -41,6 +41,7 @@ Example profile:
|
|||
master_port: 5506
|
||||
|
||||
Tested on:
|
||||
- Fedora 26 (libvirt 3.2.1, qemu 2.9.1)
|
||||
- Fedora 25 (libvirt 1.3.3.2, qemu 2.6.1)
|
||||
- Fedora 23 (libvirt 1.2.18, qemu 2.4.1)
|
||||
- Centos 7 (libvirt 1.2.17, qemu 1.5.3)
|
||||
|
@ -82,9 +83,6 @@ from salt.exceptions import (
|
|||
SaltCloudSystemExit
|
||||
)
|
||||
|
||||
# Get logging started
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
VIRT_STATE_NAME_MAP = {0: 'running',
|
||||
1: 'running',
|
||||
2: 'running',
|
||||
|
@ -99,6 +97,20 @@ IP_LEARNING_XML = """<filterref filter='clean-traffic'>
|
|||
|
||||
__virtualname__ = 'libvirt'
|
||||
|
||||
# Set up logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def libvirt_error_handler(ctx, error): # pylint: disable=unused-argument
|
||||
'''
|
||||
Redirect stderr prints from libvirt to salt logging.
|
||||
'''
|
||||
log.debug("libvirt error {0}".format(error))
|
||||
|
||||
|
||||
if HAS_LIBVIRT:
|
||||
libvirt.registerErrorHandler(f=libvirt_error_handler, ctx=None)
|
||||
|
||||
|
||||
def __virtual__():
|
||||
'''
|
||||
|
@ -450,18 +462,54 @@ def create(vm_):
|
|||
|
||||
return ret
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
# Try to clean up in as much cases as possible
|
||||
log.info('Cleaning up after exception clean up items: {0}'.format(cleanup))
|
||||
for leftover in cleanup:
|
||||
what = leftover['what']
|
||||
item = leftover['item']
|
||||
if what == 'domain':
|
||||
destroy_domain(conn, item)
|
||||
if what == 'volume':
|
||||
item.delete()
|
||||
do_cleanup(cleanup)
|
||||
# throw the root cause after cleanup
|
||||
raise e
|
||||
|
||||
|
||||
def do_cleanup(cleanup):
|
||||
'''
|
||||
Clean up clone domain leftovers as much as possible.
|
||||
|
||||
Extra robust clean up in order to deal with some small changes in libvirt
|
||||
behavior over time. Passed in volumes and domains are deleted, any errors
|
||||
are ignored. Used when cloning/provisioning a domain fails.
|
||||
|
||||
:param cleanup: list containing dictonaries with two keys: 'what' and 'item'.
|
||||
If 'what' is domain the 'item' is a libvirt domain object.
|
||||
If 'what' is volume then the item is a libvirt volume object.
|
||||
|
||||
Returns:
|
||||
none
|
||||
|
||||
.. versionadded: 2017.7.3
|
||||
'''
|
||||
log.info('Cleaning up after exception')
|
||||
for leftover in cleanup:
|
||||
what = leftover['what']
|
||||
item = leftover['item']
|
||||
if what == 'domain':
|
||||
log.info('Cleaning up {0} {1}'.format(what, item.name()))
|
||||
try:
|
||||
item.destroy()
|
||||
log.debug('{0} {1} forced off'.format(what, item.name()))
|
||||
except libvirtError:
|
||||
pass
|
||||
try:
|
||||
item.undefineFlags(libvirt.VIR_DOMAIN_UNDEFINE_MANAGED_SAVE+
|
||||
libvirt.VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA+
|
||||
libvirt.VIR_DOMAIN_UNDEFINE_NVRAM)
|
||||
log.debug('{0} {1} undefined'.format(what, item.name()))
|
||||
except libvirtError:
|
||||
pass
|
||||
if what == 'volume':
|
||||
try:
|
||||
item.delete()
|
||||
log.debug('{0} {1} cleaned up'.format(what, item.name()))
|
||||
except libvirtError:
|
||||
pass
|
||||
|
||||
|
||||
def destroy(name, call=None):
|
||||
"""
|
||||
This function irreversibly destroys a virtual machine on the cloud provider.
|
||||
|
|
|
@ -4571,7 +4571,8 @@ def _list_nodes(full=False):
|
|||
pass
|
||||
|
||||
vms[name]['id'] = vm.find('ID').text
|
||||
vms[name]['image'] = vm.find('TEMPLATE').find('TEMPLATE_ID').text
|
||||
if vm.find('TEMPLATE').find('TEMPLATE_ID'):
|
||||
vms[name]['image'] = vm.find('TEMPLATE').find('TEMPLATE_ID').text
|
||||
vms[name]['name'] = name
|
||||
vms[name]['size'] = {'cpu': cpu_size, 'memory': memory_size}
|
||||
vms[name]['state'] = vm.find('STATE').text
|
||||
|
|
|
@ -48,6 +48,10 @@ log = logging.getLogger(__name__)
|
|||
# The name salt will identify the lib by
|
||||
__virtualname__ = 'virtualbox'
|
||||
|
||||
#if no clone mode is specified in the virtualbox profile
|
||||
#then default to 0 which was the old default value
|
||||
DEFAULT_CLONE_MODE = 0
|
||||
|
||||
|
||||
def __virtual__():
|
||||
'''
|
||||
|
@ -85,6 +89,30 @@ def get_configured_provider():
|
|||
return configured
|
||||
|
||||
|
||||
def map_clonemode(vm_info):
|
||||
"""
|
||||
Convert the virtualbox config file values for clone_mode into the integers the API requires
|
||||
"""
|
||||
mode_map = {
|
||||
'state': 0,
|
||||
'child': 1,
|
||||
'all': 2
|
||||
}
|
||||
|
||||
if not vm_info:
|
||||
return DEFAULT_CLONE_MODE
|
||||
|
||||
if 'clonemode' not in vm_info:
|
||||
return DEFAULT_CLONE_MODE
|
||||
|
||||
if vm_info['clonemode'] in mode_map:
|
||||
return mode_map[vm_info['clonemode']]
|
||||
else:
|
||||
raise SaltCloudSystemExit(
|
||||
"Illegal clonemode for virtualbox profile. Legal values are: {}".format(','.join(mode_map.keys()))
|
||||
)
|
||||
|
||||
|
||||
def create(vm_info):
|
||||
"""
|
||||
Creates a virtual machine from the given VM information.
|
||||
|
@ -102,6 +130,7 @@ def create(vm_info):
|
|||
profile: <dict>
|
||||
driver: <provider>:<profile>
|
||||
clonefrom: <vm_name>
|
||||
clonemode: <mode> (default: state, choices: state, child, all)
|
||||
}
|
||||
@type vm_info dict
|
||||
@return dict of resulting vm. !!!Passwords can and should be included!!!
|
||||
|
@ -133,6 +162,9 @@ def create(vm_info):
|
|||
key_filename = config.get_cloud_config_value(
|
||||
'private_key', vm_info, __opts__, search_global=False, default=None
|
||||
)
|
||||
clone_mode = map_clonemode(vm_info)
|
||||
wait_for_pattern = vm_info['waitforpattern'] if 'waitforpattern' in vm_info.keys() else None
|
||||
interface_index = vm_info['interfaceindex'] if 'interfaceindex' in vm_info.keys() else 0
|
||||
|
||||
log.debug("Going to fire event: starting create")
|
||||
__utils__['cloud.fire_event'](
|
||||
|
@ -147,7 +179,8 @@ def create(vm_info):
|
|||
# to create the virtual machine.
|
||||
request_kwargs = {
|
||||
'name': vm_info['name'],
|
||||
'clone_from': vm_info['clonefrom']
|
||||
'clone_from': vm_info['clonefrom'],
|
||||
'clone_mode': clone_mode
|
||||
}
|
||||
|
||||
__utils__['cloud.fire_event'](
|
||||
|
@ -163,17 +196,17 @@ def create(vm_info):
|
|||
# Booting and deploying if needed
|
||||
if power:
|
||||
vb_start_vm(vm_name, timeout=boot_timeout)
|
||||
ips = vb_wait_for_network_address(wait_for_ip_timeout, machine_name=vm_name)
|
||||
ips = vb_wait_for_network_address(wait_for_ip_timeout, machine_name=vm_name, wait_for_pattern=wait_for_pattern)
|
||||
|
||||
if len(ips):
|
||||
ip = ips[0]
|
||||
ip = ips[interface_index]
|
||||
log.info("[ {0} ] IPv4 is: {1}".format(vm_name, ip))
|
||||
# ssh or smb using ip and install salt only if deploy is True
|
||||
if deploy:
|
||||
vm_info['key_filename'] = key_filename
|
||||
vm_info['ssh_host'] = ip
|
||||
|
||||
res = __utils__['cloud.bootstrap'](vm_info)
|
||||
res = __utils__['cloud.bootstrap'](vm_info, __opts__)
|
||||
vm_result.update(res)
|
||||
|
||||
__utils__['cloud.fire_event'](
|
||||
|
|
|
@ -55,7 +55,7 @@ _DFLT_LOG_DATEFMT = '%H:%M:%S'
|
|||
_DFLT_LOG_DATEFMT_LOGFILE = '%Y-%m-%d %H:%M:%S'
|
||||
_DFLT_LOG_FMT_CONSOLE = '[%(levelname)-8s] %(message)s'
|
||||
_DFLT_LOG_FMT_LOGFILE = (
|
||||
'%(asctime)s,%(msecs)03d [%(name)-17s][%(levelname)-8s][%(process)d] %(message)s'
|
||||
'%(asctime)s,%(msecs)03d [%(name)-17s:%(lineno)-4d][%(levelname)-8s][%(process)d] %(message)s'
|
||||
)
|
||||
_DFLT_REFSPECS = ['+refs/heads/*:refs/remotes/origin/*', '+refs/tags/*:refs/tags/*']
|
||||
|
||||
|
@ -334,7 +334,7 @@ VALID_OPTS = {
|
|||
# Whether or not scheduled mine updates should be accompanied by a job return for the job cache
|
||||
'mine_return_job': bool,
|
||||
|
||||
# Schedule a mine update every n number of seconds
|
||||
# The number of minutes between mine updates.
|
||||
'mine_interval': int,
|
||||
|
||||
# The ipc strategy. (i.e., sockets versus tcp, etc)
|
||||
|
@ -569,6 +569,23 @@ VALID_OPTS = {
|
|||
# False in 2016.3.0
|
||||
'add_proxymodule_to_opts': bool,
|
||||
|
||||
# Merge pillar data into configuration opts.
|
||||
# As multiple proxies can run on the same server, we may need different
|
||||
# configuration options for each, while there's one single configuration file.
|
||||
# The solution is merging the pillar data of each proxy minion into the opts.
|
||||
'proxy_merge_pillar_in_opts': bool,
|
||||
|
||||
# Deep merge of pillar data into configuration opts.
|
||||
# Evaluated only when `proxy_merge_pillar_in_opts` is True.
|
||||
'proxy_deep_merge_pillar_in_opts': bool,
|
||||
|
||||
# The strategy used when merging pillar into opts.
|
||||
# Considered only when `proxy_merge_pillar_in_opts` is True.
|
||||
'proxy_merge_pillar_in_opts_strategy': str,
|
||||
|
||||
# Allow enabling mine details using pillar data.
|
||||
'proxy_mines_pillar': bool,
|
||||
|
||||
# In some particular cases, always alive proxies are not beneficial.
|
||||
# This option can be used in those less dynamic environments:
|
||||
# the user can request the connection
|
||||
|
@ -700,6 +717,10 @@ VALID_OPTS = {
|
|||
'fileserver_limit_traversal': bool,
|
||||
'fileserver_verify_config': bool,
|
||||
|
||||
# Optionally apply '*' permissioins to any user. By default '*' is a fallback case that is
|
||||
# applied only if the user didn't matched by other matchers.
|
||||
'permissive_acl': bool,
|
||||
|
||||
# Optionally enables keeping the calculated user's auth list in the token file.
|
||||
'keep_acl_in_token': bool,
|
||||
|
||||
|
@ -1449,6 +1470,7 @@ DEFAULT_MASTER_OPTS = {
|
|||
'external_auth': {},
|
||||
'token_expire': 43200,
|
||||
'token_expire_user_override': False,
|
||||
'permissive_acl': False,
|
||||
'keep_acl_in_token': False,
|
||||
'eauth_acl_module': '',
|
||||
'extension_modules': os.path.join(salt.syspaths.CACHE_DIR, 'master', 'extmods'),
|
||||
|
@ -1637,6 +1659,12 @@ DEFAULT_PROXY_MINION_OPTS = {
|
|||
'append_minionid_config_dirs': ['cachedir', 'pidfile', 'default_include', 'extension_modules'],
|
||||
'default_include': 'proxy.d/*.conf',
|
||||
|
||||
'proxy_merge_pillar_in_opts': False,
|
||||
'proxy_deep_merge_pillar_in_opts': False,
|
||||
'proxy_merge_pillar_in_opts_strategy': 'smart',
|
||||
|
||||
'proxy_mines_pillar': True,
|
||||
|
||||
# By default, proxies will preserve the connection.
|
||||
# If this option is set to False,
|
||||
# the connection with the remote dumb device
|
||||
|
|
|
@ -204,6 +204,14 @@ def clean_old_jobs(opts):
|
|||
|
||||
|
||||
def mk_key(opts, user):
|
||||
if HAS_PWD:
|
||||
uid = None
|
||||
try:
|
||||
uid = pwd.getpwnam(user).pw_uid
|
||||
except KeyError:
|
||||
# User doesn't exist in the system
|
||||
if opts['client_acl_verify']:
|
||||
return None
|
||||
if salt.utils.is_windows():
|
||||
# The username may contain '\' if it is in Windows
|
||||
# 'DOMAIN\username' format. Fix this for the keyfile path.
|
||||
|
@ -231,9 +239,9 @@ def mk_key(opts, user):
|
|||
# Write access is necessary since on subsequent runs, if the file
|
||||
# exists, it needs to be written to again. Windows enforces this.
|
||||
os.chmod(keyfile, 0o600)
|
||||
if HAS_PWD:
|
||||
if HAS_PWD and uid is not None:
|
||||
try:
|
||||
os.chown(keyfile, pwd.getpwnam(user).pw_uid, -1)
|
||||
os.chown(keyfile, uid, -1)
|
||||
except OSError:
|
||||
# The master is not being run as root and can therefore not
|
||||
# chown the key file
|
||||
|
@ -248,27 +256,26 @@ def access_keys(opts):
|
|||
'''
|
||||
# TODO: Need a way to get all available users for systems not supported by pwd module.
|
||||
# For now users pattern matching will not work for publisher_acl.
|
||||
users = []
|
||||
keys = {}
|
||||
publisher_acl = opts['publisher_acl']
|
||||
acl_users = set(publisher_acl.keys())
|
||||
if opts.get('user'):
|
||||
acl_users.add(opts['user'])
|
||||
acl_users.add(salt.utils.get_user())
|
||||
if opts['client_acl_verify'] and HAS_PWD:
|
||||
log.profile('Beginning pwd.getpwall() call in masterarpi access_keys function')
|
||||
for user in pwd.getpwall():
|
||||
users.append(user.pw_name)
|
||||
log.profile('End pwd.getpwall() call in masterarpi access_keys function')
|
||||
for user in acl_users:
|
||||
log.info('Preparing the %s key for local communication', user)
|
||||
keys[user] = mk_key(opts, user)
|
||||
key = mk_key(opts, user)
|
||||
if key is not None:
|
||||
keys[user] = key
|
||||
|
||||
# Check other users matching ACL patterns
|
||||
if HAS_PWD:
|
||||
for user in users:
|
||||
if opts['client_acl_verify'] and HAS_PWD:
|
||||
log.profile('Beginning pwd.getpwall() call in masterapi access_keys function')
|
||||
for user in pwd.getpwall():
|
||||
user = user.pw_name
|
||||
if user not in keys and salt.utils.check_whitelist_blacklist(user, whitelist=acl_users):
|
||||
keys[user] = mk_key(opts, user)
|
||||
log.profile('End pwd.getpwall() call in masterapi access_keys function')
|
||||
|
||||
return keys
|
||||
|
||||
|
@ -579,7 +586,18 @@ class RemoteFuncs(object):
|
|||
ret = {}
|
||||
if not salt.utils.verify.valid_id(self.opts, load['id']):
|
||||
return ret
|
||||
match_type = load.get('tgt_type', 'glob')
|
||||
expr_form = load.get('expr_form')
|
||||
if expr_form is not None and 'tgt_type' not in load:
|
||||
salt.utils.warn_until(
|
||||
u'Neon',
|
||||
u'_mine_get: minion {0} uses pre-Nitrogen API key '
|
||||
u'"expr_form". Accepting for backwards compatibility '
|
||||
u'but this is not guaranteed '
|
||||
u'after the Neon release'.format(load['id'])
|
||||
)
|
||||
match_type = expr_form
|
||||
else:
|
||||
match_type = load.get('tgt_type', 'glob')
|
||||
if match_type.lower() == 'pillar':
|
||||
match_type = 'pillar_exact'
|
||||
if match_type.lower() == 'compound':
|
||||
|
|
|
@ -74,8 +74,12 @@ def start(docker_url='unix://var/run/docker.sock',
|
|||
else:
|
||||
__salt__['event.send'](tag, msg)
|
||||
|
||||
client = docker.Client(base_url=docker_url,
|
||||
timeout=timeout)
|
||||
try:
|
||||
# docker-py 2.0 renamed this client attribute
|
||||
client = docker.APIClient(base_url=docker_url, timeout=timeout)
|
||||
except AttributeError:
|
||||
client = docker.Client(base_url=docker_url, timeout=timeout)
|
||||
|
||||
try:
|
||||
events = client.events()
|
||||
for event in events:
|
||||
|
|
|
@ -265,6 +265,12 @@ class SaltCacheError(SaltException):
|
|||
'''
|
||||
|
||||
|
||||
class TimeoutError(SaltException):
|
||||
'''
|
||||
Thrown when an opration cannot be completet within a given time limit.
|
||||
'''
|
||||
|
||||
|
||||
class SaltReqTimeoutError(SaltException):
|
||||
'''
|
||||
Thrown when a salt master request call fails to return within the timeout
|
||||
|
|
|
@ -10,6 +10,7 @@ import socket
|
|||
import ctypes
|
||||
import os
|
||||
import ipaddress
|
||||
import salt.ext.six as six
|
||||
|
||||
|
||||
class sockaddr(ctypes.Structure):
|
||||
|
@ -36,7 +37,7 @@ def inet_pton(address_family, ip_string):
|
|||
# This will catch IP Addresses such as 10.1.2
|
||||
if address_family == socket.AF_INET:
|
||||
try:
|
||||
ipaddress.ip_address(ip_string.decode())
|
||||
ipaddress.ip_address(six.u(ip_string))
|
||||
except ValueError:
|
||||
raise socket.error('illegal IP address string passed to inet_pton')
|
||||
return socket.inet_aton(ip_string)
|
||||
|
|
|
@ -622,10 +622,14 @@ class Client(object):
|
|||
def on_header(hdr):
|
||||
if write_body[1] is not False and write_body[2] is None:
|
||||
if not hdr.strip() and 'Content-Type' not in write_body[1]:
|
||||
# We've reached the end of the headers and not yet
|
||||
# found the Content-Type. Reset the values we're
|
||||
# tracking so that we properly follow the redirect.
|
||||
write_body[0] = None
|
||||
# If write_body[0] is True, then we are not following a
|
||||
# redirect (initial response was a 200 OK). So there is
|
||||
# no need to reset write_body[0].
|
||||
if write_body[0] is not True:
|
||||
# We are following a redirect, so we need to reset
|
||||
# write_body[0] so that we properly follow it.
|
||||
write_body[0] = None
|
||||
# We don't need the HTTPHeaders object anymore
|
||||
write_body[1] = False
|
||||
return
|
||||
# Try to find out what content type encoding is used if
|
||||
|
@ -648,9 +652,12 @@ class Client(object):
|
|||
# If write_body[0] is False, this means that this
|
||||
# header is a 30x redirect, so we need to reset
|
||||
# write_body[0] to None so that we parse the HTTP
|
||||
# status code from the redirect target.
|
||||
# status code from the redirect target. Additionally,
|
||||
# we need to reset write_body[2] so that we inspect the
|
||||
# headers for the Content-Type of the URL we're
|
||||
# following.
|
||||
if write_body[0] is write_body[1] is False:
|
||||
write_body[0] = None
|
||||
write_body[0] = write_body[2] = None
|
||||
|
||||
# Check the status line of the HTTP request
|
||||
if write_body[0] is None:
|
||||
|
|
|
@ -367,6 +367,16 @@ def _file_lists(load, form):
|
|||
'roots: %s symlink destination is %s',
|
||||
abs_path, link_dest
|
||||
)
|
||||
if salt.utils.is_windows() \
|
||||
and link_dest.startswith('\\\\'):
|
||||
# Symlink points to a network path. Since you can't
|
||||
# join UNC and non-UNC paths, just assume the original
|
||||
# path.
|
||||
log.trace(
|
||||
'roots: %s is a UNC path, using %s instead',
|
||||
link_dest, abs_path
|
||||
)
|
||||
link_dest = abs_path
|
||||
if link_dest.startswith('..'):
|
||||
joined = os.path.join(abs_path, link_dest)
|
||||
else:
|
||||
|
|
|
@ -1175,6 +1175,10 @@ _OS_FAMILY_MAP = {
|
|||
'Raspbian': 'Debian',
|
||||
'Devuan': 'Debian',
|
||||
'antiX': 'Debian',
|
||||
'Kali': 'Debian',
|
||||
'neon': 'Debian',
|
||||
'Cumulus': 'Debian',
|
||||
'Deepin': 'Debian',
|
||||
'NILinuxRT': 'NILinuxRT',
|
||||
'NILinuxRT-XFCE': 'NILinuxRT',
|
||||
'Void': 'Void',
|
||||
|
@ -1363,7 +1367,10 @@ def os_data():
|
|||
.format(' '.join(init_cmdline))
|
||||
)
|
||||
|
||||
# Add lsb grains on any distro with lsb-release
|
||||
# Add lsb grains on any distro with lsb-release. Note that this import
|
||||
# can fail on systems with lsb-release installed if the system package
|
||||
# does not install the python package for the python interpreter used by
|
||||
# Salt (i.e. python2 or python3)
|
||||
try:
|
||||
import lsb_release # pylint: disable=import-error
|
||||
release = lsb_release.get_distro_information()
|
||||
|
@ -1412,7 +1419,13 @@ def os_data():
|
|||
if 'VERSION_ID' in os_release:
|
||||
grains['lsb_distrib_release'] = os_release['VERSION_ID']
|
||||
if 'PRETTY_NAME' in os_release:
|
||||
grains['lsb_distrib_codename'] = os_release['PRETTY_NAME']
|
||||
codename = os_release['PRETTY_NAME']
|
||||
# https://github.com/saltstack/salt/issues/44108
|
||||
if os_release['ID'] == 'debian':
|
||||
codename_match = re.search(r'\((\w+)\)$', codename)
|
||||
if codename_match:
|
||||
codename = codename_match.group(1)
|
||||
grains['lsb_distrib_codename'] = codename
|
||||
if 'CPE_NAME' in os_release:
|
||||
if ":suse:" in os_release['CPE_NAME'] or ":opensuse:" in os_release['CPE_NAME']:
|
||||
grains['os'] = "SUSE"
|
||||
|
|
|
@ -12,6 +12,7 @@ import logging
|
|||
# Import salt libs
|
||||
import salt.utils
|
||||
|
||||
__proxyenabled__ = ['*']
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
@ -31,16 +32,33 @@ def config():
|
|||
if 'conf_file' not in __opts__:
|
||||
return {}
|
||||
if os.path.isdir(__opts__['conf_file']):
|
||||
gfn = os.path.join(
|
||||
__opts__['conf_file'],
|
||||
'grains'
|
||||
)
|
||||
if salt.utils.is_proxy():
|
||||
gfn = os.path.join(
|
||||
__opts__['conf_file'],
|
||||
'proxy.d',
|
||||
__opts__['id'],
|
||||
'grains'
|
||||
)
|
||||
else:
|
||||
gfn = os.path.join(
|
||||
__opts__['conf_file'],
|
||||
'grains'
|
||||
)
|
||||
else:
|
||||
gfn = os.path.join(
|
||||
os.path.dirname(__opts__['conf_file']),
|
||||
'grains'
|
||||
)
|
||||
if salt.utils.is_proxy():
|
||||
gfn = os.path.join(
|
||||
os.path.dirname(__opts__['conf_file']),
|
||||
'proxy.d',
|
||||
__opts__['id'],
|
||||
'grains'
|
||||
)
|
||||
else:
|
||||
gfn = os.path.join(
|
||||
os.path.dirname(__opts__['conf_file']),
|
||||
'grains'
|
||||
)
|
||||
if os.path.isfile(gfn):
|
||||
log.debug('Loading static grains from %s', gfn)
|
||||
with salt.utils.fopen(gfn, 'rb') as fp_:
|
||||
try:
|
||||
return yaml.safe_load(fp_.read())
|
||||
|
|
|
@ -17,6 +17,7 @@ metadata server set `metadata_server_grains: True`.
|
|||
from __future__ import absolute_import
|
||||
|
||||
# Import python libs
|
||||
import json
|
||||
import os
|
||||
import socket
|
||||
|
||||
|
@ -47,14 +48,28 @@ def _search(prefix="latest/"):
|
|||
Recursively look up all grains in the metadata server
|
||||
'''
|
||||
ret = {}
|
||||
for line in http.query(os.path.join(HOST, prefix))['body'].split('\n'):
|
||||
linedata = http.query(os.path.join(HOST, prefix))
|
||||
if 'body' not in linedata:
|
||||
return ret
|
||||
for line in linedata['body'].split('\n'):
|
||||
if line.endswith('/'):
|
||||
ret[line[:-1]] = _search(prefix=os.path.join(prefix, line))
|
||||
elif prefix == 'latest/':
|
||||
# (gtmanfred) The first level should have a forward slash since
|
||||
# they have stuff underneath. This will not be doubled up though,
|
||||
# because lines ending with a slash are checked first.
|
||||
ret[line] = _search(prefix=os.path.join(prefix, line + '/'))
|
||||
elif '=' in line:
|
||||
key, value = line.split('=')
|
||||
ret[value] = _search(prefix=os.path.join(prefix, key))
|
||||
else:
|
||||
ret[line] = http.query(os.path.join(HOST, prefix, line))['body']
|
||||
retdata = http.query(os.path.join(HOST, prefix, line)).get('body', None)
|
||||
# (gtmanfred) This try except block is slightly faster than
|
||||
# checking if the string starts with a curly brace
|
||||
try:
|
||||
ret[line] = json.loads(retdata)
|
||||
except ValueError:
|
||||
ret[line] = retdata
|
||||
return ret
|
||||
|
||||
|
||||
|
|
37
salt/key.py
37
salt/key.py
|
@ -489,7 +489,7 @@ class Key(object):
|
|||
minions = []
|
||||
for key, val in six.iteritems(keys):
|
||||
minions.extend(val)
|
||||
if not self.opts.get('preserve_minion_cache', False) or not preserve_minions:
|
||||
if not self.opts.get('preserve_minion_cache', False):
|
||||
m_cache = os.path.join(self.opts['cachedir'], self.ACC)
|
||||
if os.path.isdir(m_cache):
|
||||
for minion in os.listdir(m_cache):
|
||||
|
@ -736,7 +736,7 @@ class Key(object):
|
|||
def delete_key(self,
|
||||
match=None,
|
||||
match_dict=None,
|
||||
preserve_minions=False,
|
||||
preserve_minions=None,
|
||||
revoke_auth=False):
|
||||
'''
|
||||
Delete public keys. If "match" is passed, it is evaluated as a glob.
|
||||
|
@ -774,11 +774,10 @@ class Key(object):
|
|||
salt.utils.event.tagify(prefix='key'))
|
||||
except (OSError, IOError):
|
||||
pass
|
||||
if preserve_minions:
|
||||
preserve_minions_list = matches.get('minions', [])
|
||||
if self.opts.get('preserve_minions') is True:
|
||||
self.check_minion_cache(preserve_minions=matches.get('minions', []))
|
||||
else:
|
||||
preserve_minions_list = []
|
||||
self.check_minion_cache(preserve_minions=preserve_minions_list)
|
||||
self.check_minion_cache()
|
||||
if self.opts.get('rotate_aes_key'):
|
||||
salt.crypt.dropfile(self.opts['cachedir'], self.opts['user'])
|
||||
return (
|
||||
|
@ -969,16 +968,17 @@ class RaetKey(Key):
|
|||
minions.extend(val)
|
||||
|
||||
m_cache = os.path.join(self.opts['cachedir'], 'minions')
|
||||
if os.path.isdir(m_cache):
|
||||
for minion in os.listdir(m_cache):
|
||||
if minion not in minions:
|
||||
shutil.rmtree(os.path.join(m_cache, minion))
|
||||
cache = salt.cache.factory(self.opts)
|
||||
clist = cache.list(self.ACC)
|
||||
if clist:
|
||||
for minion in clist:
|
||||
if not self.opts.get('preserve_minion_cache', False):
|
||||
if os.path.isdir(m_cache):
|
||||
for minion in os.listdir(m_cache):
|
||||
if minion not in minions and minion not in preserve_minions:
|
||||
cache.flush('{0}/{1}'.format(self.ACC, minion))
|
||||
shutil.rmtree(os.path.join(m_cache, minion))
|
||||
cache = salt.cache.factory(self.opts)
|
||||
clist = cache.list(self.ACC)
|
||||
if clist:
|
||||
for minion in clist:
|
||||
if minion not in minions and minion not in preserve_minions:
|
||||
cache.flush('{0}/{1}'.format(self.ACC, minion))
|
||||
|
||||
kind = self.opts.get('__role', '') # application kind
|
||||
if kind not in salt.utils.kinds.APPL_KINDS:
|
||||
|
@ -1220,7 +1220,7 @@ class RaetKey(Key):
|
|||
def delete_key(self,
|
||||
match=None,
|
||||
match_dict=None,
|
||||
preserve_minions=False,
|
||||
preserve_minions=None,
|
||||
revoke_auth=False):
|
||||
'''
|
||||
Delete public keys. If "match" is passed, it is evaluated as a glob.
|
||||
|
@ -1251,7 +1251,10 @@ class RaetKey(Key):
|
|||
os.remove(os.path.join(self.opts['pki_dir'], status, key))
|
||||
except (OSError, IOError):
|
||||
pass
|
||||
self.check_minion_cache(preserve_minions=matches.get('minions', []))
|
||||
if self.opts.get('preserve_minions') is True:
|
||||
self.check_minion_cache(preserve_minions=matches.get('minions', []))
|
||||
else:
|
||||
self.check_minion_cache()
|
||||
return (
|
||||
self.name_match(match) if match is not None
|
||||
else self.dict_match(matches)
|
||||
|
|
|
@ -270,7 +270,7 @@ def raw_mod(opts, name, functions, mod='modules'):
|
|||
testmod['test.ping']()
|
||||
'''
|
||||
loader = LazyLoader(
|
||||
_module_dirs(opts, mod, 'rawmodule'),
|
||||
_module_dirs(opts, mod, 'module'),
|
||||
opts,
|
||||
tag='rawmodule',
|
||||
virtual_enable=False,
|
||||
|
|
|
@ -128,12 +128,12 @@ def setup_handlers():
|
|||
callable(transport_registry.compute_scope)):
|
||||
conf_extras = transport_registry.compute_scope(url, dsn_config)
|
||||
dsn_config.update(conf_extras)
|
||||
options.update({
|
||||
'project': dsn_config['SENTRY_PROJECT'],
|
||||
'servers': dsn_config['SENTRY_SERVERS'],
|
||||
'public_key': dsn_config['SENTRY_PUBLIC_KEY'],
|
||||
'secret_key': dsn_config['SENTRY_SECRET_KEY']
|
||||
})
|
||||
options.update({
|
||||
'project': dsn_config['SENTRY_PROJECT'],
|
||||
'servers': dsn_config['SENTRY_SERVERS'],
|
||||
'public_key': dsn_config['SENTRY_PUBLIC_KEY'],
|
||||
'secret_key': dsn_config['SENTRY_SECRET_KEY']
|
||||
})
|
||||
except ValueError as exc:
|
||||
log.info(
|
||||
'Raven failed to parse the configuration provided '
|
||||
|
|
|
@ -60,7 +60,6 @@ import salt.search
|
|||
import salt.key
|
||||
import salt.acl
|
||||
import salt.engines
|
||||
import salt.fileserver
|
||||
import salt.daemons.masterapi
|
||||
import salt.defaults.exitcodes
|
||||
import salt.transport.server
|
||||
|
@ -181,7 +180,8 @@ class Maintenance(SignalHandlingMultiprocessingProcess):
|
|||
in the parent process, then once the fork happens you'll start getting
|
||||
errors like "WARNING: Mixing fork() and threads detected; memory leaked."
|
||||
'''
|
||||
# Init fileserver manager
|
||||
# Avoid circular import
|
||||
import salt.fileserver
|
||||
self.fileserver = salt.fileserver.Fileserver(self.opts)
|
||||
# Load Runners
|
||||
ropts = dict(self.opts)
|
||||
|
@ -459,6 +459,8 @@ class Master(SMaster):
|
|||
)
|
||||
|
||||
if self.opts.get('fileserver_verify_config', True):
|
||||
# Avoid circular import
|
||||
import salt.fileserver
|
||||
fileserver = salt.fileserver.Fileserver(self.opts)
|
||||
if not fileserver.servers:
|
||||
errors.append(
|
||||
|
@ -494,16 +496,15 @@ class Master(SMaster):
|
|||
if non_legacy_git_pillars:
|
||||
try:
|
||||
new_opts = copy.deepcopy(self.opts)
|
||||
from salt.pillar.git_pillar \
|
||||
import PER_REMOTE_OVERRIDES as per_remote_overrides, \
|
||||
PER_REMOTE_ONLY as per_remote_only
|
||||
import salt.pillar.git_pillar
|
||||
for repo in non_legacy_git_pillars:
|
||||
new_opts['ext_pillar'] = [repo]
|
||||
try:
|
||||
git_pillar = salt.utils.gitfs.GitPillar(new_opts)
|
||||
git_pillar.init_remotes(repo['git'],
|
||||
per_remote_overrides,
|
||||
per_remote_only)
|
||||
git_pillar.init_remotes(
|
||||
repo['git'],
|
||||
salt.pillar.git_pillar.PER_REMOTE_OVERRIDES,
|
||||
salt.pillar.git_pillar.PER_REMOTE_ONLY)
|
||||
except FileserverConfigError as exc:
|
||||
critical_errors.append(exc.strerror)
|
||||
finally:
|
||||
|
@ -973,6 +974,8 @@ class AESFuncs(object):
|
|||
'''
|
||||
Set the local file objects from the file server interface
|
||||
'''
|
||||
# Avoid circular import
|
||||
import salt.fileserver
|
||||
self.fs_ = salt.fileserver.Fileserver(self.opts)
|
||||
self._serve_file = self.fs_.serve_file
|
||||
self._file_find = self.fs_._find_file
|
||||
|
|
294
salt/minion.py
294
salt/minion.py
|
@ -100,6 +100,7 @@ import salt.defaults.exitcodes
|
|||
import salt.cli.daemons
|
||||
import salt.log.setup
|
||||
|
||||
import salt.utils.dictupdate
|
||||
from salt.config import DEFAULT_MINION_OPTS
|
||||
from salt.defaults import DEFAULT_TARGET_DELIM
|
||||
from salt.executors import FUNCTION_EXECUTORS
|
||||
|
@ -861,6 +862,10 @@ class MinionManager(MinionBase):
|
|||
failed = False
|
||||
while True:
|
||||
try:
|
||||
if minion.opts.get('beacons_before_connect', False):
|
||||
minion.setup_beacons(before_connect=True)
|
||||
if minion.opts.get('scheduler_before_connect', False):
|
||||
minion.setup_scheduler(before_connect=True)
|
||||
yield minion.connect_master(failed=failed)
|
||||
minion.tune_in(start=False)
|
||||
break
|
||||
|
@ -934,7 +939,8 @@ class Minion(MinionBase):
|
|||
# Flag meaning minion has finished initialization including first connect to the master.
|
||||
# True means the Minion is fully functional and ready to handle events.
|
||||
self.ready = False
|
||||
self.jid_queue = jid_queue
|
||||
self.jid_queue = jid_queue or []
|
||||
self.periodic_callbacks = {}
|
||||
|
||||
if io_loop is None:
|
||||
if HAS_ZMQ:
|
||||
|
@ -966,6 +972,19 @@ class Minion(MinionBase):
|
|||
# post_master_init
|
||||
if not salt.utils.is_proxy():
|
||||
self.opts['grains'] = salt.loader.grains(opts)
|
||||
else:
|
||||
if self.opts.get('beacons_before_connect', False):
|
||||
log.warning(
|
||||
'\'beacons_before_connect\' is not supported '
|
||||
'for proxy minions. Setting to False'
|
||||
)
|
||||
self.opts['beacons_before_connect'] = False
|
||||
if self.opts.get('scheduler_before_connect', False):
|
||||
log.warning(
|
||||
'\'scheduler_before_connect\' is not supported '
|
||||
'for proxy minions. Setting to False'
|
||||
)
|
||||
self.opts['scheduler_before_connect'] = False
|
||||
|
||||
log.info('Creating minion process manager')
|
||||
|
||||
|
@ -1069,19 +1088,22 @@ class Minion(MinionBase):
|
|||
pillarenv=self.opts.get('pillarenv')
|
||||
).compile_pillar()
|
||||
|
||||
self.functions, self.returners, self.function_errors, self.executors = self._load_modules()
|
||||
self.serial = salt.payload.Serial(self.opts)
|
||||
self.mod_opts = self._prep_mod_opts()
|
||||
self.matcher = Matcher(self.opts, self.functions)
|
||||
self.beacons = salt.beacons.Beacon(self.opts, self.functions)
|
||||
uid = salt.utils.get_uid(user=self.opts.get('user', None))
|
||||
self.proc_dir = get_proc_dir(self.opts['cachedir'], uid=uid)
|
||||
if not self.ready:
|
||||
self._setup_core()
|
||||
elif self.connected and self.opts['pillar']:
|
||||
# The pillar has changed due to the connection to the master.
|
||||
# Reload the functions so that they can use the new pillar data.
|
||||
self.functions, self.returners, self.function_errors, self.executors = self._load_modules()
|
||||
if hasattr(self, 'schedule'):
|
||||
self.schedule.functions = self.functions
|
||||
self.schedule.returners = self.returners
|
||||
|
||||
self.schedule = salt.utils.schedule.Schedule(
|
||||
self.opts,
|
||||
self.functions,
|
||||
self.returners,
|
||||
cleanup=[master_event(type='alive')])
|
||||
if not hasattr(self, 'schedule'):
|
||||
self.schedule = salt.utils.schedule.Schedule(
|
||||
self.opts,
|
||||
self.functions,
|
||||
self.returners,
|
||||
cleanup=[master_event(type='alive')])
|
||||
|
||||
# add default scheduling jobs to the minions scheduler
|
||||
if self.opts['mine_enabled'] and 'mine.update' in self.functions:
|
||||
|
@ -1135,9 +1157,6 @@ class Minion(MinionBase):
|
|||
self.schedule.delete_job(master_event(type='alive', master=self.opts['master']), persist=True)
|
||||
self.schedule.delete_job(master_event(type='failback'), persist=True)
|
||||
|
||||
self.grains_cache = self.opts['grains']
|
||||
self.ready = True
|
||||
|
||||
def _return_retry_timer(self):
|
||||
'''
|
||||
Based on the minion configuration, either return a randomized timer or
|
||||
|
@ -1599,13 +1618,24 @@ class Minion(MinionBase):
|
|||
minion side execution.
|
||||
'''
|
||||
salt.utils.appendproctitle('{0}._thread_multi_return {1}'.format(cls.__name__, data['jid']))
|
||||
ret = {
|
||||
'return': {},
|
||||
'retcode': {},
|
||||
'success': {}
|
||||
}
|
||||
for ind in range(0, len(data['fun'])):
|
||||
ret['success'][data['fun'][ind]] = False
|
||||
multifunc_ordered = opts.get('multifunc_ordered', False)
|
||||
num_funcs = len(data['fun'])
|
||||
if multifunc_ordered:
|
||||
ret = {
|
||||
'return': [None] * num_funcs,
|
||||
'retcode': [None] * num_funcs,
|
||||
'success': [False] * num_funcs
|
||||
}
|
||||
else:
|
||||
ret = {
|
||||
'return': {},
|
||||
'retcode': {},
|
||||
'success': {}
|
||||
}
|
||||
|
||||
for ind in range(0, num_funcs):
|
||||
if not multifunc_ordered:
|
||||
ret['success'][data['fun'][ind]] = False
|
||||
try:
|
||||
if minion_instance.connected and minion_instance.opts['pillar'].get('minion_blackout', False):
|
||||
# this minion is blacked out. Only allow saltutil.refresh_pillar
|
||||
|
@ -1620,12 +1650,20 @@ class Minion(MinionBase):
|
|||
data['arg'][ind],
|
||||
data)
|
||||
minion_instance.functions.pack['__context__']['retcode'] = 0
|
||||
ret['return'][data['fun'][ind]] = func(*args, **kwargs)
|
||||
ret['retcode'][data['fun'][ind]] = minion_instance.functions.pack['__context__'].get(
|
||||
'retcode',
|
||||
0
|
||||
)
|
||||
ret['success'][data['fun'][ind]] = True
|
||||
if multifunc_ordered:
|
||||
ret['return'][ind] = func(*args, **kwargs)
|
||||
ret['retcode'][ind] = minion_instance.functions.pack['__context__'].get(
|
||||
'retcode',
|
||||
0
|
||||
)
|
||||
ret['success'][ind] = True
|
||||
else:
|
||||
ret['return'][data['fun'][ind]] = func(*args, **kwargs)
|
||||
ret['retcode'][data['fun'][ind]] = minion_instance.functions.pack['__context__'].get(
|
||||
'retcode',
|
||||
0
|
||||
)
|
||||
ret['success'][data['fun'][ind]] = True
|
||||
except Exception as exc:
|
||||
trb = traceback.format_exc()
|
||||
log.warning(
|
||||
|
@ -1633,7 +1671,10 @@ class Minion(MinionBase):
|
|||
exc
|
||||
)
|
||||
)
|
||||
ret['return'][data['fun'][ind]] = trb
|
||||
if multifunc_ordered:
|
||||
ret['return'][ind] = trb
|
||||
else:
|
||||
ret['return'][data['fun'][ind]] = trb
|
||||
ret['jid'] = data['jid']
|
||||
ret['fun'] = data['fun']
|
||||
ret['fun_args'] = data['arg']
|
||||
|
@ -1873,6 +1914,8 @@ class Minion(MinionBase):
|
|||
func = data.get('func', None)
|
||||
name = data.get('name', None)
|
||||
beacon_data = data.get('beacon_data', None)
|
||||
include_pillar = data.get(u'include_pillar', None)
|
||||
include_opts = data.get(u'include_opts', None)
|
||||
|
||||
if func == 'add':
|
||||
self.beacons.add_beacon(name, beacon_data)
|
||||
|
@ -1889,7 +1932,9 @@ class Minion(MinionBase):
|
|||
elif func == 'disable_beacon':
|
||||
self.beacons.disable_beacon(name)
|
||||
elif func == 'list':
|
||||
self.beacons.list_beacons()
|
||||
self.beacons.list_beacons(include_opts, include_pillar)
|
||||
elif func == u'list_available':
|
||||
self.beacons.list_available_beacons()
|
||||
|
||||
def environ_setenv(self, tag, data):
|
||||
'''
|
||||
|
@ -2153,6 +2198,118 @@ class Minion(MinionBase):
|
|||
except (ValueError, NameError):
|
||||
pass
|
||||
|
||||
def _setup_core(self):
|
||||
'''
|
||||
Set up the core minion attributes.
|
||||
This is safe to call multiple times.
|
||||
'''
|
||||
if not self.ready:
|
||||
# First call. Initialize.
|
||||
self.functions, self.returners, self.function_errors, self.executors = self._load_modules()
|
||||
self.serial = salt.payload.Serial(self.opts)
|
||||
self.mod_opts = self._prep_mod_opts()
|
||||
self.matcher = Matcher(self.opts, self.functions)
|
||||
uid = salt.utils.get_uid(user=self.opts.get('user', None))
|
||||
self.proc_dir = get_proc_dir(self.opts['cachedir'], uid=uid)
|
||||
self.grains_cache = self.opts['grains']
|
||||
self.ready = True
|
||||
|
||||
def setup_beacons(self, before_connect=False):
|
||||
'''
|
||||
Set up the beacons.
|
||||
This is safe to call multiple times.
|
||||
'''
|
||||
self._setup_core()
|
||||
|
||||
loop_interval = self.opts['loop_interval']
|
||||
new_periodic_callbacks = {}
|
||||
|
||||
if 'beacons' not in self.periodic_callbacks:
|
||||
self.beacons = salt.beacons.Beacon(self.opts, self.functions)
|
||||
|
||||
def handle_beacons():
|
||||
# Process Beacons
|
||||
beacons = None
|
||||
try:
|
||||
beacons = self.process_beacons(self.functions)
|
||||
except Exception:
|
||||
log.critical('The beacon errored: ', exc_info=True)
|
||||
if beacons and self.connected:
|
||||
self._fire_master(events=beacons)
|
||||
|
||||
new_periodic_callbacks['beacons'] = tornado.ioloop.PeriodicCallback(handle_beacons, loop_interval * 1000, io_loop=self.io_loop)
|
||||
if before_connect:
|
||||
# Make sure there is a chance for one iteration to occur before connect
|
||||
handle_beacons()
|
||||
|
||||
if 'cleanup' not in self.periodic_callbacks:
|
||||
new_periodic_callbacks['cleanup'] = tornado.ioloop.PeriodicCallback(self._fallback_cleanups, loop_interval * 1000, io_loop=self.io_loop)
|
||||
|
||||
# start all the other callbacks
|
||||
for periodic_cb in six.itervalues(new_periodic_callbacks):
|
||||
periodic_cb.start()
|
||||
|
||||
self.periodic_callbacks.update(new_periodic_callbacks)
|
||||
|
||||
def setup_scheduler(self, before_connect=False):
|
||||
'''
|
||||
Set up the scheduler.
|
||||
This is safe to call multiple times.
|
||||
'''
|
||||
self._setup_core()
|
||||
|
||||
loop_interval = self.opts['loop_interval']
|
||||
new_periodic_callbacks = {}
|
||||
|
||||
if 'schedule' not in self.periodic_callbacks:
|
||||
if 'schedule' not in self.opts:
|
||||
self.opts['schedule'] = {}
|
||||
if not hasattr(self, 'schedule'):
|
||||
self.schedule = salt.utils.schedule.Schedule(
|
||||
self.opts,
|
||||
self.functions,
|
||||
self.returners,
|
||||
cleanup=[master_event(type='alive')])
|
||||
|
||||
try:
|
||||
if self.opts['grains_refresh_every']: # If exists and is not zero. In minutes, not seconds!
|
||||
if self.opts['grains_refresh_every'] > 1:
|
||||
log.debug(
|
||||
'Enabling the grains refresher. Will run every {0} minutes.'.format(
|
||||
self.opts['grains_refresh_every'])
|
||||
)
|
||||
else: # Clean up minute vs. minutes in log message
|
||||
log.debug(
|
||||
'Enabling the grains refresher. Will run every {0} minute.'.format(
|
||||
self.opts['grains_refresh_every'])
|
||||
)
|
||||
self._refresh_grains_watcher(
|
||||
abs(self.opts['grains_refresh_every'])
|
||||
)
|
||||
except Exception as exc:
|
||||
log.error(
|
||||
'Exception occurred in attempt to initialize grain refresh routine during minion tune-in: {0}'.format(
|
||||
exc)
|
||||
)
|
||||
|
||||
# TODO: actually listen to the return and change period
|
||||
def handle_schedule():
|
||||
self.process_schedule(self, loop_interval)
|
||||
new_periodic_callbacks['schedule'] = tornado.ioloop.PeriodicCallback(handle_schedule, 1000, io_loop=self.io_loop)
|
||||
|
||||
if before_connect:
|
||||
# Make sure there is a chance for one iteration to occur before connect
|
||||
handle_schedule()
|
||||
|
||||
if 'cleanup' not in self.periodic_callbacks:
|
||||
new_periodic_callbacks['cleanup'] = tornado.ioloop.PeriodicCallback(self._fallback_cleanups, loop_interval * 1000, io_loop=self.io_loop)
|
||||
|
||||
# start all the other callbacks
|
||||
for periodic_cb in six.itervalues(new_periodic_callbacks):
|
||||
periodic_cb.start()
|
||||
|
||||
self.periodic_callbacks.update(new_periodic_callbacks)
|
||||
|
||||
# Main Minion Tune In
|
||||
def tune_in(self, start=True):
|
||||
'''
|
||||
|
@ -2164,6 +2321,10 @@ class Minion(MinionBase):
|
|||
log.debug('Minion \'{0}\' trying to tune in'.format(self.opts['id']))
|
||||
|
||||
if start:
|
||||
if self.opts.get('beacons_before_connect', False):
|
||||
self.setup_beacons(before_connect=True)
|
||||
if self.opts.get('scheduler_before_connect', False):
|
||||
self.setup_scheduler(before_connect=True)
|
||||
self.sync_connect_master()
|
||||
if self.connected:
|
||||
self._fire_master_minion_start()
|
||||
|
@ -2178,31 +2339,9 @@ class Minion(MinionBase):
|
|||
# On first startup execute a state run if configured to do so
|
||||
self._state_run()
|
||||
|
||||
loop_interval = self.opts['loop_interval']
|
||||
self.setup_beacons()
|
||||
self.setup_scheduler()
|
||||
|
||||
try:
|
||||
if self.opts['grains_refresh_every']: # If exists and is not zero. In minutes, not seconds!
|
||||
if self.opts['grains_refresh_every'] > 1:
|
||||
log.debug(
|
||||
'Enabling the grains refresher. Will run every {0} minutes.'.format(
|
||||
self.opts['grains_refresh_every'])
|
||||
)
|
||||
else: # Clean up minute vs. minutes in log message
|
||||
log.debug(
|
||||
'Enabling the grains refresher. Will run every {0} minute.'.format(
|
||||
self.opts['grains_refresh_every'])
|
||||
|
||||
)
|
||||
self._refresh_grains_watcher(
|
||||
abs(self.opts['grains_refresh_every'])
|
||||
)
|
||||
except Exception as exc:
|
||||
log.error(
|
||||
'Exception occurred in attempt to initialize grain refresh routine during minion tune-in: {0}'.format(
|
||||
exc)
|
||||
)
|
||||
|
||||
self.periodic_callbacks = {}
|
||||
# schedule the stuff that runs every interval
|
||||
ping_interval = self.opts.get('ping_interval', 0) * 60
|
||||
if ping_interval > 0 and self.connected:
|
||||
|
@ -2220,30 +2359,7 @@ class Minion(MinionBase):
|
|||
except Exception:
|
||||
log.warning('Attempt to ping master failed.', exc_on_loglevel=logging.DEBUG)
|
||||
self.periodic_callbacks['ping'] = tornado.ioloop.PeriodicCallback(ping_master, ping_interval * 1000, io_loop=self.io_loop)
|
||||
|
||||
self.periodic_callbacks['cleanup'] = tornado.ioloop.PeriodicCallback(self._fallback_cleanups, loop_interval * 1000, io_loop=self.io_loop)
|
||||
|
||||
def handle_beacons():
|
||||
# Process Beacons
|
||||
beacons = None
|
||||
try:
|
||||
beacons = self.process_beacons(self.functions)
|
||||
except Exception:
|
||||
log.critical('The beacon errored: ', exc_info=True)
|
||||
if beacons and self.connected:
|
||||
self._fire_master(events=beacons, sync=False)
|
||||
|
||||
self.periodic_callbacks['beacons'] = tornado.ioloop.PeriodicCallback(handle_beacons, loop_interval * 1000, io_loop=self.io_loop)
|
||||
|
||||
# TODO: actually listen to the return and change period
|
||||
def handle_schedule():
|
||||
self.process_schedule(self, loop_interval)
|
||||
if hasattr(self, 'schedule'):
|
||||
self.periodic_callbacks['schedule'] = tornado.ioloop.PeriodicCallback(handle_schedule, 1000, io_loop=self.io_loop)
|
||||
|
||||
# start all the other callbacks
|
||||
for periodic_cb in six.itervalues(self.periodic_callbacks):
|
||||
periodic_cb.start()
|
||||
self.periodic_callbacks['ping'].start()
|
||||
|
||||
# add handler to subscriber
|
||||
if hasattr(self, 'pub_channel') and self.pub_channel is not None:
|
||||
|
@ -3120,6 +3236,26 @@ class ProxyMinion(Minion):
|
|||
if 'proxy' not in self.opts:
|
||||
self.opts['proxy'] = self.opts['pillar']['proxy']
|
||||
|
||||
if self.opts.get('proxy_merge_pillar_in_opts'):
|
||||
# Override proxy opts with pillar data when the user required.
|
||||
self.opts = salt.utils.dictupdate.merge(self.opts,
|
||||
self.opts['pillar'],
|
||||
strategy=self.opts.get('proxy_merge_pillar_in_opts_strategy'),
|
||||
merge_lists=self.opts.get('proxy_deep_merge_pillar_in_opts', False))
|
||||
elif self.opts.get('proxy_mines_pillar'):
|
||||
# Even when not required, some details such as mine configuration
|
||||
# should be merged anyway whenever possible.
|
||||
if 'mine_interval' in self.opts['pillar']:
|
||||
self.opts['mine_interval'] = self.opts['pillar']['mine_interval']
|
||||
if 'mine_functions' in self.opts['pillar']:
|
||||
general_proxy_mines = self.opts.get('mine_functions', [])
|
||||
specific_proxy_mines = self.opts['pillar']['mine_functions']
|
||||
try:
|
||||
self.opts['mine_functions'] = general_proxy_mines + specific_proxy_mines
|
||||
except TypeError as terr:
|
||||
log.error('Unable to merge mine functions from the pillar in the opts, for proxy {}'.format(
|
||||
self.opts['id']))
|
||||
|
||||
fq_proxyname = self.opts['proxy']['proxytype']
|
||||
|
||||
# Need to load the modules so they get all the dunder variables
|
||||
|
|
|
@ -125,7 +125,7 @@ def cert(name,
|
|||
salt 'gitlab.example.com' acme.cert dev.example.com "[gitlab.example.com]" test_cert=True renew=14 webroot=/opt/gitlab/embedded/service/gitlab-rails/public
|
||||
'''
|
||||
|
||||
cmd = [LEA, 'certonly', '--quiet']
|
||||
cmd = [LEA, 'certonly', '--non-interactive']
|
||||
|
||||
cert_file = _cert_file(name, 'cert')
|
||||
if not __salt__['file.file_exists'](cert_file):
|
||||
|
|
|
@ -12,6 +12,7 @@ import logging
|
|||
|
||||
# Import Salt libs
|
||||
import salt.utils
|
||||
import salt.utils.path
|
||||
|
||||
# Import 3rd-party libs
|
||||
import salt.ext.six as six
|
||||
|
@ -241,4 +242,4 @@ def _read_link(name):
|
|||
Throws an OSError if the link does not exist
|
||||
'''
|
||||
alt_link_path = '/etc/alternatives/{0}'.format(name)
|
||||
return os.readlink(alt_link_path)
|
||||
return salt.utils.path.readlink(alt_link_path)
|
||||
|
|
|
@ -446,11 +446,15 @@ def config(name, config, edit=True):
|
|||
salt '*' apache.config /etc/httpd/conf.d/ports.conf config="[{'Listen': '22'}]"
|
||||
'''
|
||||
|
||||
configs = []
|
||||
for entry in config:
|
||||
key = next(six.iterkeys(entry))
|
||||
configs = _parse_config(entry[key], key)
|
||||
if edit:
|
||||
with salt.utils.fopen(name, 'w') as configfile:
|
||||
configfile.write('# This file is managed by Salt.\n')
|
||||
configfile.write(configs)
|
||||
return configs
|
||||
configs.append(_parse_config(entry[key], key))
|
||||
|
||||
# Python auto-correct line endings
|
||||
configstext = "\n".join(configs)
|
||||
if edit:
|
||||
with salt.utils.fopen(name, 'w') as configfile:
|
||||
configfile.write('# This file is managed by Salt.\n')
|
||||
configfile.write(configstext)
|
||||
return configstext
|
||||
|
|
|
@ -29,7 +29,6 @@ import json
|
|||
import yaml
|
||||
# pylint: disable=no-name-in-module,import-error,redefined-builtin
|
||||
import salt.ext.six as six
|
||||
from salt.ext.six.moves import range
|
||||
from salt.ext.six.moves.urllib.error import HTTPError
|
||||
from salt.ext.six.moves.urllib.request import Request as _Request, urlopen as _urlopen
|
||||
# pylint: enable=no-name-in-module,import-error,redefined-builtin
|
||||
|
@ -93,11 +92,15 @@ __virtualname__ = 'pkg'
|
|||
|
||||
def __virtual__():
|
||||
'''
|
||||
Confirm this module is on a Debian based system
|
||||
Confirm this module is on a Debian-based system
|
||||
'''
|
||||
if __grains__.get('os_family') in ('Kali', 'Debian', 'neon'):
|
||||
return __virtualname__
|
||||
elif __grains__.get('os_family', False) == 'Cumulus':
|
||||
# If your minion is running an OS which is Debian-based but does not have
|
||||
# an "os_family" grain of Debian, then the proper fix is NOT to check for
|
||||
# the minion's "os_family" grain here in the __virtual__. The correct fix
|
||||
# is to add the value from the minion's "os" grain to the _OS_FAMILY_MAP
|
||||
# dict in salt/grains/core.py, so that we assign the correct "os_family"
|
||||
# grain to the minion.
|
||||
if __grains__.get('os_family') == 'Debian':
|
||||
return __virtualname__
|
||||
return (False, 'The pkg module could not be loaded: unsupported OS family')
|
||||
|
||||
|
@ -1554,7 +1557,7 @@ def _consolidate_repo_sources(sources):
|
|||
combined_comps = set(repo.comps).union(set(combined.comps))
|
||||
consolidated[key].comps = list(combined_comps)
|
||||
else:
|
||||
consolidated[key] = sourceslist.SourceEntry(_strip_uri(repo.line))
|
||||
consolidated[key] = sourceslist.SourceEntry(salt.utils.pkg.deb.strip_uri(repo.line))
|
||||
|
||||
if repo.file != base_file:
|
||||
delete_files.add(repo.file)
|
||||
|
@ -1662,7 +1665,7 @@ def list_repos():
|
|||
repo['dist'] = source.dist
|
||||
repo['type'] = source.type
|
||||
repo['uri'] = source.uri.rstrip('/')
|
||||
repo['line'] = _strip_uri(source.line.strip())
|
||||
repo['line'] = salt.utils.pkg.deb.strip_uri(source.line.strip())
|
||||
repo['architectures'] = getattr(source, 'architectures', [])
|
||||
repos.setdefault(source.uri, []).append(repo)
|
||||
return repos
|
||||
|
@ -2408,18 +2411,6 @@ def file_dict(*packages):
|
|||
return __salt__['lowpkg.file_dict'](*packages)
|
||||
|
||||
|
||||
def _strip_uri(repo):
|
||||
'''
|
||||
Remove the trailing slash from the URI in a repo definition
|
||||
'''
|
||||
splits = repo.split()
|
||||
for idx in range(len(splits)):
|
||||
if any(splits[idx].startswith(x)
|
||||
for x in ('http://', 'https://', 'ftp://')):
|
||||
splits[idx] = splits[idx].rstrip('/')
|
||||
return ' '.join(splits)
|
||||
|
||||
|
||||
def expand_repo_def(**kwargs):
|
||||
'''
|
||||
Take a repository definition and expand it to the full pkg repository dict
|
||||
|
@ -2435,7 +2426,7 @@ def expand_repo_def(**kwargs):
|
|||
_check_apt()
|
||||
|
||||
sanitized = {}
|
||||
repo = _strip_uri(kwargs['repo'])
|
||||
repo = salt.utils.pkg.deb.strip_uri(kwargs['repo'])
|
||||
if repo.startswith('ppa:') and __grains__['os'] in ('Ubuntu', 'Mint', 'neon'):
|
||||
dist = __grains__['lsb_distrib_codename']
|
||||
owner_name, ppa_name = repo[4:].split('/', 1)
|
||||
|
|
|
@ -498,16 +498,16 @@ def tar(options, tarfile, sources=None, dest=None,
|
|||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' archive.tar -cjvf /tmp/salt.tar.bz2 {{grains.saltpath}} template=jinja
|
||||
salt '*' archive.tar cjvf /tmp/salt.tar.bz2 {{grains.saltpath}} template=jinja
|
||||
|
||||
CLI Examples:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Create a tarfile
|
||||
salt '*' archive.tar -cjvf /tmp/tarfile.tar.bz2 /tmp/file_1,/tmp/file_2
|
||||
salt '*' archive.tar cjvf /tmp/tarfile.tar.bz2 /tmp/file_1,/tmp/file_2
|
||||
# Create a tarfile using globbing (2017.7.0 and later)
|
||||
salt '*' archive.tar -cjvf /tmp/tarfile.tar.bz2 '/tmp/file_*'
|
||||
salt '*' archive.tar cjvf /tmp/tarfile.tar.bz2 '/tmp/file_*'
|
||||
# Unpack a tarfile
|
||||
salt '*' archive.tar xf foo.tar dest=/target/directory
|
||||
'''
|
||||
|
|
|
@ -169,6 +169,9 @@ def atrm(*args):
|
|||
if not args:
|
||||
return {'jobs': {'removed': [], 'tag': None}}
|
||||
|
||||
# Convert all to strings
|
||||
args = [str(arg) for arg in args]
|
||||
|
||||
if args[0] == 'all':
|
||||
if len(args) > 1:
|
||||
opts = list(list(map(str, [j['job'] for j in atq(args[1])['jobs']])))
|
||||
|
@ -178,7 +181,7 @@ def atrm(*args):
|
|||
ret = {'jobs': {'removed': opts, 'tag': None}}
|
||||
else:
|
||||
opts = list(list(map(str, [i['job'] for i in atq()['jobs']
|
||||
if i['job'] in args])))
|
||||
if str(i['job']) in args])))
|
||||
ret = {'jobs': {'removed': opts, 'tag': None}}
|
||||
|
||||
# Shim to produce output similar to what __virtual__() should do
|
||||
|
|
|
@ -27,12 +27,22 @@ __func_alias__ = {
|
|||
}
|
||||
|
||||
|
||||
def list_(return_yaml=True):
|
||||
def list_(return_yaml=True,
|
||||
include_pillar=True,
|
||||
include_opts=True):
|
||||
'''
|
||||
List the beacons currently configured on the minion
|
||||
|
||||
:param return_yaml: Whether to return YAML formatted output, default True
|
||||
:return: List of currently configured Beacons.
|
||||
:param return_yaml: Whether to return YAML formatted output,
|
||||
default True
|
||||
|
||||
:param include_pillar: Whether to include beacons that are
|
||||
configured in pillar, default is True.
|
||||
|
||||
:param include_opts: Whether to include beacons that are
|
||||
configured in opts, default is True.
|
||||
|
||||
:return: List of currently configured Beacons.
|
||||
|
||||
CLI Example:
|
||||
|
||||
|
@ -45,7 +55,10 @@ def list_(return_yaml=True):
|
|||
|
||||
try:
|
||||
eventer = salt.utils.event.get_event('minion', opts=__opts__)
|
||||
res = __salt__['event.fire']({'func': 'list'}, 'manage_beacons')
|
||||
res = __salt__['event.fire']({'func': 'list',
|
||||
'include_pillar': include_pillar,
|
||||
'include_opts': include_opts},
|
||||
'manage_beacons')
|
||||
if res:
|
||||
event_ret = eventer.get_event(tag='/salt/minion/minion_beacons_list_complete', wait=30)
|
||||
log.debug('event_ret {0}'.format(event_ret))
|
||||
|
@ -69,6 +82,47 @@ def list_(return_yaml=True):
|
|||
return {'beacons': {}}
|
||||
|
||||
|
||||
def list_available(return_yaml=True):
|
||||
'''
|
||||
List the beacons currently available on the minion
|
||||
|
||||
:param return_yaml: Whether to return YAML formatted output, default True
|
||||
:return: List of currently configured Beacons.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' beacons.list_available
|
||||
|
||||
'''
|
||||
beacons = None
|
||||
|
||||
try:
|
||||
eventer = salt.utils.event.get_event('minion', opts=__opts__)
|
||||
res = __salt__['event.fire']({'func': 'list_available'}, 'manage_beacons')
|
||||
if res:
|
||||
event_ret = eventer.get_event(tag='/salt/minion/minion_beacons_list_available_complete', wait=30)
|
||||
if event_ret and event_ret['complete']:
|
||||
beacons = event_ret['beacons']
|
||||
except KeyError:
|
||||
# Effectively a no-op, since we can't really return without an event system
|
||||
ret = {}
|
||||
ret['result'] = False
|
||||
ret['comment'] = 'Event module not available. Beacon add failed.'
|
||||
return ret
|
||||
|
||||
if beacons:
|
||||
if return_yaml:
|
||||
tmp = {'beacons': beacons}
|
||||
yaml_out = yaml.safe_dump(tmp, default_flow_style=False)
|
||||
return yaml_out
|
||||
else:
|
||||
return beacons
|
||||
else:
|
||||
return {'beacons': {}}
|
||||
|
||||
|
||||
def add(name, beacon_data, **kwargs):
|
||||
'''
|
||||
Add a beacon on the minion
|
||||
|
@ -91,6 +145,10 @@ def add(name, beacon_data, **kwargs):
|
|||
ret['comment'] = 'Beacon {0} is already configured.'.format(name)
|
||||
return ret
|
||||
|
||||
if name not in list_available(return_yaml=False):
|
||||
ret['comment'] = 'Beacon "{0}" is not available.'.format(name)
|
||||
return ret
|
||||
|
||||
if 'test' in kwargs and kwargs['test']:
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Beacon: {0} would be added.'.format(name)
|
||||
|
@ -130,7 +188,10 @@ def add(name, beacon_data, **kwargs):
|
|||
if name in beacons and beacons[name] == beacon_data:
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Added beacon: {0}.'.format(name)
|
||||
return ret
|
||||
else:
|
||||
ret['result'] = False
|
||||
ret['comment'] = event_ret['comment']
|
||||
return ret
|
||||
except KeyError:
|
||||
# Effectively a no-op, since we can't really return without an event system
|
||||
ret['comment'] = 'Event module not available. Beacon add failed.'
|
||||
|
@ -215,7 +276,10 @@ def modify(name, beacon_data, **kwargs):
|
|||
if name in beacons and beacons[name] == beacon_data:
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Modified beacon: {0}.'.format(name)
|
||||
return ret
|
||||
else:
|
||||
ret['result'] = False
|
||||
ret['comment'] = event_ret['comment']
|
||||
return ret
|
||||
except KeyError:
|
||||
# Effectively a no-op, since we can't really return without an event system
|
||||
ret['comment'] = 'Event module not available. Beacon add failed.'
|
||||
|
@ -257,6 +321,9 @@ def delete(name, **kwargs):
|
|||
ret['result'] = True
|
||||
ret['comment'] = 'Deleted beacon: {0}.'.format(name)
|
||||
return ret
|
||||
else:
|
||||
ret['result'] = False
|
||||
ret['comment'] = event_ret['comment']
|
||||
except KeyError:
|
||||
# Effectively a no-op, since we can't really return without an event system
|
||||
ret['comment'] = 'Event module not available. Beacon add failed.'
|
||||
|
@ -279,7 +346,7 @@ def save():
|
|||
ret = {'comment': [],
|
||||
'result': True}
|
||||
|
||||
beacons = list_(return_yaml=False)
|
||||
beacons = list_(return_yaml=False, include_pillar=False)
|
||||
|
||||
# move this file into an configurable opt
|
||||
sfn = '{0}/{1}/beacons.conf'.format(__opts__['config_dir'],
|
||||
|
@ -332,7 +399,7 @@ def enable(**kwargs):
|
|||
else:
|
||||
ret['result'] = False
|
||||
ret['comment'] = 'Failed to enable beacons on minion.'
|
||||
return ret
|
||||
return ret
|
||||
except KeyError:
|
||||
# Effectively a no-op, since we can't really return without an event system
|
||||
ret['comment'] = 'Event module not available. Beacons enable job failed.'
|
||||
|
@ -372,7 +439,7 @@ def disable(**kwargs):
|
|||
else:
|
||||
ret['result'] = False
|
||||
ret['comment'] = 'Failed to disable beacons on minion.'
|
||||
return ret
|
||||
return ret
|
||||
except KeyError:
|
||||
# Effectively a no-op, since we can't really return without an event system
|
||||
ret['comment'] = 'Event module not available. Beacons enable job failed.'
|
||||
|
@ -435,7 +502,10 @@ def enable_beacon(name, **kwargs):
|
|||
else:
|
||||
ret['result'] = False
|
||||
ret['comment'] = 'Failed to enable beacon {0} on minion.'.format(name)
|
||||
return ret
|
||||
else:
|
||||
ret['result'] = False
|
||||
ret['comment'] = event_ret['comment']
|
||||
return ret
|
||||
except KeyError:
|
||||
# Effectively a no-op, since we can't really return without an event system
|
||||
ret['comment'] = 'Event module not available. Beacon enable job failed.'
|
||||
|
@ -488,7 +558,10 @@ def disable_beacon(name, **kwargs):
|
|||
else:
|
||||
ret['result'] = False
|
||||
ret['comment'] = 'Failed to disable beacon on minion.'
|
||||
return ret
|
||||
else:
|
||||
ret['result'] = False
|
||||
ret['comment'] = event_ret['comment']
|
||||
return ret
|
||||
except KeyError:
|
||||
# Effectively a no-op, since we can't really return without an event system
|
||||
ret['comment'] = 'Event module not available. Beacon disable job failed.'
|
||||
|
|
|
@ -51,6 +51,7 @@ import datetime
|
|||
import logging
|
||||
import json
|
||||
import sys
|
||||
import time
|
||||
import email.mime.multipart
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
@ -675,11 +676,23 @@ def get_scaling_policy_arn(as_group, scaling_policy_name, region=None,
|
|||
salt '*' boto_asg.get_scaling_policy_arn mygroup mypolicy
|
||||
'''
|
||||
conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile)
|
||||
policies = conn.get_all_policies(as_group=as_group)
|
||||
for policy in policies:
|
||||
if policy.name == scaling_policy_name:
|
||||
return policy.policy_arn
|
||||
log.error('Could not convert: {0}'.format(as_group))
|
||||
retries = 30
|
||||
while retries > 0:
|
||||
retries -= 1
|
||||
try:
|
||||
policies = conn.get_all_policies(as_group=as_group)
|
||||
for policy in policies:
|
||||
if policy.name == scaling_policy_name:
|
||||
return policy.policy_arn
|
||||
log.error('Could not convert: {0}'.format(as_group))
|
||||
return None
|
||||
except boto.exception.BotoServerError as e:
|
||||
if e.error_code != 'Throttling':
|
||||
raise
|
||||
log.debug('Throttled by API, will retry in 5 seconds')
|
||||
time.sleep(5)
|
||||
|
||||
log.error('Maximum number of retries exceeded')
|
||||
return None
|
||||
|
||||
|
||||
|
@ -761,11 +774,18 @@ def get_instances(name, lifecycle_state="InService", health_status="Healthy",
|
|||
# get full instance info, so that we can return the attribute
|
||||
instances = ec2_conn.get_only_instances(instance_ids=instance_ids)
|
||||
if attributes:
|
||||
return [[getattr(instance, attr).encode("ascii") for attr in attributes] for instance in instances]
|
||||
return [[_convert_attribute(instance, attr) for attr in attributes] for instance in instances]
|
||||
else:
|
||||
# properly handle case when not all instances have the requested attribute
|
||||
return [getattr(instance, attribute).encode("ascii") for instance in instances if getattr(instance, attribute)]
|
||||
return [getattr(instance, attribute).encode("ascii") for instance in instances]
|
||||
return [_convert_attribute(instance, attribute) for instance in instances if getattr(instance, attribute)]
|
||||
|
||||
|
||||
def _convert_attribute(instance, attribute):
|
||||
if attribute == "tags":
|
||||
tags = dict(getattr(instance, attribute))
|
||||
return {key.encode("utf-8"): value.encode("utf-8") for key, value in six.iteritems(tags)}
|
||||
|
||||
return getattr(instance, attribute).encode("ascii")
|
||||
|
||||
|
||||
def enter_standby(name, instance_ids, should_decrement_desired_capacity=False,
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue