Merge branch 'develop' into cmd_binary_stdin

This commit is contained in:
Herbert 2018-03-21 10:04:40 +01:00 committed by GitHub
commit 90453c9d71
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
87 changed files with 1631 additions and 3165 deletions

View File

@ -36,7 +36,7 @@ provisioner:
require_chef: false
remote_states:
name: git://github.com/saltstack/salt-jenkins.git
branch: oxygen
branch: 2018.3
repo: git
testingdir: /testing
salt_copy_filter:

View File

@ -29,6 +29,25 @@ load-plugins=saltpylint.pep8,
# Don't bump this values on PyLint 1.4.0 - Know bug that ignores the passed --rcfile
jobs=1
# List of blacklisted functions and suggested replacements
#
# NOTE: This pylint check will infer the full name of the function by walking
# back up from the function name to the parent, to the parent's parent, etc.,
# and this means that functions which come from platform-specific modules need
# to be referenced using name of the module from which the function was
# imported. This happens a lot in the os and os.path modules. Functions from
# os.path should be defined using posixpath.funcname and ntpath.funcname, while
# functions from os should be defined using posix.funcname and nt.funcname.
#
# When defining a blacklisted function, the format is:
#
# <func_name>=<suggested_replacement>
#
# The replacement text will be included in the alert message.
#
blacklisted-functions=posix.umask=salt.utils.files.set_umask or get_umask,
nt.umask=salt.utils.files.set_umask or get_umask
# Allow loading of arbitrary C extensions. Extensions are imported into the
# active Python interpreter and may run arbitrary code.
unsafe-load-any-extension=no

Binary file not shown.

Before

Width:  |  Height:  |  Size: 438 KiB

After

Width:  |  Height:  |  Size: 240 KiB

View File

@ -341,10 +341,15 @@ rst_prolog = """\
| <a href="https://repo.saltstack.com/windows/Salt-Minion-{release}-Py3-AMD64-Setup.exe.md5"><strong>md5</strong></a></p>
.. |osxdownload| raw:: html
.. |osxdownloadpy2| raw:: html
<p>x86_64: <a href="https://repo.saltstack.com/osx/salt-{release}-x86_64.pkg"><strong>salt-{release}-x86_64.pkg</strong></a>
| <a href="https://repo.saltstack.com/osx/salt-{release}-x86_64.pkg.md5"><strong>md5</strong></a></p>
<p>x86_64: <a href="https://repo.saltstack.com/osx/salt-{release}-py2-x86_64.pkg"><strong>salt-{release}-py2-x86_64.pkg</strong></a>
| <a href="https://repo.saltstack.com/osx/salt-{release}-py2-x86_64.pkg.md5"><strong>md5</strong></a></p>
.. |osxdownloadpy3| raw:: html
<p>x86_64: <a href="https://repo.saltstack.com/osx/salt-{release}-py3-x86_64.pkg"><strong>salt-{release}-py3-x86_64.pkg</strong></a>
| <a href="https://repo.saltstack.com/osx/salt-{release}-py3-x86_64.pkg.md5"><strong>md5</strong></a></p>
""".format(release=release)

View File

@ -344,7 +344,35 @@ be set in the configuration file to enable interfacing with GoGrid:
OpenStack
---------
.. automodule:: salt.cloud.clouds.openstack
Using Salt for OpenStack uses the `shade <https://docs.openstack.org/shade/latest/>` driver managed by the
openstack-infra team.
This driver can be configured using the ``/etc/openstack/clouds.yml`` file with
`os-client-config <https://docs.openstack.org/os-client-config/latest/>`
.. code-block:: yaml
myopenstack:
driver: openstack
region_name: RegionOne
cloud: mycloud
Or by just configuring the same auth block directly in the cloud provider config.
.. code-block:: yaml
myopenstack:
driver: openstack
region_name: RegionOne
auth:
username: 'demo'
password: secret
project_name: 'demo'
auth_url: 'http://openstack/identity'
Both of these methods support using the
`vendor <https://docs.openstack.org/os-client-config/latest/user/vendor-support.html>`
options.
For more information, look at :mod:`Openstack Cloud Driver Docs <salt.cloud.clouds.openstack>`
DigitalOcean
------------

View File

@ -0,0 +1,5 @@
==============================
Getting Started with Openstack
==============================
.. automodule:: salt.cloud.clouds.openstack

View File

@ -1,188 +0,0 @@
==============================
Getting Started With Rackspace
==============================
Rackspace is a major public cloud platform which may be configured using either
the `openstack` driver.
Dependencies
============
* Libcloud >= 0.13.2
Configuration
=============
To use the `openstack` driver (recommended), set up the cloud configuration at
``/etc/salt/cloud.providers`` or
``/etc/salt/cloud.providers.d/rackspace.conf``:
.. code-block:: yaml
my-rackspace-config:
# Set the location of the salt-master
#
minion:
master: saltmaster.example.com
# Configure Rackspace using the OpenStack plugin
#
identity_url: 'https://identity.api.rackspacecloud.com/v2.0/tokens'
compute_name: cloudServersOpenStack
protocol: ipv4
# Set the compute region:
#
compute_region: DFW
# Configure Rackspace authentication credentials
#
user: myname
tenant: 123456
apikey: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
driver: openstack
.. note::
.. versionchanged:: 2015.8.0
The ``provider`` parameter in cloud provider definitions was renamed to ``driver``. This
change was made to avoid confusion with the ``provider`` parameter that is used in cloud profile
definitions. Cloud provider definitions now use ``driver`` to refer to the Salt cloud module that
provides the underlying functionality to connect to a cloud host, while cloud profiles continue
to use ``provider`` to refer to provider configurations that you define.
Compute Region
==============
Rackspace currently has six compute regions which may be used:
.. code-block:: bash
DFW -> Dallas/Forth Worth
ORD -> Chicago
SYD -> Sydney
LON -> London
IAD -> Northern Virginia
HKG -> Hong Kong
Note: Currently the LON region is only available with a UK account, and UK accounts cannot access other regions
Authentication
==============
The ``user`` is the same user as is used to log into the Rackspace Control
Panel. The ``tenant`` and ``apikey`` can be found in the API Keys area of the
Control Panel. The ``apikey`` will be labeled as API Key (and may need to be
generated), and ``tenant`` will be labeled as Cloud Account Number.
An initial profile can be configured in ``/etc/salt/cloud.profiles`` or
``/etc/salt/cloud.profiles.d/rackspace.conf``:
.. code-block:: yaml
openstack_512:
provider: my-rackspace-config
size: 512 MB Standard
image: Ubuntu 12.04 LTS (Precise Pangolin)
To instantiate a machine based on this profile:
.. code-block:: bash
# salt-cloud -p openstack_512 myinstance
This will create a virtual machine at Rackspace with the name ``myinstance``.
This operation may take several minutes to complete, depending on the current
load at the Rackspace data center.
Once the instance has been created with salt-minion installed, connectivity to
it can be verified with Salt:
.. code-block:: bash
# salt myinstance test.ping
RackConnect Environments
------------------------
Rackspace offers a hybrid hosting configuration option called RackConnect that
allows you to use a physical firewall appliance with your cloud servers. When
this service is in use the public_ip assigned by nova will be replaced by a NAT
ip on the firewall. For salt-cloud to work properly it must use the newly
assigned "access ip" instead of the Nova assigned public ip. You can enable that
capability by adding this to your profiles:
.. code-block:: yaml
openstack_512:
provider: my-openstack-config
size: 512 MB Standard
image: Ubuntu 12.04 LTS (Precise Pangolin)
rackconnect: True
Managed Cloud Environments
--------------------------
Rackspace offers a managed service level of hosting. As part of the managed
service level you have the ability to choose from base of lamp installations on
cloud server images. The post build process for both the base and the lamp
installations used Chef to install things such as the cloud monitoring agent and
the cloud backup agent. It also takes care of installing the lamp stack if
selected. In order to prevent the post installation process from stomping over
the bootstrapping you can add the below to your profiles.
.. code-block:: yaml
openstack_512:
provider: my-rackspace-config
size: 512 MB Standard
image: Ubuntu 12.04 LTS (Precise Pangolin)
managedcloud: True
First and Next Generation Images
--------------------------------
Rackspace provides two sets of virtual machine images, *first*, and *next*
generation. As of ``0.8.9`` salt-cloud will default to using the *next*
generation images. To force the use of first generation images, on the profile
configuration please add:
.. code-block:: yaml
FreeBSD-9.0-512:
provider: my-rackspace-config
size: 512 MB Standard
image: FreeBSD 9.0
force_first_gen: True
Private Subnets
---------------
By default salt-cloud will not add Rackspace private networks to new servers. To enable
a private network to a server instantiated by salt cloud, add the following section
to the provider file (typically ``/etc/salt/cloud.providers.d/rackspace.conf``)
.. code-block:: yaml
networks:
- fixed:
# This is the private network
- private-network-id
# This is Rackspace's "PublicNet"
- 00000000-0000-0000-0000-000000000000
# This is Rackspace's "ServiceNet"
- 11111111-1111-1111-1111-111111111111
To get the Rackspace private network ID, go to Networking, Networks and hover over the private network name.
The order of the networks in the above code block does not map to the order of the
ethernet devices on newly created servers. Public IP will always be first ( eth0 )
followed by servicenet ( eth1 ) and then private networks.
Enabling the private network per above gives the option of using the private subnet for
all master-minion communication, including the bootstrap install of salt-minion. To
enable the minion to use the private subnet, update the master: line in the minion:
section of the providers file. To configure the master to only listen on the private
subnet IP, update the interface: line in the /etc/salt/master file to be the private
subnet IP of the salt master.

View File

@ -8,7 +8,8 @@ Installation from the Official SaltStack Repository
===================================================
**Latest stable build from the selected branch**:
|osxdownload|
|osxdownloadpy2|
|osxdownloadpy3|
The output of ``md5 <salt pkg>`` should match the contents of the
corresponding md5 file.

View File

@ -665,6 +665,37 @@ The Windows installer will now display command-line help when a help switch
Salt Cloud Features
-------------------
OpenStack Revamp
================
The OpenStack Driver has been rewritten mostly from scratch. Salt is now using
the `shade driver <https://docs.openstack.org/shade/latest/>`.
With this, the ``nova`` driver is being deprecated.
:mod:`openstack driver <salt.cloud.clouds.openstack>`
There have also been several new modules and states added for managing OpenStack
setups using shade as well.
:mod:`keystone <salt.modules.keystoneng>`
:mod:`keystone role grant <salt.states.keystone_role_grant>`
:mod:`keystone group <salt.states.keystone_group>`
:mod:`keystone role <salt.states.keystone_role>`
:mod:`keystone service <salt.states.keystone_service>`
:mod:`keystone user <salt.states.keystone_user>`
:mod:`keystone domain <salt.states.keystone_domain>`
:mod:`keystone project <salt.states.keystone_project>`
:mod:`keystone endpoint <salt.states.keystone_endpoint>`
:mod:`glance <salt.modules.glanceng>`
:mod:`glance_image <salt.states.glance_image>`
:mod:`neutron <salt.modules.neutronng>`
:mod:`neutron subnet <salt.states.neutron_subnet>`
:mod:`neutron secgroup <salt.states.neutron_secgroup>`
:mod:`neutron secgroup rule <salt.states.neutron_secgroup_rule>`
:mod:`neutron network <salt.states.neutron_network>`
Pre-Flight Commands
===================
@ -1557,6 +1588,14 @@ PyCrypto is used as it was in the previous releases. M2Crypto is used in the
same way as PyCrypto so there would be no compatibility issues, different nodes
could use different backends.
NaCL Module and Runner changes
------------------------------
In addition to argument changes in both the NaCL module and runner for future
deprecation in the Fluorine release, the default box_type has changed from
`secretbox` to `sealedbox`. SecretBox is data encrypted using private key
`sk` and Sealedbox is encrypted using public key `pk`
Deprecations
------------
@ -1617,6 +1656,15 @@ The ``win_service`` module had the following changes:
- The ``type`` option was removed from the ``create`` function. Please use
``service_type`` instead.
The ``nacl`` module had the following changes:
- The ``key_file`` option was replaced in the ``keygen``, ``enc`` and ``dec``
functions. Please use the ``sk_file`` option instead.
- The ``key`` option was replaced in the ``keygen``, ``enc`` and ``dec``
functions. Please use the ``sk`` option instead.
Runner Deprecations
===================
@ -1625,6 +1673,14 @@ The ``manage`` runner had the following changes:
- The ``root_user`` kwarg was removed from the ``bootstrap`` function. Please
use ``salt-ssh`` roster entries for the host instead.
The ``nacl`` runner had the following changes:
- The ``key_file`` option was replaced in the ``keygen``, ``enc`` and ``dec``
functions. Please use the ``sk_file`` option instead.
- The ``key`` option was replaced in the ``keygen``, ``enc`` and ``dec``
functions. Please use the ``sk`` option instead.
State Deprecations
==================

View File

@ -27,6 +27,27 @@ syndic respects :conf_minion:`enable_legacy_startup_events` as well.
Deprecations
------------
Module Deprecations
===================
The ``win_update`` module has been removed. It has been replaced by ``win_wua``
module.
The ``win_wua`` module had the following changes:
- Support for the ``download_update`` function has been removed. Please use the
``download`` function instead.
- Support for the ``download_updates`` function has been removed. Please use the
``download`` function instead.
- Support for the ``install_update`` function has been removed. Please use the
``install`` function instead.
- Support for the ``install_updates`` function has been removed. Please use the
``install`` function instead.
- Support for the ``list_update`` function has been removed. Please use the
``get`` function instead.
- Support for the ``list_updates`` function has been removed. Please use the
``list`` function instead.
Pillar Deprecations
===================
@ -49,6 +70,40 @@ The ``cache`` roster had the following changes:
State Deprecations
==================
The ``docker`` state has been removed. The following functions should be used
instead.
- The ``docker.running`` function was removed. Please update applicable SLS files
to use the ``docker_container.running`` function instead.
- The ``docker.stopped`` function was removed. Please update applicable SLS files
to use the ``docker_container.stopped`` function instead.
- The ``docker.absent`` function was removed. Please update applicable SLS files
to use the ``docker_container.absent`` function instead.
- The ``docker.absent`` function was removed. Please update applicable SLS files
to use the ``docker_container.absent`` function instead.
- The ``docker.network_present`` function was removed. Please update applicable
SLS files to use the ``docker_network.present`` function instead.
- The ``docker.network_absent`` function was removed. Please update applicable
SLS files to use the ``docker_network.absent`` function instead.
- The ``docker.image_present`` function was removed. Please update applicable SLS
files to use the ``docker_image.present`` function instead.
- The ``docker.image_absent`` function was removed. Please update applicable SLS
files to use the ``docker_image.absent`` function instead.
- The ``docker.volume_present`` function was removed. Please update applicable SLS
files to use the ``docker_volume.present`` function instead.
- The ``docker.volume_absent`` function was removed. Please update applicable SLS
files to use the ``docker_volume.absent`` function instead.
The ``docker_network`` state had the following changes:
- Support for the ``driver`` option has been removed from the ``absent`` function.
This option had no functionality in ``docker_network.absent``.
The ``git`` state had the following changes:
- Support for the ``ref`` option in the ``detached`` state has been removed.
Please use the ``rev`` option instead.
The ``k8s`` state has been removed. The following functions should be used
instead:
@ -58,3 +113,5 @@ instead:
files to use the ``kubernetes.node_label_present`` function instead.
- The ``k8s.label_folder_absent`` function was removed. Please update applicable
SLS files to use the ``kubernetes.node_label_folder_absent`` function instead.
The ``win_update`` state has been removed. Please use the ``win_wua`` state instead.

View File

@ -79,22 +79,12 @@ from the ``kevinopenstack`` profile above, you would use:
salt-call sdb.get sdb://kevinopenstack/password
Some drivers use slightly more complex URIs. For instance, the ``vault`` driver
requires the full path to where the key is stored, followed by a question mark,
followed by the key to be retrieved. If you were using a profile called
``myvault``, you would use a URI that looks like:
.. code-block:: bash
salt-call sdb.get 'sdb://myvault/secret/salt?saltstack'
Setting a value uses the same URI as would be used to retrieve it, followed
by the value as another argument. For the above ``myvault`` URI, you would set
a new value using a command like:
by the value as another argument.
.. code-block:: bash
salt-call sdb.set 'sdb://myvault/secret/salt?saltstack' 'super awesome'
salt-call sdb.set 'sdb://myvault/secret/salt/saltstack' 'super awesome'
Deleting values (if supported by the driver) is done pretty much the same way as
getting them. Provided that you have a profile called ``mykvstore`` that uses
@ -109,8 +99,8 @@ the runner system:
.. code-block:: bash
salt-run sdb.get 'sdb://myvault/secret/salt?saltstack'
salt-run sdb.set 'sdb://myvault/secret/salt?saltstack' 'super awesome'
salt-run sdb.get 'sdb://myvault/secret/salt/saltstack'
salt-run sdb.set 'sdb://myvault/secret/salt/saltstack' 'super awesome'
salt-run sdb.delete 'sdb://mykvstore/foobar'

View File

@ -361,7 +361,7 @@ def groups(username, **kwargs):
[salt.utils.stringutils.to_str(_config('accountattributename')), str('cn')]) # future lint: disable=blacklisted-function
for entry, result in search_results:
for user in result[_config('accountattributename'), _config('groupattribute')]:
for user in result[_config('accountattributename')]:
if username == salt.utils.stringutils.to_unicode(user).split(',')[0].split('=')[-1]:
group_list.append(entry.split(',')[0].split('=')[-1])

View File

@ -9,11 +9,11 @@
# Import Python libs
from __future__ import absolute_import, print_function, unicode_literals
import os
import logging
# Import Salt libs
import salt.client.netapi
import salt.utils.files
import salt.utils.parsers as parsers
from salt.utils.verify import check_user, verify_files, verify_log
@ -42,9 +42,8 @@ class SaltAPI(parsers.SaltAPIParser):
'udp://',
'file://')):
# Logfile is not using Syslog, verify
current_umask = os.umask(0o027)
with salt.utils.files.set_umask(0o027):
verify_files([logfile], self.config['user'])
os.umask(current_umask)
except OSError as err:
log.exception('Failed to prepare salt environment')
self.shutdown(err.errno)

View File

@ -1031,6 +1031,7 @@ class Single(object):
opts_pkg['pillar_roots'] = self.opts['pillar_roots']
opts_pkg['ext_pillar'] = self.opts['ext_pillar']
opts_pkg['extension_modules'] = self.opts['extension_modules']
opts_pkg['module_dirs'] = self.opts['module_dirs']
opts_pkg['_ssh_version'] = self.opts['_ssh_version']
opts_pkg['__master_opts__'] = self.context['master_opts']
if '_caller_cachedir' in self.opts:

View File

@ -106,9 +106,11 @@ def need_deployment():
'''
if os.path.exists(OPTIONS.saltdir):
shutil.rmtree(OPTIONS.saltdir)
old_umask = os.umask(0o077)
old_umask = os.umask(0o077) # pylint: disable=blacklisted-function
try:
os.makedirs(OPTIONS.saltdir)
os.umask(old_umask)
finally:
os.umask(old_umask) # pylint: disable=blacklisted-function
# Verify perms on saltdir
if not is_windows():
euid = os.geteuid()
@ -158,10 +160,10 @@ def unpack_thin(thin_path):
Unpack the Salt thin archive.
'''
tfile = tarfile.TarFile.gzopen(thin_path)
old_umask = os.umask(0o077)
old_umask = os.umask(0o077) # pylint: disable=blacklisted-function
tfile.extractall(path=OPTIONS.saltdir)
tfile.close()
os.umask(old_umask)
os.umask(old_umask) # pylint: disable=blacklisted-function
try:
os.unlink(thin_path)
except OSError:
@ -189,10 +191,10 @@ def unpack_ext(ext_path):
'minion',
'extmods')
tfile = tarfile.TarFile.gzopen(ext_path)
old_umask = os.umask(0o077)
old_umask = os.umask(0o077) # pylint: disable=blacklisted-function
tfile.extractall(path=modcache)
tfile.close()
os.umask(old_umask)
os.umask(old_umask) # pylint: disable=blacklisted-function
os.unlink(ext_path)
ver_path = os.path.join(modcache, 'ext_version')
ver_dst = os.path.join(OPTIONS.saltdir, 'ext_version')
@ -299,7 +301,7 @@ def main(argv): # pylint: disable=W0613
sys.stderr.write(OPTIONS.delimiter + '\n')
sys.stderr.flush()
if OPTIONS.cmd_umask is not None:
old_umask = os.umask(OPTIONS.cmd_umask)
old_umask = os.umask(OPTIONS.cmd_umask) # pylint: disable=blacklisted-function
if OPTIONS.tty:
# Returns bytes instead of string on python 3
stdout, _ = subprocess.Popen(salt_argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
@ -313,7 +315,7 @@ def main(argv): # pylint: disable=W0613
else:
subprocess.call(salt_argv)
if OPTIONS.cmd_umask is not None:
os.umask(old_umask)
os.umask(old_umask) # pylint: disable=blacklisted-function
if __name__ == '__main__':
sys.exit(main(sys.argv))

View File

@ -491,7 +491,7 @@ def request(mods=None,
'kwargs': kwargs
}
})
cumask = os.umask(0o77)
with salt.utils.files.set_umask(0o077):
try:
if salt.utils.platform.is_windows():
# Make sure cache file isn't read-only
@ -499,9 +499,10 @@ def request(mods=None,
with salt.utils.files.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)
log.error(
'Unable to write state request file %s. Check permission.',
notify_path
)
return ret
@ -557,7 +558,7 @@ def clear_request(name=None):
req.pop(name)
else:
return False
cumask = os.umask(0o77)
with salt.utils.files.set_umask(0o077):
try:
if salt.utils.platform.is_windows():
# Make sure cache file isn't read-only
@ -565,9 +566,10 @@ def clear_request(name=None):
with salt.utils.files.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)
log.error(
'Unable to write state request file %s. Check permission.',
notify_path
)
return True

View File

@ -264,6 +264,12 @@ def __virtual__():
if get_dependencies() is False:
return False
__utils__['versions.warn_until'](
'Neon',
'This driver has been deprecated and will be removed in the '
'{version} release of Salt. Please use the openstack driver instead.'
)
return __virtualname__

View File

@ -72,6 +72,7 @@ Or if you need to use a profile to setup some extra stuff, it can be passed as a
username: rackusername
api_key: myapikey
region_name: ORD
auth_type: rackspace_apikey
And this will pull in the profile for rackspace and setup all the correct
options for the auth_url and different api versions for services.
@ -101,6 +102,23 @@ The salt specific ones are:
This is the minimum setup required.
If metadata is set to make sure that the host has finished setting up the
`wait_for_metadata` can be set.
.. code-block:: yaml
centos:
provider: myopenstack
image: CentOS 7
size: ds1G
ssh_key_name: mykey
ssh_key_file: /root/.ssh/id_rsa
meta:
build_config: rack_user_only
wait_for_metadata:
rax_service_level_automation: Complete
rackconnect_automation_status: DEPLOYED
Anything else from the create_server_ docs can be passed through here.
- **image**: Image dict, name or ID to boot with. image is required
@ -678,12 +696,18 @@ def create(vm_):
data = request_instance(conn=conn, call='action', vm_=vm_)
log.debug('VM is now running')
def __query_node_ip(vm_):
def __query_node(vm_):
data = show_instance(vm_['name'], conn=conn, call='action')
if 'wait_for_metadata' in vm_:
for key, value in six.iteritems(vm_.get('wait_for_metadata', {})):
log.debug('Waiting for metadata: {0}={1}'.format(key, value))
if data['metadata'].get(key, None) != value:
log.debug('Metadata is not ready: {0}={1}'.format(key, data['metadata'].get(key, None)))
return False
return preferred_ip(vm_, data[ssh_interface(vm_)])
try:
ip_address = __utils__['cloud.wait_for_ip'](
__query_node_ip,
ip_address = __utils__['cloud.wait_for_fun'](
__query_node,
update_args=(vm_,)
)
except (SaltCloudExecutionTimeout, SaltCloudExecutionFailure) as exc:

View File

@ -2684,14 +2684,15 @@ def create(vm_):
non_hostname_chars = compile(r'[^\w-]')
if search(non_hostname_chars, vm_name):
hostName = split(non_hostname_chars, vm_name, maxsplit=1)[0]
domainName = split(non_hostname_chars, vm_name, maxsplit=1)[-1]
else:
hostName = vm_name
domainName = hostName.split('.', 1)[-1]
domainName = domain
if 'Windows' not in object_ref.config.guestFullName:
identity = vim.vm.customization.LinuxPrep()
identity.hostName = vim.vm.customization.FixedName(name=hostName)
identity.domain = domainName if hostName != domainName else domain
identity.domain = domainName
else:
identity = vim.vm.customization.Sysprep()
identity.guiUnattended = vim.vm.customization.GuiUnattended()

View File

@ -653,10 +653,11 @@ VALID_OPTS = {
's3fs_update_interval': int,
'svnfs_update_interval': int,
'git_pillar_base': six.string_types,
'git_pillar_branch': six.string_types,
'git_pillar_env': six.string_types,
'git_pillar_root': six.string_types,
# NOTE: git_pillar_base, git_pillar_branch, git_pillar_env, and
# git_pillar_root omitted here because their values could conceivably be
# loaded as non-string types, which is OK because git_pillar will normalize
# them to strings. But rather than include all the possible types they
# could be, we'll just skip type-checking.
'git_pillar_ssl_verify': bool,
'git_pillar_global_lock': bool,
'git_pillar_user': six.string_types,
@ -668,12 +669,11 @@ VALID_OPTS = {
'git_pillar_refspecs': list,
'git_pillar_includes': bool,
'git_pillar_verify_config': bool,
# NOTE: gitfs_base, gitfs_mountpoint, and gitfs_root omitted here because
# their values could conceivably be loaded as non-string types, which is OK
# because gitfs will normalize them to strings. But rather than include all
# the possible types they could be, we'll just skip type-checking.
'gitfs_remotes': list,
'gitfs_mountpoint': six.string_types,
'gitfs_root': six.string_types,
'gitfs_base': six.string_types,
'gitfs_user': six.string_types,
'gitfs_password': six.string_types,
'gitfs_insecure_auth': bool,
'gitfs_privkey': six.string_types,
'gitfs_pubkey': six.string_types,
@ -888,11 +888,14 @@ VALID_OPTS = {
'winrepo_dir': six.string_types,
'winrepo_dir_ng': six.string_types,
'winrepo_cachefile': six.string_types,
# NOTE: winrepo_branch omitted here because its value could conceivably be
# loaded as a non-string type, which is OK because winrepo will normalize
# them to strings. But rather than include all the possible types it could
# be, we'll just skip type-checking.
'winrepo_cache_expire_max': int,
'winrepo_cache_expire_min': int,
'winrepo_remotes': list,
'winrepo_remotes_ng': list,
'winrepo_branch': six.string_types,
'winrepo_ssl_verify': bool,
'winrepo_user': six.string_types,
'winrepo_password': six.string_types,
@ -1639,6 +1642,7 @@ DEFAULT_MASTER_OPTS = {
'eauth_acl_module': '',
'eauth_tokens': 'localfs',
'extension_modules': os.path.join(salt.syspaths.CACHE_DIR, 'master', 'extmods'),
'module_dirs': [],
'file_recv': False,
'file_recv_max_size': 100,
'file_buffer_size': 1048576,

View File

@ -84,8 +84,7 @@ def dropfile(cachedir, user=None):
'''
dfn = os.path.join(cachedir, '.dfn')
# set a mask (to avoid a race condition on file creation) and store original.
mask = os.umask(191)
try:
with salt.utils.files.set_umask(0o277):
log.info('Rotating AES key')
if os.path.isfile(dfn):
log.info('AES key rotation already requested')
@ -103,8 +102,6 @@ def dropfile(cachedir, user=None):
os.chown(dfn, uid, -1)
except (KeyError, ImportError, OSError, IOError):
pass
finally:
os.umask(mask) # restore original umask
def gen_keys(keydir, keyname, keysize, user=None, passphrase=None):
@ -138,17 +135,19 @@ def gen_keys(keydir, keyname, keysize, user=None, passphrase=None):
if not os.access(keydir, os.W_OK):
raise IOError('Write access denied to "{0}" for user "{1}".'.format(os.path.abspath(keydir), getpass.getuser()))
cumask = os.umask(0o277)
with salt.utils.files.set_umask(0o277):
if HAS_M2:
# if passphrase is empty or None use no cipher
if not passphrase:
gen.save_pem(priv, cipher=None)
else:
gen.save_pem(priv, cipher='des_ede3_cbc', callback=lambda x: six.b(passphrase))
gen.save_pem(
priv,
cipher='des_ede3_cbc',
callback=lambda x: salt.utils.stringutils.to_bytes(passphrase))
else:
with salt.utils.files.fopen(priv, 'wb+') as f:
f.write(gen.exportKey('PEM', passphrase))
os.umask(cumask)
if HAS_M2:
gen.save_pub_key(pub)
else:

View File

@ -202,10 +202,9 @@ def mk_key(opts, user):
os.unlink(keyfile)
key = salt.crypt.Crypticle.generate_key_string()
cumask = os.umask(191)
with salt.utils.files.set_umask(0o277):
with salt.utils.files.fopen(keyfile, 'w+') as fp_:
fp_.write(salt.utils.stringutils.to_str(key))
os.umask(cumask)
# 600 octal: Read and write access to the owner only.
# Write access is necessary since on subsequent runs, if the file
# exists, it needs to be written to again. Windows enforces this.

View File

@ -143,8 +143,7 @@ class Client(object):
saltenv,
path)
destdir = os.path.dirname(dest)
cumask = os.umask(63)
with salt.utils.files.set_umask(0o077):
# remove destdir if it is a regular file to avoid an OSError when
# running os.makedirs below
if os.path.isfile(destdir):
@ -158,7 +157,6 @@ class Client(object):
raise
yield dest
os.umask(cumask)
def get_cachedir(self, cachedir=None):
if cachedir is None:

View File

@ -1927,7 +1927,7 @@ def fqdns():
fqdns.add(socket.gethostbyaddr(ip)[0])
except (socket.error, socket.herror,
socket.gaierror, socket.timeout) as e:
log.error("Exception during resolving address: " + str(e))
log.info("Exception during resolving address: " + str(e))
grains['fqdns'] = sorted(list(fqdns))
return grains

View File

@ -1044,7 +1044,7 @@ class RaetKey(Key):
'''
Use libnacl to generate and safely save a private key
'''
import libnacl.dual # pylint: disable=3rd-party-module-not-gated
import libnacl.dual # pylint: disable=import-error,3rd-party-module-not-gated
d_key = libnacl.dual.DualSecret()
keydir, keyname, _, _ = self._get_key_attrs(keydir, keyname,
keysize, user)
@ -1440,14 +1440,13 @@ class RaetKey(Key):
keydata = {'priv': priv,
'sign': sign}
path = os.path.join(self.opts['pki_dir'], 'local.key')
c_umask = os.umask(191)
with salt.utils.files.set_umask(0o277):
if os.path.exists(path):
#mode = os.stat(path).st_mode
os.chmod(path, stat.S_IWUSR | stat.S_IRUSR)
with salt.utils.files.fopen(path, 'w+b') as fp_:
with salt.utils.files.fopen(path, 'w+') as fp_:
fp_.write(self.serial.dumps(keydata))
os.chmod(path, stat.S_IRUSR)
os.umask(c_umask)
def delete_local(self):
'''

View File

@ -22,6 +22,7 @@ from zipimport import zipimporter
import salt.config
import salt.syspaths
import salt.utils.context
import salt.utils.data
import salt.utils.dictupdate
import salt.utils.event
import salt.utils.files
@ -651,7 +652,7 @@ def _load_cached_grains(opts, cfn):
try:
serial = salt.payload.Serial(opts)
with salt.utils.files.fopen(cfn, 'rb') as fp_:
cached_grains = serial.load(fp_)
cached_grains = salt.utils.data.decode(serial.load(fp_))
if not cached_grains:
log.debug('Cached grains are empty, cache might be corrupted. Refreshing.')
return None
@ -791,7 +792,7 @@ def grains(opts, force_refresh=False, proxy=None):
grains_data.update(opts['grains'])
# Write cache if enabled
if opts.get('grains_cache', False):
cumask = os.umask(0o77)
with salt.utils.files.set_umask(0o077):
try:
if salt.utils.platform.is_windows():
# Late import
@ -813,13 +814,12 @@ def grains(opts, force_refresh=False, proxy=None):
# exception is.
if os.path.isfile(cfn):
os.unlink(cfn)
os.umask(cumask)
if grains_deep_merge:
salt.utils.dictupdate.update(grains_data, opts['grains'])
else:
grains_data.update(opts['grains'])
return grains_data
return salt.utils.data.decode(grains_data)
# TODO: get rid of? Does anyone use this? You should use raw() instead

View File

@ -578,9 +578,8 @@ class Master(SMaster):
# Check to see if we need to create a pillar cache dir
if self.opts['pillar_cache'] and not os.path.isdir(os.path.join(self.opts['cachedir'], 'pillar_cache')):
try:
prev_umask = os.umask(0o077)
with salt.utils.files.set_umask(0o077):
os.mkdir(os.path.join(self.opts['cachedir'], 'pillar_cache'))
os.umask(prev_umask)
except OSError:
pass

View File

@ -1077,8 +1077,7 @@ def unzip(zip_file,
if not salt.utils.platform.is_windows():
perm = zfile.getinfo(target).external_attr >> 16
if perm == 0:
umask_ = os.umask(0)
os.umask(umask_)
umask_ = salt.utils.files.get_umask()
if target.endswith('/'):
perm = 0o777 & ~umask_
else:

View File

@ -657,31 +657,25 @@ def find_images(ami_name=None, executable_by=None, owners=None, image_ids=None,
salt myminion boto_ec2.find_images tags='{"mytag": "value"}'
'''
retries = 30
conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile)
while retries:
try:
filter_parameters = {'filters': {}}
if image_ids:
filter_parameters['image_ids'] = [image_ids]
if executable_by:
filter_parameters['executable_by'] = [executable_by]
if owners:
filter_parameters['owners'] = [owners]
if ami_name:
filter_parameters['filters']['name'] = ami_name
if tags:
for tag_name, tag_value in six.iteritems(tags):
filter_parameters['filters']['tag:{0}'.format(tag_name)] = tag_value
images = conn.get_all_images(**filter_parameters)
log.debug('The filters criteria %s matched the following '
'images:%s', filter_parameters, images)
if images:
if return_objs:
return images
@ -689,7 +683,13 @@ def find_images(ami_name=None, executable_by=None, owners=None, image_ids=None,
else:
return False
except boto.exception.BotoServerError as exc:
log.error(exc)
if exc.error_code == 'Throttling':
log.debug("Throttled by AWS API, will retry in 5 seconds...")
time.sleep(5)
retries -= 1
continue
log.error('Failed to convert AMI name `%s` to an AMI ID: %s', ami_name, exc)
return False
return False

View File

@ -12,6 +12,7 @@ import functools
import glob
import logging
import os
import platform
import shutil
import subprocess
import sys
@ -3344,6 +3345,11 @@ def powershell(cmd,
python_shell = True
# Append PowerShell Object formatting
# ConvertTo-JSON is only available on Versions of Windows greater than
# `7.1.7600`. We have to use `platform.version` instead of `__grains__` here
# because this function is called by `salt/grains/core.py` before
# `__grains__` is populated
if salt.utils.versions.version_cmp(platform.version(), '7.1.7600') == 1:
cmd += ' | ConvertTo-JSON'
if depth is not None:
cmd += ' -Depth {0}'.format(depth)
@ -3363,7 +3369,7 @@ def powershell(cmd,
# caught in a try/catch block. For example, the `Get-WmiObject` command will
# often return a "Non Terminating Error". To fix this, make sure
# `-ErrorAction Stop` is set in the powershell command
cmd = 'try {' + cmd + '} catch { "{}" | ConvertTo-JSON}'
cmd = 'try {' + cmd + '} catch { "{}" }'
# Retrieve the response, while overriding shell with 'powershell'
response = run(cmd,

View File

@ -5414,9 +5414,7 @@ def manage_file(name,
# Create the file, user rw-only if mode will be set to prevent
# a small security race problem before the permissions are set
if mode:
current_umask = os.umask(0o77)
with salt.utils.files.set_umask(0o077 if mode else None):
# Create a new file when test is False and source is None
if contents is None:
if not __opts__['test']:
@ -5436,9 +5434,6 @@ def manage_file(name,
ret, 'File {0} not created'.format(name)
)
if mode:
os.umask(current_umask)
if contents is not None:
# Write the static contents to a temporary file
tmp = salt.utils.files.mkstemp(prefix=salt.utils.files.TEMPFILE_PREFIX,
@ -5471,8 +5466,7 @@ def manage_file(name,
# out what mode to use for the new file.
if mode is None and not salt.utils.platform.is_windows():
# Get current umask
mask = os.umask(0)
os.umask(mask)
mask = salt.utils.files.get_umask()
# Calculate the mode value that results from the umask
mode = oct((0o777 ^ mask) & 0o666)

View File

@ -103,6 +103,13 @@ def _auth(profile=None, api_version=2, **connection_args):
Only intended to be used within glance-enabled modules
'''
__utils__['versions.warn_until'](
'Neon',
(
'The glance module has been deprecated and will be removed in {version}. '
'Please update to using the glanceng module'
),
)
if profile:
prefix = profile + ":keystone."

View File

@ -513,7 +513,7 @@ if __name__ == '__main__':
sys.exit(1)
os.setsid()
os.umask(0)
os.umask(0o000) # pylint: disable=blacklisted-function
try:
pid = os.fork()

View File

@ -163,6 +163,13 @@ def auth(profile=None, **connection_args):
salt '*' keystone.auth
'''
__utils__['versions.warn_until'](
'Neon',
(
'The keystone module has been deprecated and will be removed in {version}. '
'Please update to using the keystoneng module',
),
)
kwargs = _get_kwargs(profile=profile, **connection_args)
disc = discover.Discover(auth_url=kwargs['auth_url'])

View File

@ -60,7 +60,9 @@ from salt.exceptions import CommandExecutionError
from salt.ext.six import iteritems
from salt.ext import six
import salt.utils.files
import salt.utils.platform
import salt.utils.templates
import salt.utils.versions
import salt.utils.yaml
from salt.exceptions import TimeoutError
from salt.ext.six.moves import range # pylint: disable=import-error
@ -223,11 +225,11 @@ def _cleanup_old(**kwargs):
cert = kubernetes.client.configuration.cert_file
key = kubernetes.client.configuration.key_file
if cert and os.path.exists(cert) and os.path.basename(cert).startswith('salt-kube-'):
salt.utils.safe_rm(cert)
salt.utils.files.safe_rm(cert)
if key and os.path.exists(key) and os.path.basename(key).startswith('salt-kube-'):
salt.utils.safe_rm(key)
salt.utils.files.safe_rm(key)
if ca and os.path.exists(ca) and os.path.basename(ca).startswith('salt-kube-'):
salt.utils.safe_rm(ca)
salt.utils.files.safe_rm(ca)
except Exception:
pass

View File

@ -164,7 +164,6 @@ import salt.utils.stringutils
import salt.utils.win_functions
import salt.utils.win_dacl
REQ_ERROR = None
try:
import libnacl.secret
@ -186,9 +185,9 @@ def _get_config(**kwargs):
config = {
'box_type': 'sealedbox',
'sk': None,
'sk_file': '/etc/salt/pki/master/nacl',
'sk_file': os.path.join(__opts__['pki_dir'], 'master/nacl'),
'pk': None,
'pk_file': '/etc/salt/pki/master/nacl.pub',
'pk_file': os.path.join(__opts__['pki_dir'], 'master/nacl.pub'),
}
config_key = '{0}.config'.format(__virtualname__)
try:
@ -233,7 +232,7 @@ def _get_pk(**kwargs):
return base64.b64decode(pubkey)
def keygen(sk_file=None, pk_file=None):
def keygen(sk_file=None, pk_file=None, **kwargs):
'''
Use libnacl to generate a keypair.
@ -253,6 +252,14 @@ def keygen(sk_file=None, pk_file=None):
salt-call nacl.keygen sk_file=/etc/salt/pki/master/nacl pk_file=/etc/salt/pki/master/nacl.pub
salt-call --local nacl.keygen
'''
if 'keyfile' in kwargs:
salt.utils.versions.warn_until(
'Fluorine',
'The \'keyfile\' argument has been deprecated and will be removed in Salt '
'{version}. Please use \'sk_file\' argument instead.'
)
sk_file = kwargs['keyfile']
if sk_file is None:
kp = libnacl.public.SecretKey()
return {'sk': base64.b64encode(kp.sk), 'pk': base64.b64encode(kp.pk)}
@ -313,6 +320,25 @@ def enc(data, **kwargs):
box_type: secretbox, sealedbox(default)
'''
if 'keyfile' in kwargs:
salt.utils.versions.warn_until(
'Fluorine',
'The \'keyfile\' argument has been deprecated and will be removed in Salt '
'{version}. Please use \'sk_file\' argument instead.'
)
kwargs['sk_file'] = kwargs['keyfile']
if 'key' in kwargs:
salt.utils.versions.warn_until(
'Fluorine',
'The \'key\' argument has been deprecated and will be removed in Salt '
'{version}. Please use \'sk\' argument instead.'
)
kwargs['sk'] = kwargs['key']
# ensure data is in bytes
data = salt.utils.stringutils.to_bytes(data)
box_type = _get_config(**kwargs)['box_type']
if box_type == 'sealedbox':
return sealedbox_encrypt(data, **kwargs)
@ -360,6 +386,31 @@ def dec(data, **kwargs):
box_type: secretbox, sealedbox(default)
'''
if 'keyfile' in kwargs:
salt.utils.versions.warn_until(
'Fluorine',
'The \'keyfile\' argument has been deprecated and will be removed in Salt '
'{version}. Please use \'sk_file\' argument instead.'
)
kwargs['sk_file'] = kwargs['keyfile']
# set boxtype to `secretbox` to maintain backward compatibility
kwargs['box_type'] = 'secretbox'
if 'key' in kwargs:
salt.utils.versions.warn_until(
'Fluorine',
'The \'key\' argument has been deprecated and will be removed in Salt '
'{version}. Please use \'sk\' argument instead.'
)
kwargs['sk'] = kwargs['key']
# set boxtype to `secretbox` to maintain backward compatibility
kwargs['box_type'] = 'secretbox'
# ensure data is in bytes
data = salt.utils.stringutils.to_bytes(data)
box_type = _get_config(**kwargs)['box_type']
if box_type == 'sealedbox':
return sealedbox_decrypt(data, **kwargs)
@ -414,6 +465,9 @@ def sealedbox_encrypt(data, **kwargs):
salt-call --local nacl.sealedbox_encrypt datatoenc pk_file=/etc/salt/pki/master/nacl.pub
salt-call --local nacl.sealedbox_encrypt datatoenc pk='vrwQF7cNiNAVQVAiS3bvcbJUnF0cN6fU9YTZD9mBfzQ='
'''
# ensure data is in bytes
data = salt.utils.stringutils.to_bytes(data)
pk = _get_pk(**kwargs)
b = libnacl.sealed.SealedBox(pk)
return base64.b64encode(b.encrypt(data))
@ -433,6 +487,10 @@ def sealedbox_decrypt(data, **kwargs):
'''
if data is None:
return None
# ensure data is in bytes
data = salt.utils.stringutils.to_bytes(data)
sk = _get_sk(**kwargs)
keypair = libnacl.public.SecretKey(sk)
b = libnacl.sealed.SealedBox(keypair)
@ -452,6 +510,9 @@ def secretbox_encrypt(data, **kwargs):
salt-call --local nacl.secretbox_encrypt datatoenc sk_file=/etc/salt/pki/master/nacl
salt-call --local nacl.secretbox_encrypt datatoenc sk='YmFkcGFzcwo='
'''
# ensure data is in bytes
data = salt.utils.stringutils.to_bytes(data)
sk = _get_sk(**kwargs)
b = libnacl.secret.SecretBox(sk)
return base64.b64encode(b.encrypt(data))
@ -472,6 +533,10 @@ def secretbox_decrypt(data, **kwargs):
'''
if data is None:
return None
# ensure data is in bytes
data = salt.utils.stringutils.to_bytes(data)
key = _get_sk(**kwargs)
b = libnacl.secret.SecretBox(key=key)
return b.decrypt(base64.b64decode(data))

View File

@ -1618,167 +1618,3 @@ def list_agents(profile=None):
'''
conn = _auth(profile)
return conn.list_agents()
# The following is a list of functions that need to be incorporated in the
# neutron module. This list should be updated as functions are added.
#
# update_ipsec_site_connection
# Updates an IPsecSiteConnection.
# update_ikepolicy Updates an IKEPolicy
# update_ipsecpolicy Updates an IPsecPolicy
# list_vips Fetches a list of all load balancer vips for a tenant.
# show_vip Fetches information of a certain load balancer vip.
# create_vip Creates a new load balancer vip.
# update_vip Updates a load balancer vip.
# delete_vip Deletes the specified load balancer vip.
# list_pools Fetches a list of all load balancer pools for a tenant.
# show_pool Fetches information of a certain load balancer pool.
# create_pool Creates a new load balancer pool.
# update_pool Updates a load balancer pool.
# delete_pool Deletes the specified load balancer pool.
# retrieve_pool_stats Retrieves stats for a certain load balancer pool.
# list_members Fetches a list of all load balancer members for
# a tenant.
# show_member Fetches information of a certain load balancer member.
# create_member Creates a new load balancer member.
# update_member Updates a load balancer member.
# delete_member Deletes the specified load balancer member.
# list_health_monitors Fetches a list of all load balancer health monitors for
# a tenant.
# show_health_monitor Fetches information of a certain load balancer
# health monitor.
# create_health_monitor
# Creates a new load balancer health monitor.
# update_health_monitor
# Updates a load balancer health monitor.
# delete_health_monitor
# Deletes the specified load balancer health monitor.
# associate_health_monitor
# Associate specified load balancer health monitor
# and pool.
# disassociate_health_monitor
# Disassociate specified load balancer health monitor
# and pool.
# create_qos_queue Creates a new queue.
# list_qos_queues Fetches a list of all queues for a tenant.
# show_qos_queue Fetches information of a certain queue.
# delete_qos_queue Deletes the specified queue.
# list_agents Fetches agents.
# show_agent Fetches information of a certain agent.
# update_agent Updates an agent.
# delete_agent Deletes the specified agent.
# list_network_gateways
# Retrieve network gateways.
# show_network_gateway Fetch a network gateway.
# create_network_gateway
# Create a new network gateway.
# update_network_gateway
# Update a network gateway.
# delete_network_gateway
# Delete the specified network gateway.
# connect_network_gateway
# Connect a network gateway to the specified network.
# disconnect_network_gateway
# Disconnect a network from the specified gateway.
# list_gateway_devices Retrieve gateway devices.
# show_gateway_device Fetch a gateway device.
# create_gateway_device
# Create a new gateway device.
# update_gateway_device
# Updates a new gateway device.
# delete_gateway_device
# Delete the specified gateway device.
# list_dhcp_agent_hosting_networks
# Fetches a list of dhcp agents hosting a network.
# list_networks_on_dhcp_agent
# Fetches a list of dhcp agents hosting a network.
# add_network_to_dhcp_agent
# Adds a network to dhcp agent.
# remove_network_from_dhcp_agent
# Remove a network from dhcp agent.
# list_l3_agent_hosting_routers
# Fetches a list of L3 agents hosting a router.
# list_routers_on_l3_agent
# Fetches a list of L3 agents hosting a router.
# add_router_to_l3_agent
# Adds a router to L3 agent.
# list_firewall_rules Fetches a list of all firewall rules for a tenant.
# show_firewall_rule Fetches information of a certain firewall rule.
# create_firewall_rule Creates a new firewall rule.
# update_firewall_rule Updates a firewall rule.
# delete_firewall_rule Deletes the specified firewall rule.
# list_firewall_policies
# Fetches a list of all firewall policies for a tenant.
# show_firewall_policy Fetches information of a certain firewall policy.
# create_firewall_policy
# Creates a new firewall policy.
# update_firewall_policy
# Updates a firewall policy.
# delete_firewall_policy
# Deletes the specified firewall policy.
# firewall_policy_insert_rule
# Inserts specified rule into firewall policy.
# firewall_policy_remove_rule
# Removes specified rule from firewall policy.
# list_firewalls Fetches a list of all firewals for a tenant.
# show_firewall Fetches information of a certain firewall.
# create_firewall Creates a new firewall.
# update_firewall Updates a firewall.
# delete_firewall Deletes the specified firewall.
# remove_router_from_l3_agent
# Remove a router from l3 agent.
# get_lbaas_agent_hosting_pool
# Fetches a loadbalancer agent hosting a pool.
# list_pools_on_lbaas_agent
# Fetches a list of pools hosted by
# the loadbalancer agent.
# list_service_providers
# Fetches service providers.
# list_credentials Fetch a list of all credentials for a tenant.
# show_credential Fetch a credential.
# create_credential Create a new credential.
# update_credential Update a credential.
# delete_credential Delete the specified credential.
# list_network_profile_bindings
# Fetch a list of all tenants associated for
# a network profile.
# list_network_profiles
# Fetch a list of all network profiles for a tenant.
# show_network_profile Fetch a network profile.
# create_network_profile
# Create a network profile.
# update_network_profile
# Update a network profile.
# delete_network_profile
# Delete the network profile.
# list_policy_profile_bindings
# Fetch a list of all tenants associated for
# a policy profile.
# list_policy_profiles Fetch a list of all network profiles for a tenant.
# show_policy_profile Fetch a network profile.
# update_policy_profile
# Update a policy profile.
# create_metering_label
# Creates a metering label.
# delete_metering_label
# Deletes the specified metering label.
# list_metering_labels Fetches a list of all metering labels for a tenant.
# show_metering_label Fetches information of a certain metering label.
# create_metering_label_rule
# Creates a metering label rule.
# delete_metering_label_rule
# Deletes the specified metering label rule.
# list_metering_label_rules
# Fetches a list of all metering label rules for a label.
# show_metering_label_rule
# Fetches information of a certain metering label rule.
# list_net_partitions Fetch a list of all network partitions for a tenant.
# show_net_partition etch a network partition.
# create_net_partition Create a network partition.
# delete_net_partition Delete the network partition.
# create_packet_filter Create a new packet filter.
# update_packet_filter Update a packet filter.
# list_packet_filters Fetch a list of all packet filters for a tenant.
# show_packet_filter Fetch information of a certain packet filter.
# delete_packet_filter Delete the specified packet filter.

View File

@ -42,8 +42,8 @@ def get(key,
Attempt to retrieve the named value from pillar, if the named value is not
available return the passed default. The default return is an empty string
except __opts__['pillar_raise_on_missing'] is set to True, in which case a
KeyError will be raised.
except ``__opts__['pillar_raise_on_missing']`` is set to True, in which
case a ``KeyError`` exception will be raised.
If the merge parameter is set to ``True``, the default will be recursively
merged into the returned pillar data.
@ -53,11 +53,18 @@ def get(key,
{'pkg': {'apache': 'httpd'}}
To retrieve the value associated with the apache key in the pkg dict this
key can be passed::
To retrieve the value associated with the ``apache`` key in the ``pkg``
dict this key can be passed as::
pkg:apache
key
The pillar key to get value from
default
If specified, return this value in case when named pillar value does
not exist.
merge : ``False``
If ``True``, the retrieved values will be merged into the passed
default. When the default and the retrieved value are both

View File

@ -338,6 +338,22 @@ def _process_requirements(requirements, cmd, cwd, saltenv, user):
return cleanup_requirements, None
def _format_env_vars(env_vars):
ret = {}
if env_vars:
if isinstance(env_vars, dict):
for key, val in six.iteritems(env_vars):
if not isinstance(key, six.string_types):
key = str(key) # future lint: disable=blacklisted-function
if not isinstance(val, six.string_types):
val = str(val) # future lint: disable=blacklisted-function
ret[key] = val
else:
raise CommandExecutionError(
'env_vars {0} is not a dictionary'.format(env_vars))
return ret
def install(pkgs=None, # pylint: disable=R0912,R0913,R0914
requirements=None,
bin_env=None,
@ -811,16 +827,7 @@ def install(pkgs=None, # pylint: disable=R0912,R0913,R0914
cmd_kwargs = dict(saltenv=saltenv, use_vt=use_vt, runas=user)
if env_vars:
if isinstance(env_vars, dict):
for key, val in six.iteritems(env_vars):
if not isinstance(key, six.string_types):
key = str(key) # future lint: disable=blacklisted-function
if not isinstance(val, six.string_types):
val = str(val) # future lint: disable=blacklisted-function
cmd_kwargs.setdefault('env', {})[key] = val
else:
raise CommandExecutionError(
'env_vars {0} is not a dictionary'.format(env_vars))
cmd_kwargs.setdefault('env', {}).update(_format_env_vars(env_vars))
try:
if cwd:
@ -974,7 +981,8 @@ def uninstall(pkgs=None,
def freeze(bin_env=None,
user=None,
cwd=None,
use_vt=False):
use_vt=False,
env_vars=None):
'''
Return a list of installed packages either globally or in the specified
virtualenv
@ -1027,6 +1035,8 @@ def freeze(bin_env=None,
cmd_kwargs = dict(runas=user, cwd=cwd, use_vt=use_vt, python_shell=False)
if bin_env and os.path.isdir(bin_env):
cmd_kwargs['env'] = {'VIRTUAL_ENV': bin_env}
if env_vars:
cmd_kwargs.setdefault('env', {}).update(_format_env_vars(env_vars))
result = __salt__['cmd.run_all'](cmd, **cmd_kwargs)
if result['retcode'] > 0:
@ -1038,7 +1048,8 @@ def freeze(bin_env=None,
def list_(prefix=None,
bin_env=None,
user=None,
cwd=None):
cwd=None,
env_vars=None):
'''
Filter list of installed apps from ``freeze`` and check to see if
``prefix`` exists in the list of packages installed.
@ -1067,7 +1078,7 @@ def list_(prefix=None,
if prefix is None or 'pip'.startswith(prefix):
packages['pip'] = version(bin_env)
for line in freeze(bin_env=bin_env, user=user, cwd=cwd):
for line in freeze(bin_env=bin_env, user=user, cwd=cwd, env_vars=env_vars):
if line.startswith('-f') or line.startswith('#'):
# ignore -f line as it contains --find-links directory
# ignore comment lines

View File

@ -83,6 +83,9 @@ def _to_unicode(vdata):
Converts from current users character encoding to unicode. Use this for
parameters being pass to reg functions
'''
# None does not convert to Unicode
if vdata is None:
return None
return salt.utils.stringutils.to_unicode(vdata, 'utf-8')
@ -526,13 +529,13 @@ def set_value(hive,
# https://www.python.org/dev/peps/pep-0237/
# String Types to Unicode
if vtype_value in [1, 2]:
if vtype_value in [win32con.REG_SZ, win32con.REG_EXPAND_SZ]:
local_vdata = _to_unicode(vdata)
# Don't touch binary...
elif vtype_value == 3:
elif vtype_value == win32con.REG_BINARY:
local_vdata = vdata
# Make sure REG_MULTI_SZ is a list of strings
elif vtype_value == 7:
elif vtype_value == win32con.REG_MULTI_SZ:
local_vdata = [_to_unicode(i) for i in vdata]
# Everything else is int
else:
@ -686,7 +689,6 @@ def delete_value(hive, key, vname=None, use_32bit_registry=False):
salt '*' reg.delete_value HKEY_CURRENT_USER 'SOFTWARE\\Salt' 'version'
'''
local_hive = _to_unicode(hive)
local_key = _to_unicode(key)
local_vname = _to_unicode(vname)

View File

@ -58,7 +58,7 @@ SCHEDULE_CONF = [
'after',
'return_config',
'return_kwargs',
'run_on_start'
'run_on_start',
'skip_during_range',
'run_after_skip_range',
]

View File

@ -778,7 +778,7 @@ def request(mods=None,
'kwargs': kwargs
}
})
cumask = os.umask(0o77)
with salt.utils.files.set_umask(0o077):
try:
if salt.utils.platform.is_windows():
# Make sure cache file isn't read-only
@ -790,7 +790,6 @@ def request(mods=None,
'Unable to write state request file %s. Check permission.',
notify_path
)
os.umask(cumask)
return ret
@ -844,7 +843,7 @@ def clear_request(name=None):
req.pop(name)
else:
return False
cumask = os.umask(0o77)
with salt.utils.files.set_umask(0o077):
try:
if salt.utils.platform.is_windows():
# Make sure cache file isn't read-only
@ -856,7 +855,6 @@ def clear_request(name=None):
'Unable to write state request file %s. Check permission.',
notify_path
)
os.umask(cumask)
return True
@ -1249,13 +1247,12 @@ def sls(mods, test=None, exclude=None, queue=False, **kwargs):
return ['Pillar failed to render with the following messages:'] + errors
orchestration_jid = kwargs.get('orchestration_jid')
umask = os.umask(0o77)
with salt.utils.files.set_umask(0o077):
if kwargs.get('cache'):
if os.path.isfile(cfn):
with salt.utils.files.fopen(cfn, 'rb') as fp_:
high_ = serial.load(fp_)
return st_.state.call_high(high_, orchestration_jid)
os.umask(umask)
mods = salt.utils.args.split_input(mods)
@ -1280,7 +1277,7 @@ def sls(mods, test=None, exclude=None, queue=False, **kwargs):
if __salt__['config.option']('state_data', '') == 'terse' or kwargs.get('terse'):
ret = _filter_running(ret)
cache_file = os.path.join(__opts__['cachedir'], 'sls.p')
cumask = os.umask(0o77)
with salt.utils.files.set_umask(0o077):
try:
if salt.utils.platform.is_windows():
# Make sure cache file isn't read-only
@ -1309,7 +1306,7 @@ def sls(mods, test=None, exclude=None, queue=False, **kwargs):
'Unable to write to highstate cache file %s. Do you have permissions?',
cfn
)
os.umask(cumask)
_snapper_post(opts, kwargs.get('__pub_jid', 'called localy'), snapper_pre)
return ret

View File

@ -98,7 +98,7 @@ def _find_utmp():
result[os.stat(utmp).st_mtime] = utmp
except Exception:
pass
if result > 0:
if len(result):
return result[sorted(result).pop()]
else:
return False

View File

@ -410,8 +410,7 @@ def _qemu_image_create(vm_name,
log.debug('Copying %s to %s', sfn, img_dest)
salt.utils.files.copyfile(sfn, img_dest)
mask = os.umask(0)
os.umask(mask)
mask = salt.utils.files.get_umask()
if disk_size and qcow2:
log.debug('Resize qcow2 image to %sM', disk_size)
@ -433,8 +432,7 @@ def _qemu_image_create(vm_name,
else:
# Create empty disk
try:
mask = os.umask(0)
os.umask(mask)
mask = salt.utils.files.get_umask()
if disk_size:
log.debug('Create empty image with size %sM', disk_size)

View File

@ -1,750 +0,0 @@
# -*- coding: utf-8 -*-
'''
Module for running windows updates.
This module is being deprecated and will be removed in Salt Fluorine. Please use
the ``win_wua`` module instead.
:depends: - win32com
- win32con
- win32api
- pywintypes
.. versionadded:: 2014.7.0
Set windows updates to run by category. Default behavior is to install
all updates that do not require user interaction to complete.
Optionally set ``categories`` to a category of your choice to only
install certain updates. Default is to set to install all available but driver updates.
The following example will install all Security and Critical Updates,
and download but not install standard updates.
.. code-block:: bash
salt '*' win_update.install_updates categories="['Critical Updates', 'Security Updates']"
You can also specify a number of features about the update to have a
fine grain approach to specific types of updates. These are the following
features/states of updates available for configuring:
.. code-block:: text
'UI' - User interaction required, skipped by default
'downloaded' - Already downloaded, included by default
'present' - Present on computer, included by default
'installed' - Already installed, skipped by default
'reboot' - Reboot required, included by default
'hidden' - Skip hidden updates, skipped by default
'software' - Software updates, included by default
'driver' - Driver updates, included by default
The following example installs all updates that don't require a reboot:
.. code-block:: bash
salt '*' win_update.install_updates skips="[{'reboot':True}]"
Once installed Salt will return a similar output:
.. code-block:: bash
2 : Windows Server 2012 Update (KB123456)
4 : Internet Explorer Security Update (KB098765)
2 : Malware Definition Update (KB321456)
...
The number at the beginning of the line is an OperationResultCode from the Windows Update Agent,
it's enumeration is described here: https://msdn.microsoft.com/en-us/library/windows/desktop/aa387095(v=vs.85).aspx.
The result code is then followed by the update name and its KB identifier.
'''
# pylint: disable=invalid-name,missing-docstring
# Import Python libs
from __future__ import absolute_import, unicode_literals, print_function
import logging
# Import 3rd-party libs
# pylint: disable=import-error
from salt.ext import six
from salt.ext.six.moves import range # pylint: disable=no-name-in-module,redefined-builtin
try:
import win32com.client
import pythoncom
HAS_DEPENDENCIES = True
except ImportError:
HAS_DEPENDENCIES = False
# pylint: enable=import-error
# Import Salt libs
import salt.utils.platform
import salt.utils.locales
import salt.utils.versions
log = logging.getLogger(__name__)
def __virtual__():
'''
Only works on Windows systems
'''
if salt.utils.platform.is_windows() and HAS_DEPENDENCIES:
salt.utils.versions.warn_until(
'Fluorine',
'The \'win_update\' module is being deprecated and will be removed '
'in Salt {version}. Please use the \'win_wua\' module instead.'
)
return True
return (False, "Module win_update: module has failed dependencies or is not on Windows client")
def _gather_update_categories(updateCollection):
'''
this is a convenience method to gather what categories of updates are available in any update
collection it is passed. Typically though, the download_collection.
Some known categories:
Updates
Windows 7
Critical Updates
Security Updates
Update Rollups
'''
categories = []
for i in range(updateCollection.Count):
update = updateCollection.Item(i)
for j in range(update.Categories.Count):
name = update.Categories.Item(j).Name
if name not in categories:
log.debug('found category: %s', name)
categories.append(name)
return categories
class PyWinUpdater(object):
def __init__(self, categories=None, skipUI=True, skipDownloaded=False,
skipInstalled=True, skipReboot=False, skipPresent=False,
skipSoftwareUpdates=False, skipDriverUpdates=False, skipHidden=True):
log.debug('CoInitializing the pycom system')
pythoncom.CoInitialize()
self.skipUI = skipUI
self.skipDownloaded = skipDownloaded
self.skipInstalled = skipInstalled
self.skipReboot = skipReboot
self.skipPresent = skipPresent
self.skipHidden = skipHidden
self.skipSoftwareUpdates = skipSoftwareUpdates
self.skipDriverUpdates = skipDriverUpdates
# the list of categories that the user wants to be searched for.
self.categories = categories
# the list of categories that are present in the updates found.
self.foundCategories = []
# careful not to get those two confused.
log.debug('dispatching update_session to keep the session object.')
self.update_session = win32com.client.Dispatch('Microsoft.Update.Session')
log.debug('update_session got. Now creating a win_searcher to seek out the updates')
self.win_searcher = self.update_session.CreateUpdateSearcher()
# list of updates that are applicable by current settings.
self.download_collection = win32com.client.Dispatch('Microsoft.Update.UpdateColl')
# list of updates to be installed.
self.install_collection = win32com.client.Dispatch('Microsoft.Update.UpdateColl')
# the object responsible for fetching the actual downloads.
self.win_downloader = self.update_session.CreateUpdateDownloader()
self.win_downloader.Updates = self.download_collection
# the object responsible for the installing of the updates.
self.win_installer = self.update_session.CreateUpdateInstaller()
self.win_installer.Updates = self.install_collection
# the results of the download process
self.download_results = None
# the results of the installation process
self.install_results = None
# search results from CreateUpdateSearcher()
self.search_results = None
def Search(self, searchString):
try:
log.debug('beginning search of the passed string: %s', searchString)
self.search_results = self.win_searcher.Search(searchString)
log.debug('search completed successfully.')
except Exception as exc:
log.info('search for updates failed. %s', exc)
return exc
log.debug('parsing results. %s updates were found.',
self.search_results.Updates.Count)
try:
# step through the list of the updates to ensure that the updates match the
# features desired.
for update in self.search_results.Updates:
# this skipps an update if UI updates are not desired.
if update.InstallationBehavior.CanRequestUserInput:
log.debug(U'Skipped update {0} - requests user input'.format(update.title))
continue
# if this update is already downloaded, it doesn't need to be in
# the download_collection. so skipping it unless the user mandates re-download.
if self.skipDownloaded and update.IsDownloaded:
log.debug(
'Skipped update %s - already downloaded',
update.title
)
continue
# check this update's categories against the ones desired.
for category in update.Categories:
# this is a zero guard. these tests have to be in this order
# or it will error out when the user tries to search for
# updates with out specifying categories.
if self.categories is None or category.Name in self.categories:
# adds it to the list to be downloaded.
self.download_collection.Add(update)
log.debug('added update %s', update.title)
# ever update has 2 categories. this prevents the
# from being added twice.
break
log.debug('download_collection made. gathering found categories.')
# gets the categories of the updates available in this collection of updates
self.foundCategories = _gather_update_categories(self.download_collection)
log.debug('found categories: %s',
six.text_type(self.foundCategories))
return True
except Exception as exc:
log.info('parsing updates failed. %s', exc)
return exc
def AutoSearch(self):
'''
this function generates a search string. simplifying the search function while
still providing as many features as possible.
'''
search_string = ''
searchParams = []
if self.skipInstalled:
searchParams.append('IsInstalled=0')
else:
searchParams.append('IsInstalled=1')
if self.skipHidden:
searchParams.append('IsHidden=0')
else:
searchParams.append('IsHidden=1')
if self.skipReboot:
searchParams.append('RebootRequired=0')
else:
searchParams.append('RebootRequired=1')
if self.skipPresent:
searchParams.append('IsPresent=0')
else:
searchParams.append('IsPresent=1')
for i in searchParams:
search_string += '{0} and '.format(i)
if not self.skipSoftwareUpdates and not self.skipDriverUpdates:
search_string += 'Type=\'Software\' or Type=\'Driver\''
elif not self.skipSoftwareUpdates:
search_string += 'Type=\'Software\''
elif not self.skipDriverUpdates:
search_string += 'Type=\'Driver\''
else:
return False
# if there is no type, the is nothing to search.
log.debug('generated search string: %s', search_string)
return self.Search(search_string)
def Download(self):
# chase the download_collection! do the actual download process.
try:
# if the download_collection is empty. no need to download things.
if self.download_collection.Count != 0:
self.download_results = self.win_downloader.Download()
else:
log.debug('Skipped downloading, all updates were already cached.')
return True
except Exception as exc:
log.debug('failed in the downloading %s.', exc)
return exc
def Install(self):
# beat those updates into place!
try:
# this does not draw from the download_collection. important thing to know.
# the blugger is created regardless of what the download_collection has done. but it
# will only download those updates which have been downloaded and are ready.
for update in self.search_results.Updates:
if update.IsDownloaded:
self.install_collection.Add(update)
log.debug('Updates prepared. beginning installation')
except Exception as exc:
log.info('Preparing install list failed: %s', exc)
return exc
# accept eula if not accepted
try:
for update in self.search_results.Updates:
if not update.EulaAccepted:
log.debug('Accepting EULA: %s', update.Title)
update.AcceptEula()
except Exception as exc:
log.info('Accepting Eula failed: %s', exc)
return exc
# if the blugger is empty. no point it starting the install process.
if self.install_collection.Count != 0:
log.debug('Install list created, about to install')
try:
# the call to install.
self.install_results = self.win_installer.Install()
log.info('Installation of updates complete')
return True
except Exception as exc:
log.info('Installation failed: %s', exc)
return exc
else:
log.info('no new updates.')
return True
def GetInstallationResults(self):
'''
this gets results of installation process.
'''
# if the blugger is empty, the results are nil.
log.debug('blugger has {0} updates in it'.format(self.install_collection.Count))
if self.install_collection.Count == 0:
return {}
updates = []
log.debug('repairing update list')
for i in range(self.install_collection.Count):
# this gets the result from install_results, but the title comes from the update
# collection install_collection.
updates.append('{0}: {1}'.format(
self.install_results.GetUpdateResult(i).ResultCode,
self.install_collection.Item(i).Title))
log.debug('Update results enumerated, now making a library to pass back')
results = {}
# translates the list of update results into a library that salt expects.
for i, update in enumerate(updates):
results['update {0}'.format(i)] = update
log.debug('Update information complied. returning')
return results
def GetInstallationResultsPretty(self):
'''
converts the installation results into a pretty print.
'''
updates = self.GetInstallationResults()
ret = 'The following are the updates and their return codes.\n'
for i in updates:
ret += '\t{0}\n'.format(updates[i])
return ret
def GetDownloadResults(self):
updates = []
for i in range(self.download_collection.Count):
updates.append('{0}: {1}'.format(
six.text_type(self.download_results.GetUpdateResult(i).ResultCode),
six.text_type(self.download_collection.Item(i).Title)))
results = {}
for i, update in enumerate(updates):
results['update {0}'.format(i)] = update
return results
def GetSearchResultsVerbose(self):
updates = []
log.debug('parsing results. %s updates were found.',
self.download_collection.count)
for update in self.download_collection:
if update.InstallationBehavior.CanRequestUserInput:
log.debug('Skipped update %s', update.title)
continue
# More fields can be added from https://msdn.microsoft.com/en-us/library/windows/desktop/aa386099(v=vs.85).aspx
update_com_fields = ['Categories', 'Deadline', 'Description',
'Identity', 'IsMandatory',
'KBArticleIDs', 'MaxDownloadSize', 'MinDownloadSize',
'MoreInfoUrls', 'MsrcSeverity', 'ReleaseNotes',
'SecurityBulletinIDs', 'SupportUrl', 'Title']
simple_enums = ['KBArticleIDs', 'MoreInfoUrls', 'SecurityBulletinIDs']
# update_dict = {k: getattr(update, k) for k in update_com_fields}
update_dict = {}
for f in update_com_fields:
v = getattr(update, f)
if not any([isinstance(v, bool), isinstance(v, six.string_types)]):
# Fields that require special evaluation.
if f in simple_enums:
v = [x for x in v]
elif f == 'Categories':
v = [{'Name': cat.Name, 'Description': cat.Description} for cat in v]
elif f == 'Deadline':
# Deadline will be useful and should be added.
# However, until it can be tested with a date object
# as returned by the COM, it is unclear how to
# handle this field.
continue
elif f == 'Identity':
v = {'RevisionNumber': v.RevisionNumber,
'UpdateID': v.UpdateID}
update_dict[f] = v
updates.append(update_dict)
log.debug('added update %s', update.title)
return updates
def GetSearchResults(self, fields=None):
"""Reduce full updates information to the most important information."""
updates_verbose = self.GetSearchResultsVerbose()
if fields is not None:
updates = [dict((k, v) for k, v in update.items() if k in fields)
for update in updates_verbose]
return updates
# Return list of titles.
return [update['Title'] for update in updates_verbose]
def SetCategories(self, categories):
self.categories = categories
def GetCategories(self):
return self.categories
def GetAvailableCategories(self):
return self.foundCategories
def SetSkips(self, skips):
if skips:
for i in skips:
value = i[next(six.iterkeys(i))]
skip = next(six.iterkeys(i))
self.SetSkip(skip, value)
log.debug('was asked to set %s to %s', skip, value)
def SetSkip(self, skip, state):
if skip == 'UI':
self.skipUI = state
elif skip == 'downloaded':
self.skipDownloaded = state
elif skip == 'installed':
self.skipInstalled = state
elif skip == 'reboot':
self.skipReboot = state
elif skip == 'present':
self.skipPresent = state
elif skip == 'hidden':
self.skipHidden = state
elif skip == 'software':
self.skipSoftwareUpdates = state
elif skip == 'driver':
self.skipDriverUpdates = state
log.debug('new search state: \n\tUI: %s\n\tDownload: %s\n\tInstalled: %s\n\treboot :%s\n\tPresent: %s\n\thidden: %s\n\tsoftware: %s\n\tdriver: %s',
self.skipUI, self.skipDownloaded, self.skipInstalled, self.skipReboot,
self.skipPresent, self.skipHidden, self.skipSoftwareUpdates, self.skipDriverUpdates)
def __str__(self):
results = 'There are {0} updates, by category there are:\n'.format(
self.download_collection.count)
for category in self.foundCategories:
count = 0
for update in self.download_collection:
for cat in update.Categories:
if category == cat.Name:
count += 1
results += '\t{0}: {1}\n'.format(category, count)
return results
def _search(quidditch, retries=5):
'''
a wrapper method for the pywinupdater class. I might move this into the class, but right now,
that is to much for one class I think.
'''
passed = False
clean = True
comment = ''
while not passed:
log.debug('Searching. tries left: %s', retries)
# let the updater make its own search string. MORE POWER this way.
passed = quidditch.AutoSearch()
log.debug('Done searching: %s', passed)
if isinstance(passed, Exception):
clean = False
comment += 'Failed in the seeking/parsing process:\n\t\t{0}\n'.format(passed)
retries -= 1
if retries:
comment += '{0} tries to go. retrying\n'.format(str(retries))
else:
comment += 'out of retries. this update round failed.\n'
return (comment, True, retries)
passed = False
if clean:
# bragging rights.
comment += 'Search was done without error.\n'
return (comment, True, retries)
def _download(quidditch, retries=5):
'''
another wrapper method.
'''
passed = False
clean = True
comment = ''
while not passed:
log.debug('Downloading. tries left: %s', retries)
passed = quidditch.Download()
log.debug('Done downloading: %s', passed)
if isinstance(passed, Exception):
clean = False
comment += 'Failed while trying to download updates:\n\t\t{0}\n'.format(str(passed))
retries -= 1
if retries:
comment += '{0} tries to go. retrying\n'.format(str(retries))
passed = False
else:
comment += 'out of retries. this update round failed.\n'
return (comment, False, retries)
if clean:
comment += 'Download was done without error.\n'
return (comment, True, retries)
def _install(quidditch, retries=5):
'''
and the last wrapper method. keeping things simple.
'''
passed = False
clean = True
comment = ''
while not passed:
log.debug('download_collection is this long: %s',
quidditch.install_collection.Count)
log.debug('Installing. tries left: %s', retries)
passed = quidditch.Install()
log.info('Done installing: %s', passed)
if isinstance(passed, Exception):
clean = False
comment += 'Failed while trying to install the updates.\n\t\t{0}\n'.format(str(passed))
retries -= 1
if retries:
comment += '{0} tries to go. retrying\n'.format(str(retries))
passed = False
else:
comment += 'out of retries. this update round failed.\n'
return (comment, False, retries)
if clean:
comment += 'Install was done without error.\n'
return (comment, True, retries)
# this is where the actual functions available to salt begin.
def list_updates(verbose=False, fields=None, skips=None, retries=5, categories=None):
'''
Returns a summary of available updates, grouped into their non-mutually
exclusive categories.
verbose
Return full set of results, including several fields from the COM.
fields
Return a list of specific fields for each update. The optional
values here are those at the root level of the verbose list. This
is superseded by the verbose option.
retries
Number of retries to make before giving up. This is total, not per
step.
categories
Specify the categories to list. Must be passed as a list.
.. code-block:: bash
salt '*' win_update.list_updates categories="['Updates']"
Categories include, but are not limited to, the following:
* Updates
* Windows 7
* Critical Updates
* Security Updates
* Update Rollups
CLI Examples:
.. code-block:: bash
# Normal Usage
salt '*' win_update.list_updates
# Specific Fields
salt '*' win_update.list_updates fields="['Title', 'Description']"
# List all critical updates list in verbose detail
salt '*' win_update.list_updates categories="['Critical Updates']" verbose=True
'''
log.debug('categories to search for are: %s', categories)
updates = PyWinUpdater()
if categories:
updates.SetCategories(categories)
updates.SetSkips(skips)
# this is where we be seeking the things! yar!
comment, passed, retries = _search(updates, retries)
if not passed:
return (comment, str(passed))
log.debug('verbose: %s', verbose)
if verbose:
return updates.GetSearchResultsVerbose()
return updates.GetSearchResults(fields=fields)
def download_updates(skips=None, retries=5, categories=None):
'''
Downloads all available updates, skipping those that require user
interaction.
Various aspects of the updates can be included or excluded. this feature is
still in development.
retries
Number of retries to make before giving up. This is total, not per
step.
categories
Specify the categories to update. Must be passed as a list.
.. code-block:: bash
salt '*' win_update.download_updates categories="['Updates']"
Categories include the following:
* Updates
* Windows 7
* Critical Updates
* Security Updates
* Update Rollups
CLI Examples:
.. code-block:: bash
# Normal Usage
salt '*' win_update.download_updates
# Download critical updates only
salt '*' win_update.download_updates categories="['Critical Updates']"
'''
log.debug('categories to search for are: %s', categories)
quidditch = PyWinUpdater(skipDownloaded=True)
quidditch.SetCategories(categories)
quidditch.SetSkips(skips)
# this is where we be seeking the things! yar!
comment, passed, retries = _search(quidditch, retries)
if not passed:
return (comment, str(passed))
# this is where we get all the things! i.e. download updates.
comment, passed, retries = _download(quidditch, retries)
if not passed:
return (comment, str(passed))
try:
comment = quidditch.GetDownloadResults()
except Exception as exc:
comment = 'could not get results, but updates were installed. {0}'.format(exc)
return 'Windows is up to date. \n{0}'.format(comment)
def install_updates(skips=None, retries=5, categories=None):
'''
Downloads and installs all available updates, skipping those that require
user interaction.
Add ``cached`` to only install those updates which have already been downloaded.
you can set the maximum number of retries to ``n`` in the search process by
adding: ``retries=n``
various aspects of the updates can be included or excluded. This function is
still under development.
retries
Number of retries to make before giving up. This is total, not per
step.
categories
Specify the categories to install. Must be passed as a list.
.. code-block:: bash
salt '*' win_update.install_updates categories="['Updates']"
Categories include the following:
* Updates
* Windows 7
* Critical Updates
* Security Updates
* Update Rollups
CLI Examples:
.. code-block:: bash
# Normal Usage
salt '*' win_update.install_updates
# Install all critical updates
salt '*' win_update.install_updates categories="['Critical Updates']"
'''
log.debug('categories to search for are: %s', categories)
quidditch = PyWinUpdater()
quidditch.SetCategories(categories)
quidditch.SetSkips(skips)
# this is where we be seeking the things! yar!
comment, passed, retries = _search(quidditch, retries)
if not passed:
return (comment, str(passed))
# this is where we get all the things! i.e. download updates.
comment, passed, retries = _download(quidditch, retries)
if not passed:
return (comment, str(passed))
# this is where we put things in their place!
comment, passed, retries = _install(quidditch, retries)
if not passed:
return (comment, str(passed))
try:
comment = quidditch.GetInstallationResultsPretty()
except Exception as exc:
comment = 'Could not get results, but updates were installed. {0}'.format(exc)
return 'Windows is up to date. \n{0}'.format(comment)

View File

@ -56,7 +56,6 @@ import logging
# Import Salt libs
import salt.utils.platform
import salt.utils.versions
import salt.utils.win_update
from salt.exceptions import CommandExecutionError
@ -228,87 +227,6 @@ def available(software=True,
return updates.summary() if summary else updates.list()
def list_update(name, download=False, install=False):
'''
.. deprecated:: 2017.7.0
Use :func:`get` instead
Returns details for all updates that match the search criteria
Args:
name (str):
The name of the update you're searching for. This can be the GUID, a
KB number, or any part of the name of the update. GUIDs and KBs are
preferred. Run ``list_updates`` to get the GUID for the update
you're looking for.
download (bool):
Download the update returned by this function. Run this function
first to see if the update exists, then set ``download=True`` to
download the update.
install (bool):
Install the update returned by this function. Run this function
first to see if the update exists, then set ``install=True`` to
install the update.
Returns:
dict: Returns a dict containing a list of updates that match the name if
download and install are both set to False. Should usually be a single
update, but can return multiple if a partial name is given.
If download or install is set to true it will return the results of the
operation.
.. code-block:: cfg
List of Updates:
{'<GUID>': {'Title': <title>,
'KB': <KB>,
'GUID': <the globally unique identifier for the update>
'Description': <description>,
'Downloaded': <has the update been downloaded>,
'Installed': <has the update been installed>,
'Mandatory': <is the update mandatory>,
'UserInput': <is user input required>,
'EULAAccepted': <has the EULA been accepted>,
'Severity': <update severity>,
'NeedsReboot': <is the update installed and awaiting reboot>,
'RebootBehavior': <will the update require a reboot>,
'Categories': [ '<category 1>',
'<category 2>',
...]
}
}
CLI Examples:
.. code-block:: bash
# Recommended Usage using GUID without braces
# Use this to find the status of a specific update
salt '*' win_wua.list_update 12345678-abcd-1234-abcd-1234567890ab
# Use the following if you don't know the GUID:
# Using a KB number (could possibly return multiple results)
# Not all updates have an associated KB
salt '*' win_wua.list_update KB3030298
# Using part or all of the name of the update
# Could possibly return multiple results
# Not all updates have an associated KB
salt '*' win_wua.list_update 'Microsoft Camera Codec Pack'
'''
salt.utils.versions.warn_until(
'Fluorine',
'This function is replaced by \'get\' as of Salt 2017.7.0. This '
'warning will be removed in Salt Fluorine.')
return get(name, download, install)
def get(name, download=False, install=False):
'''
.. versionadded:: 2017.7.0
@ -401,142 +319,6 @@ def get(name, download=False, install=False):
return ret if ret else updates.list()
def list_updates(software=True,
drivers=False,
summary=False,
skip_installed=True,
categories=None,
severities=None,
download=False,
install=False):
'''
.. deprecated:: 2017.7.0
Use :func:`list` instead
Returns a detailed list of available updates or a summary. If download or
install is True the same list will be downloaded and/or installed.
Args:
software (bool):
Include software updates in the results (default is True)
drivers (bool):
Include driver updates in the results (default is False)
summary (bool):
- True: Return a summary of updates available for each category.
- False (default): Return a detailed list of available updates.
skip_installed (bool):
Skip installed updates in the results (default is False)
download (bool):
(Overrides reporting functionality) Download the list of updates
returned by this function. Run this function first with
``download=False`` to see what will be downloaded, then set
``download=True`` to download the updates.
install (bool):
(Overrides reporting functionality) Install the list of updates
returned by this function. Run this function first with
``install=False`` to see what will be installed, then set
``install=True`` to install the updates.
categories (list):
Specify the categories to list. Must be passed as a list. All
categories returned by default.
Categories include the following:
* Critical Updates
* Definition Updates
* Drivers (make sure you set drivers=True)
* Feature Packs
* Security Updates
* Update Rollups
* Updates
* Update Rollups
* Windows 7
* Windows 8.1
* Windows 8.1 drivers
* Windows 8.1 and later drivers
* Windows Defender
severities (list):
Specify the severities to include. Must be passed as a list. All
severities returned by default.
Severities include the following:
* Critical
* Important
Returns:
dict: Returns a dict containing either a summary or a list of updates:
.. code-block:: cfg
List of Updates:
{'<GUID>': {'Title': <title>,
'KB': <KB>,
'GUID': <the globally unique identifier for the update>
'Description': <description>,
'Downloaded': <has the update been downloaded>,
'Installed': <has the update been installed>,
'Mandatory': <is the update mandatory>,
'UserInput': <is user input required>,
'EULAAccepted': <has the EULA been accepted>,
'Severity': <update severity>,
'NeedsReboot': <is the update installed and awaiting reboot>,
'RebootBehavior': <will the update require a reboot>,
'Categories': [ '<category 1>',
'<category 2>',
...]
}
}
Summary of Updates:
{'Total': <total number of updates returned>,
'Available': <updates that are not downloaded or installed>,
'Downloaded': <updates that are downloaded but not installed>,
'Installed': <updates installed (usually 0 unless installed=True)>,
'Categories': { <category 1>: <total for that category>,
<category 2>: <total for category 2>,
... }
}
CLI Examples:
.. code-block:: bash
# Normal Usage (list all software updates)
salt '*' win_wua.list_updates
# List all updates with categories of Critical Updates and Drivers
salt '*' win_wua.list_updates categories=['Critical Updates','Drivers']
# List all Critical Security Updates
salt '*' win_wua.list_updates categories=['Security Updates'] severities=['Critical']
# List all updates with a severity of Critical
salt '*' win_wua.list_updates severities=['Critical']
# A summary of all available updates
salt '*' win_wua.list_updates summary=True
# A summary of all Feature Packs and Windows 8.1 Updates
salt '*' win_wua.list_updates categories=['Feature Packs','Windows 8.1'] summary=True
'''
salt.utils.versions.warn_until(
'Fluorine',
'This function is replaced by \'list\' as of Salt 2017.7.0. This '
'warning will be removed in Salt Fluorine.')
return list(software, drivers, summary, skip_installed, categories,
severities, download, install)
def list(software=True,
drivers=False,
summary=False,
@ -688,74 +470,6 @@ def list(software=True,
return ret
def download_update(name):
'''
.. deprecated:: 2017.7.0
Use :func:`download` instead
Downloads a single update.
Args:
name (str):
The name of the update to download. This can be a GUID, a KB number,
or any part of the name. To ensure a single item is matched the GUID
is preferred.
.. note::
If more than one result is returned an error will be raised.
Returns:
dict: A dictionary containing the results of the download
CLI Examples:
.. code-block:: bash
salt '*' win_wua.download_update 12345678-abcd-1234-abcd-1234567890ab
salt '*' win_wua.download_update KB12312321
'''
salt.utils.versions.warn_until(
'Fluorine',
'This function is replaced by \'download\' as of Salt 2017.7.0. This '
'warning will be removed in Salt Fluorine.')
return download(name)
def download_updates(names):
'''
.. deprecated:: 2017.7.0
Use :func:`download` instead
Downloads updates that match the list of passed identifiers. It's easier to
use this function by using list_updates and setting install=True.
Args:
names (list):
A list of updates to download. This can be any combination of GUIDs,
KB numbers, or names. GUIDs or KBs are preferred.
Returns:
dict: A dictionary containing the details about the downloaded updates
CLI Examples:
.. code-block:: bash
# Normal Usage
salt '*' win_wua.download_updates guid=['12345678-abcd-1234-abcd-1234567890ab', 'KB2131233']
'''
salt.utils.versions.warn_until(
'Fluorine',
'This function is replaced by \'download\' as of Salt 2017.7.0. This '
'warning will be removed in Salt Fluorine.')
return download(names)
def download(names):
'''
.. versionadded:: 2017.7.0
@ -808,73 +522,6 @@ def download(names):
return wua.download(updates)
def install_update(name):
'''
.. deprecated:: 2017.7.0
Use :func:`install` instead
Installs a single update
Args:
name (str): The name of the update to install. This can be a GUID, a KB
number, or any part of the name. To ensure a single item is matched the
GUID is preferred.
.. note::
If no results or more than one result is returned an error will be
raised.
Returns:
dict: A dictionary containing the results of the install
CLI Examples:
.. code-block:: bash
salt '*' win_wua.install_update 12345678-abcd-1234-abcd-1234567890ab
salt '*' win_wua.install_update KB12312231
'''
salt.utils.versions.warn_until(
'Fluorine',
'This function is replaced by \'install\' as of Salt 2017.7.0. This '
'warning will be removed in Salt Fluorine.')
return install(name)
def install_updates(names):
'''
.. deprecated:: 2017.7.0
Use :func:`install` instead
Installs updates that match the list of identifiers. It may be easier to use
the list_updates function and set install=True.
Args:
names (list): A list of updates to install. This can be any combination
of GUIDs, KB numbers, or names. GUIDs or KBs are preferred.
Returns:
dict: A dictionary containing the details about the installed updates
CLI Examples:
.. code-block:: bash
# Normal Usage
salt '*' win_wua.install_updates guid=['12345678-abcd-1234-abcd-1234567890ab', 'KB12323211']
'''
salt.utils.versions.warn_until(
'Fluorine',
'This function is replaced by \'install\' as of Salt 2017.7.0. This '
'warning will be removed in Salt Fluorine.')
return install(names)
def install(names):
'''
.. versionadded:: 2017.7.0

View File

@ -756,7 +756,7 @@ def write_pem(text, path, overwrite=True, pem_type=None):
"-----BEGIN CERTIFICATE-----MIIGMzCCBBugA..." \\
path=/etc/pki/mycert.crt
'''
old_umask = os.umask(0o77)
with salt.utils.files.set_umask(0o077):
text = get_pem_entry(text, pem_type=pem_type)
_dhparams = ''
_private_key = ''
@ -777,7 +777,6 @@ def write_pem(text, path, overwrite=True, pem_type=None):
_fp.write(text)
if pem_type and pem_type == 'CERTIFICATE' and _dhparams:
_fp.write(salt.utils.stringutils.to_str(_dhparams))
os.umask(old_umask)
return 'PEM written to {0}'.format(path)

View File

@ -307,9 +307,9 @@ class EventListener(object):
'''
if request not in self.request_map:
return
for tag, future in self.request_map[request]:
for tag, matcher, future in self.request_map[request]:
# timeout the future
self._timeout_future(tag, future)
self._timeout_future(tag, matcher, future)
# remove the timeout
if future in self.timeout_map:
tornado.ioloop.IOLoop.current().remove_timeout(self.timeout_map[future])
@ -317,9 +317,22 @@ class EventListener(object):
del self.request_map[request]
@staticmethod
def prefix_matcher(mtag, tag):
if mtag is None or tag is None:
raise TypeError('mtag or tag can not be None')
return mtag.startswith(tag)
@staticmethod
def exact_matcher(mtag, tag):
if mtag is None or tag is None:
raise TypeError('mtag or tag can not be None')
return mtag == tag
def get_event(self,
request,
tag='',
matcher=prefix_matcher.__func__,
callback=None,
timeout=None
):
@ -339,40 +352,49 @@ class EventListener(object):
tornado.ioloop.IOLoop.current().add_callback(callback, future)
future.add_done_callback(handle_future)
# add this tag and future to the callbacks
self.tag_map[tag].append(future)
self.request_map[request].append((tag, future))
self.tag_map[(tag, matcher)].append(future)
self.request_map[request].append((tag, matcher, future))
if timeout:
timeout_future = tornado.ioloop.IOLoop.current().call_later(timeout, self._timeout_future, tag, future)
timeout_future = tornado.ioloop.IOLoop.current().call_later(timeout, self._timeout_future, tag, matcher, future)
self.timeout_map[future] = timeout_future
return future
def _timeout_future(self, tag, future):
def _timeout_future(self, tag, matcher, future):
'''
Timeout a specific future
'''
if tag not in self.tag_map:
if (tag, matcher) not in self.tag_map:
return
if not future.done():
future.set_exception(TimeoutException())
self.tag_map[tag].remove(future)
if len(self.tag_map[tag]) == 0:
del self.tag_map[tag]
self.tag_map[(tag, matcher)].remove(future)
if len(self.tag_map[(tag, matcher)]) == 0:
del self.tag_map[(tag, matcher)]
def _handle_event_socket_recv(self, raw):
'''
Callback for events on the event sub socket
'''
mtag, data = self.event.unpack(raw, self.event.serial)
# see if we have any futures that need this info:
for tag_prefix, futures in six.iteritems(self.tag_map):
if mtag.startswith(tag_prefix):
for (tag, matcher), futures in six.iteritems(self.tag_map):
try:
is_matched = matcher(mtag, tag)
except Exception as e:
log.error('Failed to run a matcher.', exc_info=True)
is_matched = False
if not is_matched:
continue
for future in futures:
if future.done():
continue
future.set_result({'data': data, 'tag': mtag})
self.tag_map[tag_prefix].remove(future)
self.tag_map[(tag, matcher)].remove(future)
if future in self.timeout_map:
tornado.ioloop.IOLoop.current().remove_timeout(self.timeout_map[future])
del self.timeout_map[future]
@ -924,55 +946,73 @@ class SaltAPIHandler(BaseSaltAPIHandler): # pylint: disable=W0223
if self.application.opts['order_masters']:
syndic_min_wait = tornado.gen.sleep(self.application.opts['syndic_wait'])
job_not_running = self.job_not_running(pub_data['jid'],
# To ensure job_not_running and all_return are terminated by each other, communicate using a future
is_finished = Future()
job_not_running_future = self.job_not_running(pub_data['jid'],
chunk['tgt'],
f_call['kwargs']['tgt_type'],
minions_remaining=minions_remaining
is_finished,
minions_remaining=list(minions_remaining),
)
# if we have a min_wait, do that
if syndic_min_wait is not None:
yield syndic_min_wait
# we are completed when either all minions return or the job isn't running anywhere
chunk_ret = yield self.all_returns(pub_data['jid'],
finish_futures=[job_not_running],
minions_remaining=minions_remaining,
)
raise tornado.gen.Return(chunk_ret)
all_return_future = self.all_returns(pub_data['jid'],
is_finished,
minions_remaining=list(minions_remaining),
)
yield job_not_running_future
raise tornado.gen.Return((yield all_return_future))
@tornado.gen.coroutine
def all_returns(self,
jid,
finish_futures=None,
is_finished,
minions_remaining=None,
):
'''
Return a future which will complete once all returns are completed
(according to minions_remaining), or one of the passed in "finish_futures" completes
(according to minions_remaining), or one of the passed in "is_finished" completes
'''
if finish_futures is None:
finish_futures = []
if minions_remaining is None:
minions_remaining = []
ret_tag = tagify([jid, 'ret'], 'job')
chunk_ret = {}
minion_events = {}
for minion in minions_remaining:
tag = tagify([jid, 'ret', minion], 'job')
minion_event = self.application.event_listener.get_event(self,
tag=tag,
matcher=EventListener.exact_matcher,
timeout=self.application.opts['timeout'])
minion_events[minion_event] = minion
while True:
ret_event = self.application.event_listener.get_event(self,
tag=ret_tag,
)
f = yield Any([ret_event] + finish_futures)
if f in finish_futures:
raise tornado.gen.Return(chunk_ret)
event = f.result()
chunk_ret[event['data']['id']] = event['data']['return']
# its possible to get a return that wasn't in the minion_remaining list
f = yield Any(minion_events.keys() + [is_finished])
try:
minions_remaining.remove(event['data']['id'])
if f is is_finished:
for event in minion_events:
if not event.done():
event.set_result(None)
raise tornado.gen.Return(chunk_ret)
f_result = f.result()
chunk_ret[f_result['data']['id']] = f_result['data']['return']
except TimeoutException:
pass
# clear finished event future
try:
minions_remaining.remove(minion_events[f])
del minion_events[f]
except ValueError:
pass
if len(minions_remaining) == 0:
if not is_finished.done():
is_finished.set_result(True)
raise tornado.gen.Return(chunk_ret)
@tornado.gen.coroutine
@ -980,6 +1020,7 @@ class SaltAPIHandler(BaseSaltAPIHandler): # pylint: disable=W0223
jid,
tgt,
tgt_type,
is_finished,
minions_remaining=None,
):
'''
@ -998,12 +1039,21 @@ class SaltAPIHandler(BaseSaltAPIHandler): # pylint: disable=W0223
minion_running = False
while True:
try:
event = yield self.application.event_listener.get_event(self,
event = self.application.event_listener.get_event(self,
tag=ping_tag,
timeout=self.application.opts['gather_job_timeout'],
)
f = yield Any([event, is_finished])
# When finished entire routine, cleanup other futures and return result
if f is is_finished:
if not event.done():
event.set_result(None)
raise tornado.gen.Return(True)
event = f.result()
except TimeoutException:
if not minion_running:
if not is_finished.done():
is_finished.set_result(True)
raise tornado.gen.Return(True)
else:
ping_pub_data = yield self.saltclients['local'](tgt,

View File

@ -8,9 +8,6 @@ This is often useful if you wish to store your pillars in source control or
share your pillar data with others that you trust. I don't advise making your pillars public
regardless if they are encrypted or not.
When generating keys and encrypting passwords use --local when using salt-call for extra
security. Also consider using just the salt runner nacl when encrypting pillar passwords.
:configuration: The following configuration defaults can be
define (pillar or config files) Avoid storing private keys in pillars! Ensure master does not have `pillar_opts=True`:
@ -30,7 +27,7 @@ security. Also consider using just the salt runner nacl when encrypting pillar p
.. code-block:: bash
salt-call nacl.enc sk_file=/etc/salt/pki/master/nacl pk_file=/etc/salt/pki/master/nacl.pub
salt-run nacl.enc sk_file=/etc/salt/pki/master/nacl pk_file=/etc/salt/pki/master/nacl.pub
The nacl lib uses 32byte keys, these keys are base64 encoded to make your life more simple.
@ -38,9 +35,9 @@ To generate your `sk_file` and `pk_file` use:
.. code-block:: bash
salt-call --local nacl.keygen sk_file=/etc/salt/pki/master/nacl
salt-run nacl.keygen sk_file=/etc/salt/pki/master/nacl
# or if you want to work without files.
salt-call --local nacl.keygen
salt-run nacl.keygen
local:
----------
pk:
@ -59,14 +56,14 @@ Sealedbox only has one key that is for both encryption and decryption.
.. code-block:: bash
salt-call --local nacl.enc asecretpass pk=/kfGX7PbWeu099702PBbKWLpG/9p06IQRswkdWHCDk0=
salt-run nacl.enc asecretpass pk=/kfGX7PbWeu099702PBbKWLpG/9p06IQRswkdWHCDk0=
tqXzeIJnTAM9Xf0mdLcpEdklMbfBGPj2oTKmlgrm3S1DTVVHNnh9h8mU1GKllGq/+cYsk6m5WhGdk58=
To decrypt the data:
.. code-block:: bash
salt-call --local nacl.dec data='tqXzeIJnTAM9Xf0mdLcpEdklMbfBGPj2oTKmlgrm3S1DTVVHNnh9h8mU1GKllGq/+cYsk6m5WhGdk58=' \
salt-run nacl.dec data='tqXzeIJnTAM9Xf0mdLcpEdklMbfBGPj2oTKmlgrm3S1DTVVHNnh9h8mU1GKllGq/+cYsk6m5WhGdk58=' \
sk='SVWut5SqNpuPeNzb1b9y6b2eXg2PLIog43GBzp48Sow='
When the keys are defined in the master config you can use them from the nacl runner
@ -94,7 +91,7 @@ The developer can then use a less-secure system to encrypt data.
.. code-block:: bash
salt-call --local nacl.enc apassword
salt-run nacl.enc apassword
Pillar files can include protected data that the salt master decrypts:
@ -111,42 +108,7 @@ Larger files like certificates can be encrypted with:
.. code-block:: bash
salt-call nacl.enc_file /tmp/cert.crt out=/tmp/cert.nacl
# or more advanced
cert=$(cat /tmp/cert.crt)
salt-call --out=newline_values_only nacl.enc_pub data="$cert" > /tmp/cert.nacl
In pillars rended with jinja be sure to include `|json` so line breaks are encoded:
.. code-block:: jinja
cert: "{{salt.nacl.dec('S2uogToXkgENz9...085KYt')|json}}"
In states rendered with jinja it is also good pratice to include `|json`:
.. code-block:: jinja
{{sls}} private key:
file.managed:
- name: /etc/ssl/private/cert.key
- mode: 700
- contents: "{{pillar['pillarexample']['cert_key']|json}}"
Optional small program to encrypt data without needing salt modules.
.. code-block:: python
#!/bin/python3
import sys, base64, libnacl.sealed
pk = base64.b64decode('YOURPUBKEY')
b = libnacl.sealed.SealedBox(pk)
data = sys.stdin.buffer.read()
print(base64.b64encode(b.encrypt(data)).decode())
.. code-block:: bash
echo 'apassword' | nacl_enc.py
salt-run nacl.enc_file /tmp/cert.crt out=/tmp/cert.nacl
'''
@ -158,6 +120,7 @@ import os
# Import Salt libs
import salt.utils.files
import salt.utils.platform
import salt.utils.stringutils
import salt.utils.win_functions
import salt.utils.win_dacl
import salt.syspaths
@ -186,9 +149,9 @@ def _get_config(**kwargs):
config = {
'box_type': 'sealedbox',
'sk': None,
'sk_file': '/etc/salt/pki/master/nacl',
'sk_file': os.path.join(__opts__['pki_dir'], 'nacl'),
'pk': None,
'pk_file': '/etc/salt/pki/master/nacl.pub',
'pk_file': os.path.join(__opts__['pki_dir'], 'nacl.pub'),
}
config_key = '{0}.config'.format(__virtualname__)
try:
@ -233,7 +196,7 @@ def _get_pk(**kwargs):
return base64.b64decode(pubkey)
def keygen(sk_file=None, pk_file=None):
def keygen(sk_file=None, pk_file=None, **kwargs):
'''
Use libnacl to generate a keypair.
@ -248,11 +211,20 @@ def keygen(sk_file=None, pk_file=None):
.. code-block:: bash
salt-call nacl.keygen
salt-call nacl.keygen sk_file=/etc/salt/pki/master/nacl
salt-call nacl.keygen sk_file=/etc/salt/pki/master/nacl pk_file=/etc/salt/pki/master/nacl.pub
salt-call --local nacl.keygen
salt-run nacl.keygen
salt-run nacl.keygen sk_file=/etc/salt/pki/master/nacl
salt-run nacl.keygen sk_file=/etc/salt/pki/master/nacl pk_file=/etc/salt/pki/master/nacl.pub
salt-run nacl.keygen
'''
if 'keyfile' in kwargs:
salt.utils.versions.warn_until(
'Fluorine',
'The \'keyfile\' argument has been deprecated and will be removed in Salt '
'{version}. Please use \'sk_file\' argument instead.'
)
sk_file = kwargs['keyfile']
if sk_file is None:
kp = libnacl.public.SecretKey()
return {'sk': base64.b64encode(kp.sk), 'pk': base64.b64encode(kp.pk)}
@ -313,6 +285,26 @@ def enc(data, **kwargs):
box_type: secretbox, sealedbox(default)
'''
if 'keyfile' in kwargs:
salt.utils.versions.warn_until(
'Fluorine',
'The \'keyfile\' argument has been deprecated and will be removed in Salt '
'{version}. Please use \'sk_file\' argument instead.'
)
kwargs['sk_file'] = kwargs['keyfile']
if 'key' in kwargs:
salt.utils.versions.warn_until(
'Fluorine',
'The \'key\' argument has been deprecated and will be removed in Salt '
'{version}. Please use \'sk\' argument instead.'
)
kwargs['sk'] = kwargs['key']
# ensure data is bytes
data = salt.utils.stringutils.to_bytes(data)
box_type = _get_config(**kwargs)['box_type']
if box_type == 'sealedbox':
return sealedbox_encrypt(data, **kwargs)
@ -334,7 +326,6 @@ def enc_file(name, out=None, **kwargs):
.. code-block:: bash
salt-run nacl.enc_file name=/tmp/id_rsa
salt-call nacl.enc_file name=salt://crt/mycert out=/tmp/cert
salt-run nacl.enc_file name=/tmp/id_rsa box_type=secretbox \
sk_file=/etc/salt/pki/master/nacl.pub
'''
@ -360,6 +351,31 @@ def dec(data, **kwargs):
box_type: secretbox, sealedbox(default)
'''
if 'keyfile' in kwargs:
salt.utils.versions.warn_until(
'Fluorine',
'The \'keyfile\' argument has been deprecated and will be removed in Salt '
'{version}. Please use \'sk_file\' argument instead.'
)
kwargs['sk_file'] = kwargs['keyfile']
# set boxtype to `secretbox` to maintain backward compatibility
kwargs['box_type'] = 'secretbox'
if 'key' in kwargs:
salt.utils.versions.warn_until(
'Fluorine',
'The \'key\' argument has been deprecated and will be removed in Salt '
'{version}. Please use \'sk\' argument instead.'
)
kwargs['sk'] = kwargs['key']
# set boxtype to `secretbox` to maintain backward compatibility
kwargs['box_type'] = 'secretbox'
# ensure data is bytes
data = salt.utils.stringutils.to_bytes(data)
box_type = _get_config(**kwargs)['box_type']
if box_type == 'sealedbox':
return sealedbox_decrypt(data, **kwargs)
@ -381,7 +397,6 @@ def dec_file(name, out=None, **kwargs):
.. code-block:: bash
salt-run nacl.dec_file name=/tmp/id_rsa.nacl
salt-call nacl.dec_file name=salt://crt/mycert.nacl out=/tmp/id_rsa
salt-run nacl.dec_file name=/tmp/id_rsa.nacl box_type=secretbox \
sk_file=/etc/salt/pki/master/nacl.pub
'''
@ -411,9 +426,10 @@ def sealedbox_encrypt(data, **kwargs):
.. code-block:: bash
salt-run nacl.sealedbox_encrypt datatoenc
salt-call --local nacl.sealedbox_encrypt datatoenc pk_file=/etc/salt/pki/master/nacl.pub
salt-call --local nacl.sealedbox_encrypt datatoenc pk='vrwQF7cNiNAVQVAiS3bvcbJUnF0cN6fU9YTZD9mBfzQ='
'''
# ensure data is bytes
data = salt.utils.stringutils.to_bytes(data)
pk = _get_pk(**kwargs)
b = libnacl.sealed.SealedBox(pk)
return base64.b64encode(b.encrypt(data))
@ -427,12 +443,16 @@ def sealedbox_decrypt(data, **kwargs):
.. code-block:: bash
salt-call nacl.sealedbox_decrypt pEXHQM6cuaF7A=
salt-call --local nacl.sealedbox_decrypt data='pEXHQM6cuaF7A=' sk_file=/etc/salt/pki/master/nacl
salt-call --local nacl.sealedbox_decrypt data='pEXHQM6cuaF7A=' sk='YmFkcGFzcwo='
salt-run nacl.sealedbox_decrypt pEXHQM6cuaF7A=
salt-run nacl.sealedbox_decrypt data='pEXHQM6cuaF7A=' sk_file=/etc/salt/pki/master/nacl
salt-run nacl.sealedbox_decrypt data='pEXHQM6cuaF7A=' sk='YmFkcGFzcwo='
'''
if data is None:
return None
# ensure data is bytes
data = salt.utils.stringutils.to_bytes(data)
sk = _get_sk(**kwargs)
keypair = libnacl.public.SecretKey(sk)
b = libnacl.sealed.SealedBox(keypair)
@ -449,9 +469,12 @@ def secretbox_encrypt(data, **kwargs):
.. code-block:: bash
salt-run nacl.secretbox_encrypt datatoenc
salt-call --local nacl.secretbox_encrypt datatoenc sk_file=/etc/salt/pki/master/nacl
salt-call --local nacl.secretbox_encrypt datatoenc sk='YmFkcGFzcwo='
salt-run nacl.secretbox_encrypt datatoenc sk_file=/etc/salt/pki/master/nacl
salt-run nacl.secretbox_encrypt datatoenc sk='YmFkcGFzcwo='
'''
# ensure data is bytes
data = salt.utils.stringutils.to_bytes(data)
sk = _get_sk(**kwargs)
b = libnacl.secret.SecretBox(sk)
return base64.b64encode(b.encrypt(data))
@ -466,12 +489,16 @@ def secretbox_decrypt(data, **kwargs):
.. code-block:: bash
salt-call nacl.secretbox_decrypt pEXHQM6cuaF7A=
salt-call --local nacl.secretbox_decrypt data='pEXHQM6cuaF7A=' sk_file=/etc/salt/pki/master/nacl
salt-call --local nacl.secretbox_decrypt data='pEXHQM6cuaF7A=' sk='YmFkcGFzcwo='
salt-run nacl.secretbox_decrypt pEXHQM6cuaF7A=
salt-run nacl.secretbox_decrypt data='pEXHQM6cuaF7A=' sk_file=/etc/salt/pki/master/nacl
salt-run nacl.secretbox_decrypt data='pEXHQM6cuaF7A=' sk='YmFkcGFzcwo='
'''
if data is None:
return None
# ensure data is bytes
data = salt.utils.stringutils.to_bytes(data)
key = _get_sk(**kwargs)
b = libnacl.secret.SecretBox(key=key)
return b.decrypt(base64.b64decode(data))

View File

@ -27,7 +27,7 @@ Once configured you can access data using a URL such as:
.. code-block:: yaml
password: sdb://myvault/secret/passwords?mypassword
password: sdb://myvault/secret/passwords/mypassword
In this URL, ``myvault`` refers to the configuration profile,
``secret/passwords`` is the path where the data resides, and ``mypassword`` is
@ -56,9 +56,17 @@ def set_(key, value, profile=None):
'''
Set a key/value pair in the vault service
'''
comps = key.split('?')
path = comps[0]
key = comps[1]
if '?' in key:
__utils__['versions.warn_until'](
'Neon',
(
'Using ? to seperate between the path and key for vault has been deprecated '
'and will be removed in {version}. Please just use a /.'
),
)
path, key = key.split('?')
else:
path, key = key.rsplit('/', 1)
try:
url = 'v1/{0}'.format(path)
@ -81,9 +89,17 @@ def get(key, profile=None):
'''
Get a value from the vault service
'''
comps = key.split('?')
path = comps[0]
key = comps[1]
if '?' in key:
__utils__['versions.warn_until'](
'Neon',
(
'Using ? to seperate between the path and key for vault has been deprecated '
'and will be removed in {version}. Please just use a /.'
),
)
path, key = key.split('?')
else:
path, key = key.rsplit('/', 1)
try:
url = 'v1/{0}'.format(path)

View File

@ -720,8 +720,12 @@ class State(object):
except AttributeError:
pillar_enc = six.text_type(pillar_enc).lower()
self._pillar_enc = pillar_enc
if initial_pillar:
if initial_pillar and not self._pillar_override:
self.opts['pillar'] = initial_pillar
else:
# Compile pillar data
self.opts['pillar'] = self._gather_pillar()
# Reapply overrides on top of compiled pillar
if self._pillar_override:
self.opts['pillar'] = salt.utils.dictupdate.merge(
self.opts['pillar'],
@ -729,8 +733,6 @@ class State(object):
self.opts.get('pillar_source_merging_strategy', 'smart'),
self.opts.get('renderer', 'yaml'),
self.opts.get('pillar_merge_lists', False))
else:
self.opts['pillar'] = self._gather_pillar()
self.state_con = context or {}
self.load_modules()
self.active = set()
@ -3919,7 +3921,7 @@ class BaseHighState(object):
return err
if not high:
return ret
cumask = os.umask(0o77)
with salt.utils.files.set_umask(0o077):
try:
if salt.utils.platform.is_windows():
# Make sure cache file isn't read-only
@ -3936,7 +3938,6 @@ class BaseHighState(object):
except (IOError, OSError):
log.error('Unable to write to "state.highstate" cache file %s', cfn)
os.umask(cumask)
return self.state.call_high(high, orchestration_jid)
def compile_highstate(self):

View File

@ -487,9 +487,10 @@ def present(
iargs = {'ami_name': image_name, 'region': region, 'key': key,
'keyid': keyid, 'profile': profile}
image_ids = __salt__['boto_ec2.find_images'](**iargs)
if len(image_ids):
if image_ids: # find_images() returns False on failure
launch_config[index]['image_id'] = image_ids[0]
else:
log.warning("Couldn't find AMI named `%s`, passing literally.", image_name)
launch_config[index]['image_id'] = image_name
del launch_config[index]['image_name']
break

View File

@ -199,8 +199,7 @@ executed when the state it is watching changes. Example:
``cmd.wait`` itself does not do anything; all functionality is inside its ``mod_watch``
function, which is called by ``watch`` on changes.
``cmd.wait`` will be deprecated in future due to the confusion it causes. The
preferred format is using the :ref:`onchanges Requisite <requisites-onchanges>`, which
The preferred format is using the :ref:`onchanges Requisite <requisites-onchanges>`, which
works on ``cmd.run`` as well as on any other state. The example would then look as follows:
.. code-block:: yaml

View File

@ -1,278 +0,0 @@
# -*- coding: utf-8 -*-
'''
States to manage Docker containers, images, volumes, and networks
.. versionchanged:: 2017.7.0
The legacy Docker state and execution module have been removed, and the
new modules (formerly called ``dockerng`` have taken their places).
.. important::
As of the 2017.7.0 release, the states in this module have been separated
into the following four state modules:
- :mod:`docker_container <salt.states.docker_container>` - States to manage
Docker containers
- :mod:`docker_image <salt.states.docker_image>` - States to manage Docker
images
- :mod:`docker_volume <salt.states.docker_volume>` - States to manage
Docker volumes
- :mod:`docker_network <salt.states.docker_network>` - States to manage
Docker networks
The reason for this change was to make states and requisites more clear.
For example, imagine this SLS:
.. code-block:: yaml
myuser/appimage:
docker.image_present:
- sls: docker.images.appimage
myapp:
docker.running:
- image: myuser/appimage
- require:
- docker: myuser/appimage
The new syntax would be:
.. code-block:: yaml
myuser/appimage:
docker_image.present:
- sls: docker.images.appimage
myapp:
docker_container.running:
- image: myuser/appimage
- require:
- docker_image: myuser/appimage
This is similar to how Salt handles MySQL, MongoDB, Zabbix, and other cases
where the same execution module is used to manage several different kinds
of objects (users, databases, roles, etc.).
The old syntax will continue to work until the **Fluorine** release of
Salt.
'''
from __future__ import absolute_import, print_function, unicode_literals
import copy
import logging
# Import salt libs
import salt.utils.args
import salt.utils.versions
# Enable proper logging
log = logging.getLogger(__name__) # pylint: disable=invalid-name
# Define the module's virtual name
__virtualname__ = 'docker'
__virtual_aliases__ = ('dockerng', 'moby')
def __virtual__():
'''
Only load if the docker execution module is available
'''
if 'docker.version' in __salt__:
return __virtualname__
return (False, __salt__.missing_fun_string('docker.version'))
def running(name, **kwargs):
'''
.. deprecated:: 2017.7.0
This state has been moved to :py:func:`docker_container.running
<salt.states.docker_container.running>`.
'''
ret = __states__['docker_container.running'](
name,
**salt.utils.args.clean_kwargs(**kwargs)
)
msg = (
'The docker.running state has been renamed to '
'docker_container.running. To get rid of this warning, update your '
'SLS to use the new name.'
)
salt.utils.versions.warn_until('Fluorine', msg)
ret.setdefault('warnings', []).append(msg)
return ret
def stopped(**kwargs):
'''
.. deprecated:: 2017.7.0
This state has been moved to :py:func:`docker_container.stopped
<salt.states.docker_container.stopped>`.
'''
ret = __states__['docker_container.stopped'](
**salt.utils.args.clean_kwargs(**kwargs)
)
msg = (
'The docker.stopped state has been renamed to '
'docker_container.stopped. To get rid of this warning, update your '
'SLS to use the new name.'
)
salt.utils.versions.warn_until('Fluorine', msg)
ret.setdefault('warnings', []).append(msg)
return ret
def absent(name, **kwargs):
'''
.. deprecated:: 2017.7.0
This state has been moved to :py:func:`docker_container.absent
<salt.states.docker_container.absent>`.
'''
ret = __states__['docker_container.absent'](
name,
**salt.utils.args.clean_kwargs(**kwargs)
)
msg = (
'The docker.absent state has been renamed to '
'docker_container.absent. To get rid of this warning, update your '
'SLS to use the new name.'
)
salt.utils.versions.warn_until('Fluorine', msg)
ret.setdefault('warnings', []).append(msg)
return ret
def network_present(name, **kwargs):
'''
.. deprecated:: 2017.7.0
This state has been moved to :py:func:`docker_network.present
<salt.states.docker_network.present>`.
'''
ret = __states__['docker_network.present'](
name,
**salt.utils.args.clean_kwargs(**kwargs)
)
msg = (
'The docker.network_present state has been renamed to '
'docker_network.present. To get rid of this warning, update your SLS '
'to use the new name.'
)
salt.utils.versions.warn_until('Fluorine', msg)
ret.setdefault('warnings', []).append(msg)
return ret
def network_absent(name, **kwargs):
'''
.. deprecated:: 2017.7.0
This state has been moved to :py:func:`docker_network.absent
<salt.states.docker_network.absent>`.
'''
ret = __states__['docker_network.absent'](
name,
**salt.utils.args.clean_kwargs(**kwargs)
)
msg = (
'The docker.network_absent state has been renamed to '
'docker_network.absent. To get rid of this warning, update your SLS '
'to use the new name.'
)
salt.utils.versions.warn_until('Fluorine', msg)
ret.setdefault('warnings', []).append(msg)
return ret
def image_present(name, **kwargs):
'''
.. deprecated:: 2017.7.0
This state has been moved to :py:func:`docker_image.present
<salt.states.docker_image.present>`.
'''
ret = __states__['docker_image.present'](
name,
**salt.utils.args.clean_kwargs(**kwargs)
)
msg = (
'The docker.image_present state has been renamed to '
'docker_image.present. To get rid of this warning, update your SLS '
'to use the new name.'
)
salt.utils.versions.warn_until('Fluorine', msg)
ret.setdefault('warnings', []).append(msg)
return ret
def image_absent(**kwargs):
'''
.. deprecated:: 2017.7.0
This state has been moved to :py:func:`docker_image.absent
<salt.states.docker_image.absent>`.
'''
ret = __states__['docker_image.absent'](
**salt.utils.args.clean_kwargs(**kwargs)
)
msg = (
'The docker.image_absent state has been renamed to '
'docker_image.absent. To get rid of this warning, update your SLS to '
'use the new name.'
)
salt.utils.versions.warn_until('Fluorine', msg)
ret.setdefault('warnings', []).append(msg)
return ret
def volume_present(name, driver=None, driver_opts=None, force=False):
'''
.. deprecated:: 2017.7.0
This state has been moved to :py:func:`docker_volume.present
<salt.states.docker_volume.present>`.
'''
ret = __states__['docker_volume.present'](name,
driver=driver,
driver_opts=driver_opts,
force=force)
msg = (
'The docker.volume_present state has been renamed to '
'docker_volume.present. To get rid of this warning, update your SLS '
'to use the new name.'
)
salt.utils.versions.warn_until('Fluorine', msg)
ret.setdefault('warnings', []).append(msg)
return ret
def volume_absent(name, driver=None):
'''
.. deprecated:: 2017.7.0
This state has been moved to :py:func:`docker_volume.absent
<salt.states.docker_volume.absent>`.
'''
ret = __states__['docker_volume.absent'](name, driver=driver)
msg = (
'The docker.volume_absent state has been renamed to '
'docker_volume.absent. To get rid of this warning, update your SLS '
'to use the new name.'
)
salt.utils.versions.warn_until('Fluorine', msg)
ret.setdefault('warnings', []).append(msg)
return ret
# Handle requisites
def mod_watch(name, sfun=None, **kwargs):
if sfun == 'running':
watch_kwargs = copy.deepcopy(kwargs)
if watch_kwargs.get('watch_action', 'force') == 'force':
watch_kwargs['force'] = True
else:
watch_kwargs['send_signal'] = True
watch_kwargs['force'] = False
return running(name, **watch_kwargs)
if sfun == 'image_present':
# Force image to be updated
kwargs['force'] = True
return image_present(name, **kwargs)
return {'name': name,
'changes': {},
'result': False,
'comment': ('watch requisite is not'
' implemented for {0}'.format(sfun))}

View File

@ -890,7 +890,7 @@ def present(name,
return ret
def absent(name, driver=None):
def absent(name):
'''
Ensure that a network is absent.
@ -909,12 +909,6 @@ def absent(name, driver=None):
'result': False,
'comment': ''}
if driver is not None:
ret.setdefault('warnings', []).append(
'The \'driver\' argument has no function and will be removed in '
'the Fluorine release.'
)
try:
network = __salt__['docker.inspect_network'](name)
except CommandExecutionError as exc:

View File

@ -2177,10 +2177,6 @@ def detached(name,
If a branch or tag is specified it will be resolved to a commit ID
and checked out.
ref
.. deprecated:: 2017.7.0
Use ``rev`` instead.
target
Name of the target directory where repository is about to be cloned.
@ -2271,7 +2267,6 @@ def detached(name,
ret = {'name': name, 'result': True, 'comment': '', 'changes': {}}
ref = kwargs.pop('ref', None)
kwargs = salt.utils.args.clean_kwargs(**kwargs)
if kwargs:
return _fail(
@ -2279,15 +2274,6 @@ def detached(name,
salt.utils.args.invalid_kwargs(kwargs, raise_exc=False)
)
if ref is not None:
rev = ref
deprecation_msg = (
'The \'ref\' argument has been renamed to \'rev\' for '
'consistency. Please update your SLS to reflect this.'
)
ret.setdefault('warnings', []).append(deprecation_msg)
salt.utils.versions.warn_until('Fluorine', deprecation_msg)
if not rev:
return _fail(
ret,

View File

@ -63,7 +63,7 @@ def send_message(name,
- api_url: https://hipchat.myteam.com
- api_key: peWcBiMOS9HrZG15peWcBiMOS9HrZG15
- api_version: v1
- color: green
- message_color: green
- notify: True
The following parameters are required:
@ -96,7 +96,7 @@ def send_message(name,
The api version for Hipchat to use,
if not specified in the configuration options of master or minion.
color
message_color
The color the Hipchat message should be displayed in. One of the following, default: yellow
"yellow", "red", "green", "purple", "gray", or "random".

View File

@ -341,10 +341,12 @@ def mounted(name,
mount_invisible_keys = [
'actimeo',
'comment',
'credentials',
'direct-io-mode',
'password',
'retry',
'port',
'retry',
'secretfile',
]
if extra_mount_invisible_keys:

View File

@ -180,8 +180,8 @@ def _check_pkg_version_format(pkg):
def _check_if_installed(prefix, state_pkg_name, version_spec,
ignore_installed, force_reinstall,
upgrade, user, cwd, bin_env, index_url):
upgrade, user, cwd, bin_env, env_vars,
index_url):
# result: None means the command failed to run
# result: True means the package is installed
# result: False means the package is not installed
@ -190,7 +190,8 @@ def _check_if_installed(prefix, state_pkg_name, version_spec,
# Check if the requested package is already installed.
try:
pip_list = __salt__['pip.list'](prefix, bin_env=bin_env,
user=user, cwd=cwd)
user=user, cwd=cwd,
env_vars=env_vars)
prefix_realname = _find_key(prefix, pip_list)
except (CommandNotFoundError, CommandExecutionError) as err:
ret['result'] = None
@ -682,7 +683,8 @@ def installed(name,
version_spec = version_spec
out = _check_if_installed(prefix, state_pkg_name, version_spec,
ignore_installed, force_reinstall,
upgrade, user, cwd, bin_env, index_url)
upgrade, user, cwd, bin_env, env_vars,
index_url)
# If _check_if_installed result is None, something went wrong with
# the command running. This way we keep stateful output.
if out['result'] is None:
@ -823,7 +825,8 @@ def installed(name,
# Case for packages that are not an URL
if prefix:
pipsearch = __salt__['pip.list'](prefix, bin_env,
user=user, cwd=cwd)
user=user, cwd=cwd,
env_vars=env_vars)
# If we didnt find the package in the system after
# installing it report it

View File

@ -33,6 +33,7 @@ import salt.utils.dateutils
import salt.utils.platform
import salt.utils.user
from salt.utils.locales import sdecode, sdecode_if_string
from salt.exceptions import CommandExecutionError
# Import 3rd-party libs
from salt.ext.six import string_types, iteritems
@ -77,7 +78,9 @@ def _changes(name,
win_homedrive=None,
win_profile=None,
win_logonscript=None,
win_description=None):
win_description=None,
allow_uid_change=False,
allow_gid_change=False):
'''
Return a dict of the changes required for a user if the user is present,
otherwise return False.
@ -191,6 +194,25 @@ def _changes(name,
if __salt__['user.get_loginclass'](name) != loginclass:
change['loginclass'] = loginclass
errors = []
if not allow_uid_change and 'uid' in change:
errors.append(
'Changing uid ({0} -> {1}) not permitted, set allow_uid_change to '
'True to force this change. Note that this will not change file '
'ownership.'.format(lusr['uid'], uid)
)
if not allow_gid_change and 'gid' in change:
errors.append(
'Changing gid ({0} -> {1}) not permitted, set allow_gid_change to '
'True to force this change. Note that this will not change file '
'ownership.'.format(lusr['gid'], gid)
)
if errors:
raise CommandExecutionError(
'Encountered error checking for needed changes',
info=errors
)
return change
@ -225,7 +247,9 @@ def present(name,
win_profile=None,
win_logonscript=None,
win_description=None,
nologinit=False):
nologinit=False,
allow_uid_change=False,
allow_gid_change=False):
'''
Ensure that the named user is present with the specified properties
@ -233,16 +257,28 @@ def present(name,
The name of the user to manage
uid
The user id to assign, if left empty then the next available user id
will be assigned
The user id to assign. If not specified, and the user does not exist,
then the next available uid will be assigned.
gid
The default group id. Also accepts group name.
The id of the default group to assign to the user. Either a group name
or gid can be used. If not specified, and the user does not exist, then
he next available gid will be assigned.
gid_from_name
If True, the default group id will be set to the id of the group with
the same name as the user. If the group does not exist the state will
fail. Default is ``False``.
gid_from_name : False
If ``True``, the default group id will be set to the id of the group
with the same name as the user. If the group does not exist the state
will fail.
allow_uid_change : False
Set to ``True`` to allow the state to update the uid.
.. versionadded:: 2018.3.1
allow_gid_change : False
Set to ``True`` to allow the state to update the gid.
.. versionadded:: 2018.3.1
groups
A list of groups to assign the user to, pass a list object. If a group
@ -466,6 +502,7 @@ def present(name,
ret['result'] = False
return ret
try:
changes = _changes(name,
uid,
gid,
@ -492,7 +529,13 @@ def present(name,
win_homedrive,
win_profile,
win_logonscript,
win_description)
win_description,
allow_uid_change,
allow_gid_change)
except CommandExecutionError as exc:
ret['result'] = False
ret['comment'] = exc.strerror
return ret
if changes:
if __opts__['test']:
@ -621,7 +664,13 @@ def present(name,
win_homedrive,
win_profile,
win_logonscript,
win_description)
win_description,
allow_uid_change=True,
allow_gid_change=True)
# allow_uid_change and allow_gid_change passed as True to avoid race
# conditions where a uid/gid is modified outside of Salt. If an
# unauthorized change was requested, it would have been caught the
# first time we ran _changes().
if changes:
ret['comment'] = 'These values could not be changed: {0}'.format(

View File

@ -1,587 +0,0 @@
# -*- coding: utf-8 -*-
'''
Management of the windows update agent
======================================
This module is being deprecated and will be removed in Salt Fluorine. Please use
the ``win_wua`` state module instead.
.. versionadded:: 2014.7.0
Set windows updates to run by category. Default behavior is to install
all updates that do not require user interaction to complete.
Optionally set ``category`` to a category of your choice to only
install certain updates. Default is to set to install all available updates.
The following example will install all Security and Critical Updates,
and download but not install standard updates.
.. code-block:: yaml
updates:
win_update.installed:
- categories:
- 'Critical Updates'
- 'Security Updates'
- skips:
- downloaded
win_update.downloaded:
- categories:
- 'Updates'
- skips:
- downloaded
You can also specify a number of features about the update to have a
fine grain approach to specific types of updates. These are the following
features/states of updates available for configuring:
.. code-block:: text
'UI' - User interaction required, skipped by default
'downloaded' - Already downloaded, included by default
'present' - Present on computer, skipped by default
'installed' - Already installed, skipped by default
'reboot' - Reboot required, included by default
'hidden' - Skip updates that have been hidden, skipped by default
'software' - Software updates, included by default
'driver' - driver updates, included by default
The following example installs all driver updates that don't require a reboot:
.. code-block:: yaml
gryffindor:
win_update.installed:
- skips:
- driver: True
- software: False
- reboot: False
To just update your windows machine, add this your sls:
.. code-block:: yaml
updates:
win_update.installed
'''
# Import Python libs
from __future__ import absolute_import, unicode_literals, print_function
import logging
# Import 3rd-party libs
# pylint: disable=import-error
from salt.ext import six
from salt.ext.six.moves import range # pylint: disable=redefined-builtin
try:
import win32com.client
import pythoncom
HAS_DEPENDENCIES = True
except ImportError:
HAS_DEPENDENCIES = False
# pylint: enable=import-error
# Import Salt libs
import salt.utils.platform
import salt.utils.versions
log = logging.getLogger(__name__)
def __virtual__():
'''
Only works on Windows systems
'''
if salt.utils.platform.is_windows() and HAS_DEPENDENCIES:
return True
return False
def _gather_update_categories(updateCollection):
'''
this is a convenience method to gather what categories of updates are available in any update
collection it is passed. Typically though, the download_collection.
Some known categories:
Updates
Windows 7
Critical Updates
Security Updates
Update Rollups
'''
categories = []
for i in range(updateCollection.Count):
update = updateCollection.Item(i)
for j in range(update.Categories.Count):
name = update.Categories.Item(j).Name
if name not in categories:
log.debug('found category: {0}'.format(name))
categories.append(name)
return categories
class PyWinUpdater(object):
def __init__(self, categories=None, skipUI=True, skipDownloaded=False,
skipInstalled=True, skipReboot=False, skipPresent=False,
skipSoftwareUpdates=False, skipDriverUpdates=False, skipHidden=True):
log.debug('CoInitializing the pycom system')
pythoncom.CoInitialize()
# pylint: disable=invalid-name
self.skipUI = skipUI
self.skipDownloaded = skipDownloaded
self.skipInstalled = skipInstalled
self.skipReboot = skipReboot
self.skipPresent = skipPresent
self.skipHidden = skipHidden
self.skipSoftwareUpdates = skipSoftwareUpdates
self.skipDriverUpdates = skipDriverUpdates
self.categories = categories
self.foundCategories = None
# pylint: enable=invalid-name
log.debug('dispatching update_session to keep the session object.')
self.update_session = win32com.client.Dispatch('Microsoft.Update.Session')
log.debug('update_session got. Now creating a win_searcher to seek out the updates')
self.win_searcher = self.update_session.CreateUpdateSearcher()
# list of updates that are applicable by current settings.
self.download_collection = win32com.client.Dispatch('Microsoft.Update.UpdateColl')
# list of updates to be installed.
self.install_collection = win32com.client.Dispatch('Microsoft.Update.UpdateColl')
# the object responsible for fetching the actual downloads.
self.win_downloader = self.update_session.CreateUpdateDownloader()
self.win_downloader.Updates = self.download_collection
# the object responsible for the installing of the updates.
self.win_installer = self.update_session.CreateUpdateInstaller()
self.win_installer.Updates = self.install_collection
# the results of the download process
self.download_results = None
# the results of the installation process
self.install_results = None
def Search(self, searchString):
try:
log.debug('beginning search of the passed string: %s',
searchString)
self.search_results = self.win_searcher.Search(searchString)
log.debug('search completed successfully.')
except Exception as exc:
log.info('search for updates failed. %s', exc)
return exc
log.debug('parsing results. %s updates were found.',
self.search_results.Updates.Count)
try:
for update in self.search_results.Updates:
if update.InstallationBehavior.CanRequestUserInput:
log.debug('Skipped update %s', update.title)
continue
for category in update.Categories:
if self.skipDownloaded and update.IsDownloaded:
continue
if self.categories is None or category.Name in self.categories:
self.download_collection.Add(update)
log.debug('added update %s', update.title)
self.foundCategories = _gather_update_categories(self.download_collection)
return True
except Exception as exc:
log.info('parsing updates failed. %s', exc)
return exc
def AutoSearch(self):
search_string = ''
searchParams = []
if self.skipInstalled:
searchParams.append('IsInstalled=0')
else:
searchParams.append('IsInstalled=1')
if self.skipHidden:
searchParams.append('IsHidden=0')
else:
searchParams.append('IsHidden=1')
if self.skipReboot:
searchParams.append('RebootRequired=0')
else:
searchParams.append('RebootRequired=1')
if self.skipPresent:
searchParams.append('IsPresent=0')
else:
searchParams.append('IsPresent=1')
if len(searchParams) > 1:
for i in searchParams:
search_string += '{0} and '.format(i)
else:
search_string += '{0} and '.format(searchParams[1])
if not self.skipSoftwareUpdates and not self.skipDriverUpdates:
search_string += 'Type=\'Software\' or Type=\'Driver\''
elif not self.skipSoftwareUpdates:
search_string += 'Type=\'Software\''
elif not self.skipDriverUpdates:
search_string += 'Type=\'Driver\''
else:
return False
# if there is no type, the is nothing to search.
log.debug('generated search string: %s', search_string)
return self.Search(search_string)
def Download(self):
try:
if self.download_collection.Count != 0:
self.download_results = self.win_downloader.Download()
else:
log.debug('Skipped downloading, all updates were already cached.')
return True
except Exception as exc:
log.debug('failed in the downloading %s.', exc)
return exc
def Install(self):
try:
for update in self.search_results.Updates:
if update.IsDownloaded:
self.install_collection.Add(update)
log.debug('Updates prepared. beginning installation')
except Exception as exc:
log.info('Preparing install list failed: %s', exc)
return exc
# accept eula if not accepted
try:
for update in self.search_results.Updates:
if not update.EulaAccepted:
log.debug('Accepting EULA: %s', update.Title)
update.AcceptEula()
except Exception as exc:
log.info('Accepting Eula failed: %s', exc)
return exc
if self.install_collection.Count != 0:
log.debug('Install list created, about to install')
updates = []
try:
self.install_results = self.win_installer.Install()
log.info('Installation of updates complete')
return True
except Exception as exc:
log.info('Installation failed: %s', exc)
return exc
else:
log.info('no new updates.')
return True
def GetInstallationResults(self):
log.debug('bluger has %s updates in it', self.install_collection.Count)
updates = []
if self.install_collection.Count == 0:
return {}
for i in range(self.install_collection.Count):
updates.append('{0}: {1}'.format(
self.install_results.GetUpdateResult(i).ResultCode,
self.install_collection.Item(i).Title))
log.debug('Update results enumerated, now making a list to pass back')
results = {}
for i, update in enumerate(updates):
results['update {0}'.format(i)] = update
log.debug('Update information complied. returning')
return results
def GetDownloadResults(self):
updates = []
for i in range(self.download_collection.Count):
updates.append('{0}: {1}'.format(
self.download_results.GetUpdateResult(i).ResultCode,
self.download_collection.Item(i).Title))
results = {}
for i, update in enumerate(updates):
results['update {0}'.format(i)] = update
return results
def SetCategories(self, categories):
self.categories = categories
def GetCategories(self):
return self.categories
def GetAvailableCategories(self):
return self.foundCategories
def SetSkips(self, skips):
if skips:
for i in skips:
value = i[next(six.iterkeys(i))]
skip = next(six.iterkeys(i))
self.SetSkip(skip, value)
log.debug('was asked to set %s to %s', skip, value)
def SetSkip(self, skip, state):
if skip == 'UI':
self.skipUI = state
elif skip == 'downloaded':
self.skipDownloaded = state
elif skip == 'installed':
self.skipInstalled = state
elif skip == 'reboot':
self.skipReboot = state
elif skip == 'present':
self.skipPresent = state
elif skip == 'hidden':
self.skipHidden = state
elif skip == 'software':
self.skipSoftwareUpdates = state
elif skip == 'driver':
self.skipDriverUpdates = state
log.debug('new search state: \n\tUI: %s\n\tDownload: %s\n'
'\tInstalled: %s\n\treboot :%s\n\tPresent: %s\n'
'\thidden: %s\n\tsoftware: %s\n\tdriver: %s',
self.skipUI, self.skipDownloaded, self.skipInstalled,
self.skipReboot, self.skipPresent, self.skipHidden,
self.skipSoftwareUpdates, self.skipDriverUpdates)
def _search(win_updater, retries=5):
passed = False
clean = True
comment = ''
while not passed:
log.debug('Searching. tries left: %s', retries)
passed = win_updater.AutoSearch()
log.debug('Done searching: %s', passed)
if isinstance(passed, Exception):
clean = False
comment += 'Failed in the seeking/parsing process:\n\t\t{0}\n'.format(passed)
retries -= 1
if retries:
comment += '{0} tries to go. retrying\n'.format(retries)
passed = False
else:
comment += 'out of retries. this update round failed.\n'
return (comment, True, retries)
passed = False
if clean:
comment += 'Search was done without error.\n'
return (comment, True, retries)
def _download(win_updater, retries=5):
passed = False
clean = True
comment = ''
while not passed:
log.debug('Downloading. tries left: %s', retries)
passed = win_updater.Download()
log.debug('Done downloading: %s', passed)
if isinstance(passed, Exception):
clean = False
comment += 'Failed while trying to download updates:\n\t\t{0}\n'.format(passed)
retries -= 1
if retries:
comment += '{0} tries to go. retrying\n'.format(retries)
passed = False
else:
comment += 'out of retries. this update round failed.\n'
return (comment, False, retries)
if clean:
comment += 'Download was done without error.\n'
return (comment, True, retries)
def _install(win_updater, retries=5):
passed = False
clean = True
comment = ''
while not passed:
log.debug('download_collection is this long: %s',
win_updater.install_collection.Count)
log.debug('Installing. tries left: %s', retries)
passed = win_updater.Install()
log.info('Done installing: %s', passed)
if isinstance(passed, Exception):
clean = False
comment += 'Failed while trying to install the updates.\n\t\t{0}\n'.format(passed)
retries -= 1
if retries:
comment += '{0} tries to go. retrying\n'.format(retries)
passed = False
else:
comment += 'out of retries. this update round failed.\n'
return (comment, False, retries)
if clean:
comment += 'Install was done without error.\n'
return (comment, True, retries)
def installed(name, categories=None, skips=None, retries=10):
'''
Install specified windows updates.
name:
if categories is left empty, it will be assumed that you are passing the category option
through the name. These are separate because you can only have one name, but can have
multiple categories.
categories:
the list of categories to be downloaded. These are simply strings in the update's
information, so there is no enumeration of the categories available. Some known categories:
.. code-block:: text
Updates
Windows 7
Critical Updates
Security Updates
Update Rollups
skips:
a list of features of the updates to cull by. Available features:
.. code-block:: text
'UI' - User interaction required, skipped by default
'downloaded' - Already downloaded, skipped by default (downloading)
'present' - Present on computer, included by default (installing)
'installed' - Already installed, skipped by default
'reboot' - Reboot required, included by default
'hidden' - skip those updates that have been hidden.
'software' - Software updates, included by default
'driver' - driver updates, skipped by default
retries
Number of retries to make before giving up. This is total, not per
step.
'''
ret = {'name': name,
'result': True,
'changes': {},
'comment': ''}
deprecation_msg = 'The \'win_update\' module is deprecated, and will be ' \
'removed in Salt Fluorine. Please use the \'win_wua\' ' \
'module instead.'
salt.utils.versions.warn_until('Fluorine', deprecation_msg)
ret.setdefault('warnings', []).append(deprecation_msg)
if not categories:
categories = [name]
log.debug('categories to search for are: %s', categories)
win_updater = PyWinUpdater()
win_updater.SetCategories(categories)
win_updater.SetSkips(skips)
# this is where we be seeking the things! yar!
comment, passed, retries = _search(win_updater, retries)
ret['comment'] += comment
if not passed:
ret['result'] = False
return ret
# this is where we get all the things! i.e. download updates.
comment, passed, retries = _download(win_updater, retries)
ret['comment'] += comment
if not passed:
ret['result'] = False
return ret
# this is where we put things in their place!
comment, passed, retries = _install(win_updater, retries)
ret['comment'] += comment
if not passed:
ret['result'] = False
return ret
try:
ret['changes'] = win_updater.GetInstallationResults()
except Exception:
ret['comment'] += 'could not get results, but updates were installed.'
return ret
def downloaded(name, categories=None, skips=None, retries=10):
'''
Cache updates for later install.
name:
if categories is left empty, it will be assumed that you are passing the category option
through the name. These are separate because you can only have one name, but can have
multiple categories.
categories:
the list of categories to be downloaded. These are simply strings in the update's
information, so there is no enumeration of the categories available. Some known categories:
.. code-block:: text
Updates
Windows 7
Critical Updates
Security Updates
Update Rollups
skips:
a list of features of the updates to cull by. Available features:
.. code-block:: text
'UI' - User interaction required, skipped by default
'downloaded' - Already downloaded, skipped by default (downloading)
'present' - Present on computer, included by default (installing)
'installed' - Already installed, skipped by default
'reboot' - Reboot required, included by default
'hidden' - skip those updates that have been hidden.
'software' - Software updates, included by default
'driver' - driver updates, skipped by default
retries
Number of retries to make before giving up. This is total, not per
step.
'''
ret = {'name': name,
'result': True,
'changes': {},
'comment': ''}
deprecation_msg = 'The \'win_update\' module is deprecated, and will be ' \
'removed in Salt Fluorine. Please use the \'win_wua\' ' \
'module instead.'
salt.utils.versions.warn_until('Fluorine', deprecation_msg)
ret.setdefault('warnings', []).append(deprecation_msg)
if not categories:
categories = [name]
log.debug('categories to search for are: %s', categories)
win_updater = PyWinUpdater()
win_updater.SetCategories(categories)
win_updater.SetSkips(skips)
# this is where we be seeking the things! yar!
comment, passed, retries = _search(win_updater, retries)
ret['comment'] += comment
if not passed:
ret['result'] = False
return ret
# this is where we get all the things! i.e. download updates.
comment, passed, retries = _download(win_updater, retries)
ret['comment'] += comment
if not passed:
ret['result'] = False
return ret
try:
ret['changes'] = win_updater.GetDownloadResults()
except Exception:
ret['comment'] += 'could not get results, but updates were downloaded.'
return ret

View File

@ -193,7 +193,7 @@ def present(host, groups, interfaces, **kwargs):
host_exists = __salt__['zabbix.host_exists'](host, **connection_args)
if host_exists:
host = __salt__['zabbix.host_get'](name=host, **connection_args)[0]
host = __salt__['zabbix.host_get'](host=host, **connection_args)[0]
hostid = host['hostid']
update_proxy = False
@ -457,7 +457,7 @@ def assign_templates(host, templates, **kwargs):
ret['comment'] = comment_host_templ_notupdated
return ret
host_info = __salt__['zabbix.host_get'](name=host, **connection_args)[0]
host_info = __salt__['zabbix.host_get'](host=host, **connection_args)[0]
hostid = host_info['hostid']
if not templates:

View File

@ -21,6 +21,7 @@ import errno
import salt.crypt
import salt.utils.async
import salt.utils.event
import salt.utils.files
import salt.utils.platform
import salt.utils.process
import salt.utils.verify
@ -1386,11 +1387,8 @@ class TCPPubServerChannel(salt.transport.server.PubServerChannel):
# Securely create socket
log.info('Starting the Salt Puller on %s', pull_uri)
old_umask = os.umask(0o177)
try:
with salt.utils.files.set_umask(0o177):
pull_sock.start()
finally:
os.umask(old_umask)
# run forever
try:

View File

@ -19,6 +19,7 @@ from random import randint
import salt.auth
import salt.crypt
import salt.utils.event
import salt.utils.files
import salt.utils.minions
import salt.utils.process
import salt.utils.stringutils
@ -806,11 +807,8 @@ class ZeroMQPubServerChannel(salt.transport.server.PubServerChannel):
# Securely create socket
log.info('Starting the Salt Puller on %s', pull_uri)
old_umask = os.umask(0o177)
try:
with salt.utils.files.set_umask(0o177):
pull_sock.bind(pull_uri)
finally:
os.umask(old_umask)
try:
while True:

View File

@ -142,7 +142,8 @@ def yamlify_arg(arg):
return arg
if arg.strip() == '':
# Because YAML loads empty strings as None, we return the original string
# Because YAML loads empty (or all whitespace) strings as None, we
# return the original string
# >>> import yaml
# >>> yaml.load('') is None
# True
@ -151,6 +152,9 @@ def yamlify_arg(arg):
return arg
elif '_' in arg and all([x in '0123456789_' for x in arg.strip()]):
# When the stripped string includes just digits and underscores, the
# underscores are ignored and the digits are combined together and
# loaded as an int. We don't want that, so return the original value.
return arg
try:
@ -177,6 +181,14 @@ def yamlify_arg(arg):
else:
return arg
elif isinstance(arg, list):
# lists must be wrapped in brackets
if (isinstance(original_arg, six.string_types) and
not original_arg.startswith('[')):
return original_arg
else:
return arg
elif arg is None \
or isinstance(arg, (list, float, six.integer_types, six.string_types)):
# yaml.safe_load will load '|' as '', don't let it do that.

View File

@ -10,7 +10,6 @@ input as formatted by states.
from __future__ import absolute_import, print_function, unicode_literals
import copy
import logging
import os
# Import Salt libs
import salt.utils.args

View File

@ -75,6 +75,7 @@ import salt.payload
import salt.utils.async
import salt.utils.cache
import salt.utils.dicttrim
import salt.utils.files
import salt.utils.platform
import salt.utils.process
import salt.utils.stringutils
@ -1018,12 +1019,9 @@ class AsyncEventPublisher(object):
)
log.info('Starting pull socket on {0}'.format(epull_uri))
old_umask = os.umask(0o177)
try:
with salt.utils.files.set_umask(0o177):
self.publisher.start()
self.puller.start()
finally:
os.umask(old_umask)
def handle_publish(self, package, _):
'''
@ -1106,8 +1104,7 @@ class EventPublisher(salt.utils.process.SignalHandlingMultiprocessingProcess):
)
# Start the master event publisher
old_umask = os.umask(0o177)
try:
with salt.utils.files.set_umask(0o177):
self.publisher.start()
self.puller.start()
if (self.opts['ipc_mode'] != 'tcp' and (
@ -1115,8 +1112,6 @@ class EventPublisher(salt.utils.process.SignalHandlingMultiprocessingProcess):
self.opts['external_auth'])):
os.chmod(os.path.join(
self.opts['sock_dir'], 'master_event_pub.ipc'), 0o666)
finally:
os.umask(old_umask)
# Make sure the IO loop and respective sockets are closed and
# destroyed

View File

@ -11,6 +11,7 @@ import shutil
# Import salt libs
import salt.fileclient
import salt.utils.files
import salt.utils.hashutils
import salt.utils.path
import salt.utils.url
@ -71,8 +72,8 @@ def sync(opts,
remote = set()
source = salt.utils.url.create('_' + form)
mod_dir = os.path.join(opts['extension_modules'], '{0}'.format(form))
cumask = os.umask(0o77)
touched = False
with salt.utils.files.set_umask(0o077):
try:
if not os.path.isdir(mod_dir):
log.info('Creating module dir \'%s\'', mod_dir)
@ -150,6 +151,4 @@ def sync(opts,
shutil.rmtree(emptydir, ignore_errors=True)
except Exception as exc:
log.error('Failed to sync %s module: %s', form, exc)
finally:
os.umask(cumask)
return ret, touched

View File

@ -299,20 +299,29 @@ def wait_lock(path, lock_fn=None, timeout=5, sleep=0.1, time_start=None):
log.trace('Write lock for %s (%s) released', path, lock_fn)
def get_umask():
'''
Returns the current umask
'''
ret = os.umask(0) # pylint: disable=blacklisted-function
os.umask(ret) # pylint: disable=blacklisted-function
return ret
@contextlib.contextmanager
def set_umask(mask):
'''
Temporarily set the umask and restore once the contextmanager exits
'''
if salt.utils.platform.is_windows():
# Don't attempt on Windows
if mask is None or salt.utils.platform.is_windows():
# Don't attempt on Windows, or if no mask was passed
yield
else:
try:
orig_mask = os.umask(mask)
orig_mask = os.umask(mask) # pylint: disable=blacklisted-function
yield
finally:
os.umask(orig_mask)
os.umask(orig_mask) # pylint: disable=blacklisted-function
def fopen(*args, **kwargs):

View File

@ -81,6 +81,13 @@ class SaltNeutron(NeutronShell):
'''
Set up neutron credentials
'''
__utils__['versions.warn_until'](
'Neon',
(
'The neutron module has been deprecated and will be removed in {version}. '
'Please update to using the neutronng module'
),
)
if not HAS_NEUTRON:
return None

View File

@ -715,9 +715,8 @@ class LogLevelMixIn(six.with_metaclass(MixInMeta, object)):
# verify the default
if logfile is not None and not logfile.startswith(('tcp://', 'udp://', 'file://')):
# Logfile is not using Syslog, verify
current_umask = os.umask(0o027)
with salt.utils.files.set_umask(0o027):
verify_files([logfile], self.config['user'])
os.umask(current_umask)
if logfile is None:
# Use the default setting if the logfile wasn't explicity set

View File

@ -80,7 +80,7 @@ def daemonize(redirect_out=True):
os.chdir('/')
# noinspection PyArgumentList
os.setsid()
os.umask(18)
os.umask(0o022) # pylint: disable=blacklisted-function
# do second fork
try:

View File

@ -426,7 +426,9 @@ class Schedule(object):
# Grab run, assume True
run = data.get('run', True)
run_schedule_jobs_in_background = self.opts.get('run_schedule_jobs_in_background', True)
if run:
if run_schedule_jobs_in_background:
multiprocessing_enabled = self.opts.get('multiprocessing', True)
if multiprocessing_enabled:
thread_cls = salt.utils.process.SignalHandlingMultiprocessingProcess
@ -443,6 +445,8 @@ class Schedule(object):
else:
proc = thread_cls(target=self.handle_func, args=(multiprocessing_enabled, func, data))
proc.start()
else:
func(data)
def enable_schedule(self):
'''
@ -1474,7 +1478,7 @@ class Schedule(object):
# If there is no job specific skip_during_range available,
# grab the global which defaults to None.
if 'skip_during_range' not in data:
if 'skip_during_range' not in data and self.skip_during_range:
data['skip_during_range'] = self.skip_during_range
if 'skip_during_range' in data and data['skip_during_range']:

View File

@ -254,7 +254,7 @@ def chugid_and_umask(runas, umask, group=None):
if set_runas or set_grp:
chugid(runas_user, runas_grp)
if umask is not None:
os.umask(umask)
os.umask(umask) # pylint: disable=blacklisted-function
def get_default_group(user):

View File

@ -230,12 +230,11 @@ def verify_env(
continue
if not os.path.isdir(dir_):
try:
cumask = os.umask(18) # 077
with salt.utils.files.set_umask(0o022):
os.makedirs(dir_)
# If starting the process as root, chown the new dirs
if os.getuid() == 0:
os.chown(dir_, uid, gid)
os.umask(cumask)
except OSError as err:
msg = 'Failed to create directory path "{0}" - {1}\n'
sys.stderr.write(msg.format(dir_, err))

View File

@ -1,6 +1,6 @@
profitbricks-config:
username: ''
password: ''
username: 'foo'
password: 'bar'
datacenter_id: 74d65326-d9b7-41c3-9f51-73ffe0fcd16d
driver: profitbricks
ssh_public_key: ~/.ssh/id_rsa.pub

View File

@ -0,0 +1,38 @@
{%- set virtualenv_base = salt['runtests_helpers.get_salt_temp_dir_for_path']('virtualenv-12-base-1') -%}
{%- set virtualenv_test = salt['runtests_helpers.get_salt_temp_dir_for_path']('issue-46127-pip-env-vars') -%}
{{ virtualenv_base }}:
virtualenv.managed:
- system_site_packages: False
- distribute: True
install_older_venv_1:
pip.installed:
- name: 'virtualenv < 13.0'
- bin_env: {{ virtualenv_base }}
- require:
- virtualenv: {{ virtualenv_base }}
# For this test we need to make sure that the virtualenv used in the
# 'issue-46127-setup' pip.installed state below was created using
# virtualenv < 13.0. virtualenvs created using later versions make
# packages with custom setuptools prefixes relative to the virtualenv
# itself, which makes the use of env_vars obsolete.
# Thus, the two states above ensure that the 'base' venv has
# a version old enough to exhibit the behavior we want to test.
setup_test_virtualenv_1:
cmd.run:
- name: {{ virtualenv_base }}/bin/virtualenv {{ virtualenv_test }}
- onchanges:
- pip: install_older_venv_1
issue-46127-setup:
pip.installed:
- name: 'carbon < 1.3'
- no_deps: True
- env_vars:
PYTHONPATH: "/opt/graphite/lib/:/opt/graphite/webapp/"
- bin_env: {{ virtualenv_test }}
- require:
- cmd: setup_test_virtualenv_1

View File

@ -0,0 +1,66 @@
# -*- coding: utf-8 -*-
'''
Tests for the salt-run command
'''
# Import Python libs
from __future__ import absolute_import, print_function, unicode_literals
import salt.utils.stringutils
# Import Salt Testing libs
from tests.support.case import ModuleCase
from tests.support.unit import skipIf
try:
import libnacl # pylint: disable=unused-import
HAS_LIBNACL = True
except ImportError:
HAS_LIBNACL = False
@skipIf(not HAS_LIBNACL, 'skipping test_nacl, libnacl is unavailable')
class NaclTest(ModuleCase):
'''
Test the nacl runner
'''
def test_keygen(self):
'''
Test keygen
'''
# Store the data
ret = self.run_function(
'nacl.keygen',
)
self.assertIn('pk', ret)
self.assertIn('sk', ret)
def test_enc_dec(self):
'''
Generate keys, encrypt, then decrypt.
'''
# Store the data
ret = self.run_function(
'nacl.keygen',
)
self.assertIn('pk', ret)
self.assertIn('sk', ret)
pk = ret['pk']
sk = ret['sk']
unencrypted_data = salt.utils.stringutils.to_bytes('hello')
# Encrypt with pk
ret = self.run_function(
'nacl.enc',
data=unencrypted_data,
pk=pk,
)
encrypted_data = ret
# Decrypt with sk
ret = self.run_function(
'nacl.dec',
data=encrypted_data,
sk=sk,
)
self.assertEqual(unencrypted_data, ret)

View File

@ -0,0 +1,89 @@
# -*- coding: utf-8 -*-
'''
Tests for the salt-run command
'''
# Import Python libs
from __future__ import absolute_import, print_function, unicode_literals
# Import Salt Testing libs
from tests.support.case import ShellCase
from tests.support.unit import skipIf
try:
import libnacl # pylint: disable=unused-import
HAS_LIBNACL = True
except ImportError:
HAS_LIBNACL = False
@skipIf(not HAS_LIBNACL, 'skipping test_nacl, libnacl is unavailable')
class NaclTest(ShellCase):
'''
Test the nacl runner
'''
def test_keygen(self):
'''
Test keygen
'''
# Store the data
ret = self.run_run_plus(
'nacl.keygen',
)
self.assertIn('pk', ret['return'])
self.assertIn('sk', ret['return'])
def test_enc(self):
'''
Test keygen
'''
# Store the data
ret = self.run_run_plus(
'nacl.keygen',
)
self.assertIn('pk', ret['return'])
self.assertIn('sk', ret['return'])
pk = ret['return']['pk']
sk = ret['return']['sk']
unencrypted_data = 'hello'
# Encrypt with pk
ret = self.run_run_plus(
'nacl.enc',
data=unencrypted_data,
pk=pk,
)
self.assertIn('return', ret)
def test_enc_dec(self):
'''
Store, list, fetch, then flush data
'''
# Store the data
ret = self.run_run_plus(
'nacl.keygen',
)
self.assertIn('pk', ret['return'])
self.assertIn('sk', ret['return'])
pk = ret['return']['pk']
sk = ret['return']['sk']
unencrypted_data = 'hello'
# Encrypt with pk
ret = self.run_run_plus(
'nacl.enc',
data=unencrypted_data,
pk=pk,
)
self.assertIn('return', ret)
encrypted_data = ret['return']
# Decrypt with sk
ret = self.run_run_plus(
'nacl.dec',
data=encrypted_data,
sk=sk,
)
self.assertIn('return', ret)
self.assertEqual(unencrypted_data, ret['return'])

View File

@ -515,3 +515,32 @@ class SchedulerEvalTest(ModuleCase, SaltReturnAssertsMixin):
ret = self.schedule.job_status('job1')
self.assertNotIn('_last_run', ret)
self.assertEqual(ret['_skip_reason'], 'disabled')
def test_eval_run_on_start(self):
'''
verify that scheduled job is run when minion starts
'''
job = {
'schedule': {
'job1': {
'function': 'test.ping',
'hours': '1',
'run_on_start': True
}
}
}
# Add job to schedule
self.schedule.opts.update(job)
# eval at 2:00pm, will run.
run_time = dateutil_parser.parse('11/29/2017 2:00pm')
self.schedule.eval(now=run_time)
ret = self.schedule.job_status('job1')
self.assertEqual(ret['_last_run'], run_time)
# eval at 3:00pm, will run.
run_time = dateutil_parser.parse('11/29/2017 3:00pm')
self.schedule.eval(now=run_time)
ret = self.schedule.job_status('job1')
self.assertEqual(ret['_last_run'], run_time)

View File

@ -363,7 +363,7 @@ class CallTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMixin
def test_issue_14979_output_file_permissions(self):
output_file = os.path.join(TMP, 'issue-14979')
current_umask = os.umask(0o077)
with salt.utils.files.set_umask(0o077):
try:
# Let's create an initial output file with some data
self.run_script(
@ -378,7 +378,7 @@ class CallTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMixin
stat1 = os.stat(output_file)
# Let's change umask
os.umask(0o777)
os.umask(0o777) # pylint: disable=blacklisted-function
self.run_script(
'salt-call',
@ -413,8 +413,6 @@ class CallTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMixin
finally:
if os.path.exists(output_file):
os.unlink(output_file)
# Restore umask
os.umask(current_umask)
@skipIf(sys.platform.startswith('win'), 'This test does not apply on Win')
def test_42116_cli_pillar_override(self):

View File

@ -524,3 +524,78 @@ class PipStateTest(ModuleCase, SaltReturnAssertsMixin):
finally:
if os.path.isdir(venv_dir):
shutil.rmtree(venv_dir)
def test_46127_pip_env_vars(self):
'''
Test that checks if env_vars passed to pip.installed are also passed
to pip.freeze while checking for existing installations
'''
# This issue is most easily checked while installing carbon
# Much of the code here comes from the test_weird_install function above
ographite = '/opt/graphite'
if os.path.isdir(ographite):
self.skipTest(
'You already have \'{0}\'. This test would overwrite this '
'directory'.format(ographite)
)
try:
os.makedirs(ographite)
except OSError as err:
if err.errno == errno.EACCES:
# Permission denied
self.skipTest(
'You don\'t have the required permissions to run this test'
)
finally:
if os.path.isdir(ographite):
shutil.rmtree(ographite)
venv_dir = os.path.join(RUNTIME_VARS.TMP, 'issue-46127-pip-env-vars')
try:
# We may be able to remove this, I had to add it because the custom
# modules from the test suite weren't available in the jinja
# context when running the call to state.sls that comes after.
self.run_function('saltutil.sync_modules')
# Since we don't have the virtualenv created, pip.installed will
# thrown and error.
ret = self.run_function(
'state.sls', mods='issue-46127-pip-env-vars'
)
self.assertSaltTrueReturn(ret)
for key in six.iterkeys(ret):
self.assertTrue(ret[key]['result'])
if ret[key]['name'] != 'carbon < 1.3':
continue
self.assertEqual(
ret[key]['comment'],
'All packages were successfully installed'
)
break
else:
raise Exception('Expected state did not run')
# Run the state again. Now the already installed message should
# appear
ret = self.run_function(
'state.sls', mods='issue-46127-pip-env-vars'
)
self.assertSaltTrueReturn(ret)
# We cannot use assertInSaltComment here because we need to skip
# some of the state return parts
for key in six.iterkeys(ret):
self.assertTrue(ret[key]['result'])
# As we are re-running the formula, some states will not be run
# and "name" may or may not be present, so we use .get() pattern
if ret[key].get('name', '') != 'carbon < 1.3':
continue
self.assertEqual(
ret[key]['comment'],
('Python package carbon < 1.3 was already installed\n'
'All packages were successfully installed'))
break
else:
raise Exception('Expected state did not run')
finally:
if os.path.isdir(ographite):
shutil.rmtree(ographite)
if os.path.isdir(venv_dir):
shutil.rmtree(venv_dir)

View File

@ -937,6 +937,27 @@ class PipTestCase(TestCase, LoaderModuleMockMixin):
)
self.assertEqual(ret, eggs)
mock = MagicMock(
return_value={
'retcode': 0,
'stdout': '\n'.join(eggs)
}
)
# Passing env_vars passes them to underlying command?
with patch.dict(pip.__salt__, {'cmd.run_all': mock}):
with patch('salt.modules.pip.version',
MagicMock(return_value='6.1.1')):
ret = pip.freeze(env_vars={"foo": "bar"})
mock.assert_called_once_with(
['pip', 'freeze'],
cwd=None,
runas=None,
use_vt=False,
python_shell=False,
env={"foo": "bar"}
)
self.assertEqual(ret, eggs)
# Non zero returncode raises exception?
mock = MagicMock(return_value={'retcode': 1, 'stderr': 'CABOOOOMMM!'})
with patch.dict(pip.__salt__, {'cmd.run_all': mock}):

View File

@ -91,7 +91,7 @@ class TestEventListener(AsyncTestCase):
{'sock_dir': SOCK_DIR,
'transport': 'zeromq'})
self._finished = False # fit to event_listener's behavior
event_future = event_listener.get_event(self, 'evt1', self.stop) # get an event future
event_future = event_listener.get_event(self, 'evt1', callback=self.stop) # get an event future
me.fire_event({'data': 'foo2'}, 'evt2') # fire an event we don't want
me.fire_event({'data': 'foo1'}, 'evt1') # fire an event we do want
self.wait() # wait for the future

View File

@ -10,6 +10,7 @@ from __future__ import absolute_import, print_function, unicode_literals
from tests.support.mixins import LoaderModuleMockMixin
from tests.support.unit import TestCase, skipIf
from tests.support.mock import (
Mock,
MagicMock,
patch,
NO_MOCK,
@ -79,6 +80,110 @@ class UserTestCase(TestCase, LoaderModuleMockMixin):
' user salt', 'result': False})
self.assertDictEqual(user.present('salt'), ret)
def test_present_invalid_uid_change(self):
mock_info = MagicMock(side_effect=[
{'uid': 5000,
'gid': 5000,
'groups': ['foo'],
'home': '/home/foo',
'fullname': 'Foo Bar'}
])
dunder_salt = {'user.info': mock_info,
'file.group_to_gid': MagicMock(side_effect=['foo']),
'file.gid_to_group': MagicMock(side_effect=[5000])}
# side_effect used because these mocks should only be called once
with patch.dict(user.__grains__, {'kernel': 'Linux'}), \
patch.dict(user.__salt__, dunder_salt):
ret = user.present('foo', uid=5001)
# State should have failed
self.assertFalse(ret['result'])
# Only one of uid/gid should have been flagged in the comment
self.assertEqual(ret['comment'].count('not permitted'), 1)
def test_present_invalid_gid_change(self):
mock_info = MagicMock(side_effect=[
{'uid': 5000,
'gid': 5000,
'groups': ['foo'],
'home': '/home/foo',
'fullname': 'Foo Bar'}
])
dunder_salt = {'user.info': mock_info,
'file.group_to_gid': MagicMock(side_effect=['foo']),
'file.gid_to_group': MagicMock(side_effect=[5000])}
# side_effect used because these mocks should only be called once
with patch.dict(user.__grains__, {'kernel': 'Linux'}), \
patch.dict(user.__salt__, dunder_salt):
ret = user.present('foo', gid=5001)
# State should have failed
self.assertFalse(ret['result'])
# Only one of uid/gid should have been flagged in the comment
self.assertEqual(ret['comment'].count('not permitted'), 1)
def test_present_invalid_uid_gid_change(self):
mock_info = MagicMock(side_effect=[
{'uid': 5000,
'gid': 5000,
'groups': ['foo'],
'home': '/home/foo',
'fullname': 'Foo Bar'}
])
dunder_salt = {'user.info': mock_info,
'file.group_to_gid': MagicMock(side_effect=['foo']),
'file.gid_to_group': MagicMock(side_effect=[5000])}
# side_effect used because these mocks should only be called once
with patch.dict(user.__grains__, {'kernel': 'Linux'}), \
patch.dict(user.__salt__, dunder_salt):
ret = user.present('foo', uid=5001, gid=5001)
# State should have failed
self.assertFalse(ret['result'])
# Both the uid and gid should have been flagged in the comment
self.assertEqual(ret['comment'].count('not permitted'), 2)
def test_present_uid_gid_change(self):
before = {'uid': 5000,
'gid': 5000,
'groups': ['foo'],
'home': '/home/foo',
'fullname': 'Foo Bar'}
after = {'uid': 5001,
'gid': 5001,
'groups': ['othergroup'],
'home': '/home/foo',
'fullname': 'Foo Bar'}
# user.info should be called 4 times. Once the first time that
# _changes() is called, once before and after changes are applied (to
# get the before/after for the changes dict, and one last time to
# confirm that no changes still need to be made.
mock_info = MagicMock(side_effect=[before, before, after, after])
mock_group_to_gid = MagicMock(side_effect=['foo', 'othergroup'])
mock_gid_to_group = MagicMock(side_effect=[5000, 5001])
dunder_salt = {'user.info': mock_info,
'user.chuid': Mock(),
'user.chgid': Mock(),
'file.group_to_gid': mock_group_to_gid,
'file.gid_to_group': mock_gid_to_group}
# side_effect used because these mocks should only be called once
with patch.dict(user.__grains__, {'kernel': 'Linux'}), \
patch.dict(user.__salt__, dunder_salt), \
patch.dict(user.__opts__, {'test': False}), \
patch('os.path.isdir', MagicMock(return_value=True)):
ret = user.present(
'foo',
uid=5001,
gid=5001,
allow_uid_change=True,
allow_gid_change=True)
self.assertEqual(
ret,
{'comment': 'Updated user foo',
'changes': {'gid': 5001,
'uid': 5001,
'groups': ['othergroup']},
'name': 'foo',
'result': True}
)
def test_absent(self):
'''
Test to ensure that the named user is absent

View File

@ -1,133 +0,0 @@
# -*- coding: utf-8 -*-
'''
:codeauthor: :email:`Rahul Handay <rahulha@saltstack.com>`
'''
# Import Python Libs
from __future__ import absolute_import, unicode_literals, print_function
# Import Salt Testing Libs
from tests.support.mixins import LoaderModuleMockMixin
from tests.support.unit import TestCase, skipIf
from tests.support.mock import (
MagicMock,
patch,
NO_MOCK,
NO_MOCK_REASON
)
# Import Salt Libs
import salt.states.win_update as win_update
class MockPyWinUpdater(object):
'''
Mock PyWinUpdater class
'''
def __init__(self):
pass
@staticmethod
def SetCategories(arg):
'''
Mock SetCategories
'''
return arg
@staticmethod
def SetIncludes(arg):
'''
Mock SetIncludes
'''
return arg
@staticmethod
def GetInstallationResults():
'''
Mock GetInstallationResults
'''
return True
@staticmethod
def GetDownloadResults():
'''
Mock GetDownloadResults
'''
return True
@staticmethod
def SetSkips(arg):
return True
@skipIf(NO_MOCK, NO_MOCK_REASON)
class WinUpdateTestCase(TestCase, LoaderModuleMockMixin):
'''
Validate the win_update state
'''
def setup_loader_modules(self):
return {win_update: {'PyWinUpdater': MockPyWinUpdater}}
def test_installed(self):
'''
Test to install specified windows updates
'''
ret = {'name': 'salt',
'changes': {},
'result': False,
'comment': '',
'warnings': ["The 'win_update' module is deprecated, and will "
"be removed in Salt Fluorine. Please use the "
"'win_wua' module instead."]}
mock = MagicMock(side_effect=[['Saltstack', False, 5],
['Saltstack', True, 5],
['Saltstack', True, 5],
['Saltstack', True, 5]])
with patch.object(win_update, '_search', mock):
ret.update({'comment': 'Saltstack'})
self.assertDictEqual(win_update.installed('salt'), ret)
mock = MagicMock(side_effect=[['dude', False, 5],
['dude', True, 5],
['dude', True, 5]])
with patch.object(win_update, '_download', mock):
ret.update({'comment': 'Saltstackdude'})
self.assertDictEqual(win_update.installed('salt'), ret)
mock = MagicMock(side_effect=[['@Me', False, 5],
['@Me', True, 5]])
with patch.object(win_update, '_install', mock):
ret.update({'comment': 'Saltstackdude@Me'})
self.assertDictEqual(win_update.installed('salt'), ret)
ret.update({'changes': True, 'result': True})
self.assertDictEqual(win_update.installed('salt'), ret)
def test_downloaded(self):
'''
Test to cache updates for later install.
'''
ret = {'name': 'salt',
'changes': {},
'result': False,
'comment': '',
'warnings': ["The 'win_update' module is deprecated, and will "
"be removed in Salt Fluorine. Please use the "
"'win_wua' module instead."]}
mock = MagicMock(side_effect=[['Saltstack', False, 5],
['Saltstack', True, 5],
['Saltstack', True, 5]])
with patch.object(win_update, '_search', mock):
ret.update({'comment': 'Saltstack'})
self.assertDictEqual(win_update.downloaded('salt'), ret)
mock = MagicMock(side_effect=[['dude', False, 5],
['dude', True, 5]])
with patch.object(win_update, '_download', mock):
ret.update({'comment': 'Saltstackdude'})
self.assertDictEqual(win_update.downloaded('salt'), ret)
ret.update({'changes': True, 'result': True})
self.assertDictEqual(win_update.downloaded('salt'), ret)

View File

@ -3,6 +3,7 @@
# Import python libs
from __future__ import absolute_import, print_function, unicode_literals
from collections import namedtuple
import logging
# Import Salt Libs
from salt.exceptions import SaltInvocationError
@ -19,6 +20,8 @@ from tests.support.mock import (
patch
)
log = logging.getLogger(__name__)
class ArgsTestCase(TestCase):
'''
@ -202,3 +205,63 @@ class ArgsTestCase(TestCase):
self.assertEqual(fun, 'amod.afunc')
self.assertEqual(args, ['double " single \'', 'double " single \''])
self.assertEqual(kwargs, {'kw1': 'equal=equal', 'kw2': 'val2'})
def test_yamlify_arg(self):
'''
Test that we properly yamlify CLI input. In several of the tests below
assertIs is used instead of assertEqual. This is because we want to
confirm that the return value is not a copy of the original, but the
same instance as the original.
'''
def _yamlify_arg(item):
log.debug('Testing yamlify_arg with %r', item)
return salt.utils.args.yamlify_arg(item)
# Make sure non-strings are just returned back
for item in (True, False, None, 123, 45.67, ['foo'], {'foo': 'bar'}):
self.assertIs(_yamlify_arg(item), item)
# Make sure whitespace-only isn't loaded as None
for item in ('', '\t', ' '):
self.assertIs(_yamlify_arg(item), item)
# This value would be loaded as an int (123), the underscores would be
# ignored. Test that we identify this case and return the original
# value.
item = '1_2_3'
self.assertIs(_yamlify_arg(item), item)
# The '#' is treated as a comment when not part of a data structure, we
# don't want that behavior
for item in ('# hash at beginning', 'Hello world! # hash elsewhere'):
self.assertIs(_yamlify_arg(item), item)
# However we _do_ want the # to be intact if it _is_ within a data
# structure.
item = '["foo", "bar", "###"]'
self.assertEqual(_yamlify_arg(item), ["foo", "bar", "###"])
item = '{"foo": "###"}'
self.assertEqual(_yamlify_arg(item), {"foo": "###"})
# The string "None" should load _as_ None
self.assertIs(_yamlify_arg('None'), None)
# Leading dashes, or strings containing colons, will result in lists
# and dicts, and we only want to load lists and dicts when the strings
# look like data structures.
for item in ('- foo', 'foo: bar'):
self.assertIs(_yamlify_arg(item), item)
# Make sure we don't load '|' as ''
item = '|'
self.assertIs(_yamlify_arg(item), item)
# Make sure we load ints, floats, and strings correctly
self.assertEqual(_yamlify_arg('123'), 123)
self.assertEqual(_yamlify_arg('45.67'), 45.67)
self.assertEqual(_yamlify_arg('foo'), 'foo')
# We tested list/dict loading above, but there is separate logic when
# the string contains a '#', so we need to test again here.
self.assertEqual(_yamlify_arg('["foo", "bar"]'), ["foo", "bar"])
self.assertEqual(_yamlify_arg('{"foo": "bar"}'), {"foo": "bar"})