mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Merge branch 'develop' into issue36942
This commit is contained in:
commit
78fd3f9b4d
211 changed files with 6955 additions and 2839 deletions
4
.github/stale.yml
vendored
4
.github/stale.yml
vendored
|
@ -1,8 +1,8 @@
|
|||
# Probot Stale configuration file
|
||||
|
||||
# Number of days of inactivity before an issue becomes stale
|
||||
# 1130 is approximately 3 years and 1 month
|
||||
daysUntilStale: 1130
|
||||
# 1115 is approximately 3 years and 1 month
|
||||
daysUntilStale: 1115
|
||||
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 7
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
{
|
||||
"alwaysNotifyForPaths": [
|
||||
{
|
||||
"name": "ryan-lane",
|
||||
"files": ["salt/**/*boto*.py"],
|
||||
"skipTeamPrs": false
|
||||
}
|
||||
],
|
||||
"skipTitle": "Merge forward",
|
||||
"userBlacklist": ["cvrebert", "markusgattol", "olliewalsh"]
|
||||
}
|
||||
|
|
11
conf/cloud
11
conf/cloud
|
@ -97,3 +97,14 @@
|
|||
#
|
||||
#delete_sshkeys: False
|
||||
|
||||
# Whether or not to include grains information in the /etc/salt/minion file
|
||||
# which is generated when the minion is provisioned. For example...
|
||||
# grains:
|
||||
# salt-cloud:
|
||||
# driver: ec2
|
||||
# provider: my_ec2:ec2
|
||||
# profile: micro_ec2
|
||||
#
|
||||
# Default: 'True'
|
||||
#
|
||||
#enable_cloud_grains: 'True'
|
||||
|
|
|
@ -245,8 +245,8 @@ on_saltstack = 'SALT_ON_SALTSTACK' in os.environ
|
|||
project = 'Salt'
|
||||
|
||||
version = salt.version.__version__
|
||||
latest_release = '2017.7.0' # latest release
|
||||
previous_release = '2016.11.6' # latest release from previous branch
|
||||
latest_release = '2017.7.1' # latest release
|
||||
previous_release = '2016.11.7' # latest release from previous branch
|
||||
previous_release_dir = '2016.11' # path on web server for previous branch
|
||||
next_release = '' # next release
|
||||
next_release_dir = '' # path on web server for next release branch
|
||||
|
|
|
@ -19,5 +19,4 @@ auth modules
|
|||
pki
|
||||
rest
|
||||
sharedsecret
|
||||
stormpath
|
||||
yubico
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
===================
|
||||
salt.auth.stormpath
|
||||
===================
|
||||
|
||||
.. automodule:: salt.auth.stormpath
|
||||
:members:
|
|
@ -33,6 +33,10 @@ Output Options
|
|||
|
||||
Write the output to the specified file.
|
||||
|
||||
.. option:: --out-file-append, --output-file-append
|
||||
|
||||
Append the output to the specified file.
|
||||
|
||||
.. option:: --no-color
|
||||
|
||||
Disable all colored output
|
||||
|
@ -46,3 +50,14 @@ Output Options
|
|||
|
||||
``green`` denotes success, ``red`` denotes failure, ``blue`` denotes
|
||||
changes and success and ``yellow`` denotes a expected future change in configuration.
|
||||
|
||||
.. option:: --state-output=STATE_OUTPUT, --state_output=STATE_OUTPUT
|
||||
|
||||
Override the configured state_output value for minion
|
||||
output. One of 'full', 'terse', 'mixed', 'changes' or
|
||||
'filter'. Default: 'none'.
|
||||
|
||||
.. option:: --state-verbose=STATE_VERBOSE, --state_verbose=STATE_VERBOSE
|
||||
|
||||
Override the configured state_verbose value for minion
|
||||
output. Set to True or False. Default: none.
|
||||
|
|
|
@ -81,7 +81,7 @@ Options
|
|||
|
||||
Pass in an external authentication medium to validate against. The
|
||||
credentials will be prompted for. The options are `auto`,
|
||||
`keystone`, `ldap`, `pam`, and `stormpath`. Can be used with the -T
|
||||
`keystone`, `ldap`, and `pam`. Can be used with the -T
|
||||
option.
|
||||
|
||||
.. option:: -T, --make-token
|
||||
|
|
6
doc/ref/clouds/all/salt.cloud.clouds.oneandone.rst
Normal file
6
doc/ref/clouds/all/salt.cloud.clouds.oneandone.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
===========================
|
||||
salt.cloud.clouds.oneandone
|
||||
===========================
|
||||
|
||||
.. automodule:: salt.cloud.clouds.oneandone
|
||||
:members:
|
|
@ -97,6 +97,7 @@ execution modules
|
|||
cytest
|
||||
daemontools
|
||||
data
|
||||
datadog_api
|
||||
ddns
|
||||
deb_apache
|
||||
deb_postgres
|
||||
|
@ -399,7 +400,6 @@ execution modules
|
|||
state
|
||||
status
|
||||
statuspage
|
||||
stormpath
|
||||
supervisord
|
||||
suse_apache
|
||||
svn
|
||||
|
|
6
doc/ref/modules/all/salt.modules.datadog_api.rst
Normal file
6
doc/ref/modules/all/salt.modules.datadog_api.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
========================
|
||||
salt.modules.datadog_api
|
||||
========================
|
||||
|
||||
.. automodule:: salt.modules.datadog_api
|
||||
:members:
|
|
@ -1,6 +0,0 @@
|
|||
======================
|
||||
salt.modules.stormpath
|
||||
======================
|
||||
|
||||
.. automodule:: salt.modules.stormpath
|
||||
:members:
|
|
@ -250,7 +250,6 @@ state modules
|
|||
stateconf
|
||||
status
|
||||
statuspage
|
||||
stormpath_account
|
||||
supervisord
|
||||
svn
|
||||
sysctl
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
=============================
|
||||
salt.states.stormpath_account
|
||||
=============================
|
||||
|
||||
.. automodule:: salt.states.stormpath_account
|
||||
:members:
|
|
@ -135,19 +135,23 @@ A State Module must return a dict containing the following keys/values:
|
|||
``test=True``, and changes would have been made if the state was not run in
|
||||
test mode.
|
||||
|
||||
+--------------------+-----------+-----------+
|
||||
| | live mode | test mode |
|
||||
+====================+===========+===========+
|
||||
| no changes | ``True`` | ``True`` |
|
||||
+--------------------+-----------+-----------+
|
||||
| successful changes | ``True`` | ``None`` |
|
||||
+--------------------+-----------+-----------+
|
||||
| failed changes | ``False`` | ``None`` |
|
||||
+--------------------+-----------+-----------+
|
||||
+--------------------+-----------+------------------------+
|
||||
| | live mode | test mode |
|
||||
+====================+===========+========================+
|
||||
| no changes | ``True`` | ``True`` |
|
||||
+--------------------+-----------+------------------------+
|
||||
| successful changes | ``True`` | ``None`` |
|
||||
+--------------------+-----------+------------------------+
|
||||
| failed changes | ``False`` | ``False`` or ``None`` |
|
||||
+--------------------+-----------+------------------------+
|
||||
|
||||
.. note::
|
||||
|
||||
Test mode does not predict if the changes will be successful or not.
|
||||
Test mode does not predict if the changes will be successful or not,
|
||||
and hence the result for pending changes is usually ``None``.
|
||||
|
||||
However, if a state is going to fail and this can be determined
|
||||
in test mode without applying the change, ``False`` can be returned.
|
||||
|
||||
- **comment:** A string containing a summary of the result.
|
||||
|
||||
|
|
|
@ -777,8 +777,6 @@ Stateconf
|
|||
stderr
|
||||
stdin
|
||||
stdout
|
||||
stormpath
|
||||
Stormpath
|
||||
str
|
||||
strftime
|
||||
subfolder
|
||||
|
|
|
@ -56,6 +56,24 @@ settings can be placed in the provider or profile:
|
|||
sls_list:
|
||||
- web
|
||||
|
||||
|
||||
When salt cloud creates a new minon, it can automatically add grain information
|
||||
to the minion configuration file identifying the sources originally used
|
||||
to define it.
|
||||
|
||||
The generated grain information will appear similar to:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
grains:
|
||||
salt-cloud:
|
||||
driver: ec2
|
||||
provider: my_ec2:ec2
|
||||
profile: ec2-web
|
||||
|
||||
The generation of the salt-cloud grain can be surpressed by the
|
||||
option ``enable_cloud_grains: 'False'`` in the cloud configuration file.
|
||||
|
||||
Cloud Configuration Syntax
|
||||
==========================
|
||||
|
||||
|
|
|
@ -119,6 +119,7 @@ Cloud Provider Specifics
|
|||
Getting Started With Libvirt <libvirt>
|
||||
Getting Started With Linode <linode>
|
||||
Getting Started With LXC <lxc>
|
||||
Getting Started With OneAndOne <oneandone>
|
||||
Getting Started With OpenNebula <opennebula>
|
||||
Getting Started With OpenStack <openstack>
|
||||
Getting Started With Parallels <parallels>
|
||||
|
|
|
@ -406,4 +406,22 @@ configuration file. For example:
|
|||
- whoami
|
||||
- echo 'hello world!'
|
||||
|
||||
These commands will run in sequence **before** the bootstrap script is executed.
|
||||
These commands will run in sequence **before** the bootstrap script is executed.
|
||||
|
||||
Force Minion Config
|
||||
===================
|
||||
|
||||
.. versionadded:: Oxygen
|
||||
|
||||
The ``force_minion_config`` option requests the bootstrap process to overwrite
|
||||
an existing minion configuration file and public/private key files.
|
||||
Default: False
|
||||
|
||||
This might be important for drivers (such as ``saltify``) which are expected to
|
||||
take over a connection from a former salt master.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
my_saltify_provider:
|
||||
driver: saltify
|
||||
force_minion_config: true
|
||||
|
|
146
doc/topics/cloud/oneandone.rst
Normal file
146
doc/topics/cloud/oneandone.rst
Normal file
|
@ -0,0 +1,146 @@
|
|||
==========================
|
||||
Getting Started With 1and1
|
||||
==========================
|
||||
|
||||
1&1 is one of the world’s leading Web hosting providers. 1&1 currently offers
|
||||
a wide range of Web hosting products, including email solutions and high-end
|
||||
servers in 10 different countries including Germany, Spain, Great Britain
|
||||
and the United States. From domains to 1&1 MyWebsite to eBusiness solutions
|
||||
like Cloud Hosting and Web servers for complex tasks, 1&1 is well placed to deliver
|
||||
a high quality service to its customers. All 1&1 products are hosted in
|
||||
1&1‘s high-performance, green data centers in the USA and Europe.
|
||||
|
||||
Dependencies
|
||||
============
|
||||
|
||||
* 1and1 >= 1.2.0
|
||||
|
||||
Configuration
|
||||
=============
|
||||
|
||||
* Using the new format, set up the cloud configuration at
|
||||
``/etc/salt/cloud.providers`` or
|
||||
``/etc/salt/cloud.providers.d/oneandone.conf``:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
my-oneandone-config:
|
||||
driver: oneandone
|
||||
|
||||
# Set the location of the salt-master
|
||||
#
|
||||
minion:
|
||||
master: saltmaster.example.com
|
||||
|
||||
# Configure oneandone authentication credentials
|
||||
#
|
||||
api_token: <api_token>
|
||||
ssh_private_key: /path/to/id_rsa
|
||||
ssh_public_key: /path/to/id_rsa.pub
|
||||
|
||||
Authentication
|
||||
==============
|
||||
|
||||
The ``api_key`` is used for API authorization. This token can be obtained
|
||||
from the CloudPanel in the Management section below Users.
|
||||
|
||||
Profiles
|
||||
========
|
||||
|
||||
Here is an example of a profile:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
oneandone_fixed_size:
|
||||
provider: my-oneandone-config
|
||||
description: Small instance size server
|
||||
fixed_instance_size: S
|
||||
appliance_id: 8E3BAA98E3DFD37857810E0288DD8FBA
|
||||
|
||||
oneandone_custom_size:
|
||||
provider: my-oneandone-config
|
||||
description: Custom size server
|
||||
vcore: 2
|
||||
cores_per_processor: 2
|
||||
ram: 8
|
||||
appliance_id: 8E3BAA98E3DFD37857810E0288DD8FBA
|
||||
hdds:
|
||||
-
|
||||
is_main: true
|
||||
size: 20
|
||||
-
|
||||
is_main: false
|
||||
size: 20
|
||||
|
||||
The following list explains some of the important properties.
|
||||
|
||||
fixed_instance_size_id
|
||||
When creating a server, either ``fixed_instance_size_id`` or custom hardware params
|
||||
containing ``vcore``, ``cores_per_processor``, ``ram``, and ``hdds`` must be provided.
|
||||
Can be one of the IDs listed among the output of the following command:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-cloud --list-sizes oneandone
|
||||
|
||||
vcore
|
||||
Total amount of processors.
|
||||
|
||||
cores_per_processor
|
||||
Number of cores per processor.
|
||||
|
||||
ram
|
||||
RAM memory size in GB.
|
||||
|
||||
hdds
|
||||
Hard disks.
|
||||
|
||||
appliance_id
|
||||
ID of the image that will be installed on server.
|
||||
Can be one of the IDs listed in the output of the following command:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-cloud --list-images oneandone
|
||||
|
||||
datacenter_id
|
||||
ID of the datacenter where the server will be created.
|
||||
Can be one of the IDs listed in the output of the following command:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-cloud --list-locations oneandone
|
||||
|
||||
description
|
||||
Description of the server.
|
||||
|
||||
password
|
||||
Password of the server. Password must contain more than 8 characters
|
||||
using uppercase letters, numbers and other special symbols.
|
||||
|
||||
power_on
|
||||
Power on server after creation. Default is set to true.
|
||||
|
||||
firewall_policy_id
|
||||
Firewall policy ID. If it is not provided, the server will assign
|
||||
the best firewall policy, creating a new one if necessary. If the parameter
|
||||
is sent with a 0 value, the server will be created with all ports blocked.
|
||||
|
||||
ip_id
|
||||
IP address ID.
|
||||
|
||||
load_balancer_id
|
||||
Load balancer ID.
|
||||
|
||||
monitoring_policy_id
|
||||
Monitoring policy ID.
|
||||
|
||||
deploy
|
||||
Set to False if Salt should not be installed on the node.
|
||||
|
||||
wait_for_timeout
|
||||
The timeout to wait in seconds for provisioning resources such as servers.
|
||||
The default wait_for_timeout is 15 minutes.
|
||||
|
||||
For more information concerning cloud profiles, see :ref:`here
|
||||
<salt-cloud-profiles>`.
|
|
@ -16,7 +16,7 @@ The Saltify driver has no external dependencies.
|
|||
Configuration
|
||||
=============
|
||||
|
||||
Because the Saltify driver does not use an actual cloud provider host, it has a
|
||||
Because the Saltify driver does not use an actual cloud provider host, it can have a
|
||||
simple provider configuration. The only thing that is required to be set is the
|
||||
driver name, and any other potentially useful information, like the location of
|
||||
the salt-master:
|
||||
|
@ -31,6 +31,12 @@ the salt-master:
|
|||
master: 111.222.333.444
|
||||
provider: saltify
|
||||
|
||||
However, if you wish to use the more advanced capabilities of salt-cloud, such as
|
||||
rebooting, listing, and disconnecting machines, then the salt master must fill
|
||||
the role usually performed by a vendor's cloud management system. In order to do
|
||||
that, you must configure your salt master as a salt-api server, and supply credentials
|
||||
to use it. (See ``salt-api setup`` below.)
|
||||
|
||||
|
||||
Profiles
|
||||
========
|
||||
|
@ -72,6 +78,30 @@ to it can be verified with Salt:
|
|||
salt my-machine test.ping
|
||||
|
||||
|
||||
Destroy Options
|
||||
---------------
|
||||
|
||||
For obvious reasons, the ``destroy`` action does not actually vaporize hardware.
|
||||
If the salt master is connected using salt-api, it can tear down parts of
|
||||
the client machines. It will remove the client's key from the salt master,
|
||||
and will attempt the following options:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
- remove_config_on_destroy: true
|
||||
# default: true
|
||||
# Deactivate salt-minion on reboot and
|
||||
# delete the minion config and key files from its ``/etc/salt`` directory,
|
||||
# NOTE: If deactivation is unsuccessful (older Ubuntu machines) then when
|
||||
# salt-minion restarts it will automatically create a new, unwanted, set
|
||||
# of key files. The ``force_minion_config`` option must be used in that case.
|
||||
|
||||
- shutdown_on_destroy: false
|
||||
# default: false
|
||||
# send a ``shutdown`` command to the client.
|
||||
|
||||
.. versionadded:: Oxygen
|
||||
|
||||
Using Map Files
|
||||
---------------
|
||||
The settings explained in the section above may also be set in a map file. An
|
||||
|
@ -135,3 +165,67 @@ Return values:
|
|||
- ``True``: Credential verification succeeded
|
||||
- ``False``: Credential verification succeeded
|
||||
- ``None``: Credential verification was not attempted.
|
||||
|
||||
Provisioning salt-api
|
||||
=====================
|
||||
|
||||
In order to query or control minions it created, saltify needs to send commands
|
||||
to the salt master. It does that using the network interface to salt-api.
|
||||
|
||||
The salt-api is not enabled by default. The following example will provide a
|
||||
simple installation.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
# file /etc/salt/cloud.profiles.d/my_saltify_profiles.conf
|
||||
hw_41: # a theoretical example hardware machine
|
||||
ssh_host: 10.100.9.41 # the hard address of your target
|
||||
ssh_username: vagrant # a user name which has passwordless sudo
|
||||
password: vagrant # on your target machine
|
||||
provider: my_saltify_provider
|
||||
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
# file /etc/salt/cloud.providers.d/saltify_provider.conf
|
||||
my_saltify_provider:
|
||||
driver: saltify
|
||||
eauth: pam
|
||||
username: vagrant # supply some sudo-group-member's name
|
||||
password: vagrant # and password on the salt master
|
||||
minion:
|
||||
master: 10.100.9.5 # the hard address of the master
|
||||
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
# file /etc/salt/master.d/auth.conf
|
||||
# using salt-api ... members of the 'sudo' group can do anything ...
|
||||
external_auth:
|
||||
pam:
|
||||
sudo%:
|
||||
- .*
|
||||
- '@wheel'
|
||||
- '@runner'
|
||||
- '@jobs'
|
||||
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
# file /etc/salt/master.d/api.conf
|
||||
# see https://docs.saltstack.com/en/latest/ref/netapi/all/salt.netapi.rest_cherrypy.html
|
||||
rest_cherrypy:
|
||||
host: localhost
|
||||
port: 8000
|
||||
ssl_crt: /etc/pki/tls/certs/localhost.crt
|
||||
ssl_key: /etc/pki/tls/certs/localhost.key
|
||||
thread_pool: 30
|
||||
socket_queue_size: 10
|
||||
|
||||
|
||||
Start your target machine as a Salt minion named "node41" by:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ sudo salt-cloud -p hw_41 node41
|
||||
|
||||
|
|
|
@ -18,10 +18,10 @@ on the significance and complexity of the changes required by the user.
|
|||
|
||||
Salt feature releases are based on the Periodic Table. Any new features going
|
||||
into the develop branch will be named after the next element in the Periodic
|
||||
Table. For example, Beryllium was the feature release name of the develop branch
|
||||
before the 2015.8 branch was tagged. At that point in time, any new features going
|
||||
into the develop branch after 2015.8 was branched were part of the Boron feature
|
||||
release.
|
||||
Table. For example, Beryllium was the feature release name of the develop
|
||||
branch before the 2015.8 branch was tagged. At that point in time, any new
|
||||
features going into the develop branch after 2015.8 was branched were part of
|
||||
the Boron feature release.
|
||||
|
||||
A deprecation warning should be in place for at least two major releases before
|
||||
the deprecated code and its accompanying deprecation warning are removed. More
|
||||
|
@ -29,14 +29,14 @@ time should be given for more complex changes. For example, if the current
|
|||
release under development is ``Sodium``, the deprecated code and associated
|
||||
warnings should remain in place and warn for at least ``Aluminum``.
|
||||
|
||||
To help in this deprecation task, salt provides :func:`salt.utils.warn_until
|
||||
<salt.utils.warn_until>`. The idea behind this helper function is to show the
|
||||
deprecation warning to the user until salt reaches the provided version. Once
|
||||
that provided version is equaled :func:`salt.utils.warn_until
|
||||
<salt.utils.warn_until>` will raise a :py:exc:`RuntimeError` making salt stop
|
||||
its execution. This stoppage is unpleasant and will remind the developer that
|
||||
the deprecation limit has been reached and that the code can then be safely
|
||||
removed.
|
||||
To help in this deprecation task, salt provides
|
||||
:func:`salt.utils.versions.warn_until <salt.utils.versions.warn_until>`. The
|
||||
idea behind this helper function is to show the deprecation warning to the user
|
||||
until salt reaches the provided version. Once that provided version is equaled
|
||||
:func:`salt.utils.versions.warn_until <salt.utils.versions.warn_until>` will
|
||||
raise a :py:exc:`RuntimeError` making salt stop its execution. This stoppage is
|
||||
unpleasant and will remind the developer that the deprecation limit has been
|
||||
reached and that the code can then be safely removed.
|
||||
|
||||
Consider the following example:
|
||||
|
||||
|
@ -44,7 +44,7 @@ Consider the following example:
|
|||
|
||||
def some_function(bar=False, foo=None):
|
||||
if foo is not None:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Aluminum',
|
||||
'The \'foo\' argument has been deprecated and its '
|
||||
'functionality removed, as such, its usage is no longer '
|
||||
|
|
|
@ -319,7 +319,7 @@ function into ``__salt__`` that's actually a MagicMock instance.
|
|||
|
||||
def show_patch(self):
|
||||
with patch.dict(my_module.__salt__,
|
||||
{'function.to_replace': MagicMock()}:
|
||||
{'function.to_replace': MagicMock()}):
|
||||
# From this scope, carry on with testing, with a modified __salt__!
|
||||
|
||||
|
||||
|
|
154
doc/topics/installation/eos.rst
Normal file
154
doc/topics/installation/eos.rst
Normal file
|
@ -0,0 +1,154 @@
|
|||
=========================================
|
||||
Arista EOS Salt minion installation guide
|
||||
=========================================
|
||||
|
||||
The Salt minion for Arista EOS is distributed as a SWIX extension and can be installed directly on the switch. The EOS network operating system is based on old Fedora distributions and the installation of the ``salt-minion`` requires backports. This SWIX extension contains the necessary backports, together with the Salt basecode.
|
||||
|
||||
.. note::
|
||||
|
||||
This SWIX extension has been tested on Arista DCS-7280SE-68-R, running EOS 4.17.5M and vEOS 4.18.3F.
|
||||
|
||||
Important Notes
|
||||
===============
|
||||
|
||||
This package is in beta, make sure to test it carefully before running it in production.
|
||||
|
||||
If confirmed working correctly, please report and add a note on this page with the platform model and EOS version.
|
||||
|
||||
If you want to uninstall this package, please refer to the uninstalling_ section.
|
||||
|
||||
Installation from the Official SaltStack Repository
|
||||
===================================================
|
||||
|
||||
Download the swix package and save it to flash.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
veos#copy https://salt-eos.netops.life/salt-eos-latest.swix flash:
|
||||
veos#copy https://salt-eos.netops.life/startup.sh flash:
|
||||
|
||||
Install the Extension
|
||||
=====================
|
||||
|
||||
Copy the Salt package to extension
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
veos#copy flash:salt-eos-latest.swix extension:
|
||||
|
||||
Install the SWIX
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
veos#extension salt-eos-latest.swix force
|
||||
|
||||
Verify the installation
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
veos#show extensions | include salt-eos
|
||||
salt-eos-2017-07-19.swix 1.0.11/1.fc25 A, F 27
|
||||
|
||||
Change the Salt master IP address or FQDN, by edit the variable (SALT_MASTER)
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
veos#bash vi /mnt/flash/startup.sh
|
||||
|
||||
Make sure you enable the eAPI with unix-socket
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
veos(config)#management api http-commands
|
||||
protocol unix-socket
|
||||
no shutdown
|
||||
|
||||
Post-installation tasks
|
||||
=======================
|
||||
|
||||
Generate Keys and host record and start Salt minion
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
veos#bash
|
||||
#sudo /mnt/flash/startup.sh
|
||||
|
||||
``salt-minion`` should be running
|
||||
|
||||
Copy the installed extensions to boot-extensions
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
veos#copy installed-extensions boot-extensions
|
||||
|
||||
Apply event-handler to let EOS start salt-minion during boot-up
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
veos(config)#event-handler boot-up-script
|
||||
trigger on-boot
|
||||
action bash sudo /mnt/flash/startup.sh
|
||||
|
||||
For more specific installation details of the ``salt-minion``, please refer to :ref:`Configuring Salt<configuring-salt>`.
|
||||
|
||||
.. _uninstalling:
|
||||
|
||||
Uninstalling
|
||||
============
|
||||
|
||||
If you decide to uninstall this package, the following steps are recommended for safety:
|
||||
|
||||
1. Remove the extension from boot-extensions
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
veos#bash rm /mnt/flash/boot-extensions
|
||||
|
||||
2. Remove the extension from extensions folder
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
veos#bash rm /mnt/flash/.extensions/salt-eos-latest.swix
|
||||
|
||||
2. Remove boot-up script
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
veos(config)#no event-handler boot-up-script
|
||||
|
||||
Additional Information
|
||||
======================
|
||||
|
||||
This SWIX extension contains the following RPM packages:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
libsodium-1.0.11-1.fc25.i686.rpm
|
||||
libstdc++-6.2.1-2.fc25.i686.rpm
|
||||
openpgm-5.2.122-6.fc24.i686.rpm
|
||||
python-Jinja2-2.8-0.i686.rpm
|
||||
python-PyYAML-3.12-0.i686.rpm
|
||||
python-babel-0.9.6-5.fc18.noarch.rpm
|
||||
python-backports-1.0-3.fc18.i686.rpm
|
||||
python-backports-ssl_match_hostname-3.4.0.2-1.fc18.noarch.rpm
|
||||
python-backports_abc-0.5-0.i686.rpm
|
||||
python-certifi-2016.9.26-0.i686.rpm
|
||||
python-chardet-2.0.1-5.fc18.noarch.rpm
|
||||
python-crypto-1.4.1-1.noarch.rpm
|
||||
python-crypto-2.6.1-1.fc18.i686.rpm
|
||||
python-futures-3.1.1-1.noarch.rpm
|
||||
python-jtextfsm-0.3.1-0.noarch.rpm
|
||||
python-kitchen-1.1.1-2.fc18.noarch.rpm
|
||||
python-markupsafe-0.18-1.fc18.i686.rpm
|
||||
python-msgpack-python-0.4.8-0.i686.rpm
|
||||
python-napalm-base-0.24.3-1.noarch.rpm
|
||||
python-napalm-eos-0.6.0-1.noarch.rpm
|
||||
python-netaddr-0.7.18-0.noarch.rpm
|
||||
python-pyeapi-0.7.0-0.noarch.rpm
|
||||
python-salt-2017.7.0_1414_g2fb986f-1.noarch.rpm
|
||||
python-singledispatch-3.4.0.3-0.i686.rpm
|
||||
python-six-1.10.0-0.i686.rpm
|
||||
python-tornado-4.4.2-0.i686.rpm
|
||||
python-urllib3-1.5-7.fc18.noarch.rpm
|
||||
python2-zmq-15.3.0-2.fc25.i686.rpm
|
||||
zeromq-4.1.4-5.fc25.i686.rpm
|
|
@ -46,6 +46,7 @@ These guides go into detail how to install Salt on a given platform.
|
|||
|
||||
arch
|
||||
debian
|
||||
eos
|
||||
fedora
|
||||
freebsd
|
||||
gentoo
|
||||
|
|
|
@ -38,6 +38,14 @@ In previous releases the bind credentials would only be used to determine
|
|||
the LDAP user's existence and group membership. The user's LDAP credentials
|
||||
were used from then on.
|
||||
|
||||
Stormpath External Authentication Removed
|
||||
-----------------------------------------
|
||||
|
||||
Per Stormpath's announcement, their API will be shutting down on 8/17/2017 at
|
||||
noon PST so the Stormpath external authentication module has been removed.
|
||||
|
||||
https://stormpath.com/oktaplusstormpath
|
||||
|
||||
New GitFS Features
|
||||
------------------
|
||||
|
||||
|
@ -98,6 +106,497 @@ running on T-Series SPARC hardware. The ``virtual_subtype`` grain is
|
|||
populated as a list of domain roles.
|
||||
|
||||
|
||||
Beacon configuration changes
|
||||
----------------------------------------
|
||||
|
||||
In order to remain consistent and to align with other Salt components such as states,
|
||||
support for configuring beacons using dictionary based configuration has been deprecated
|
||||
in favor of list based configuration. All beacons have a validation function which will
|
||||
check the configuration for the correct format and only load if the validation passes.
|
||||
|
||||
- ``avahi_announce`` beacon
|
||||
|
||||
Old behavior:
|
||||
```
|
||||
beacons:
|
||||
avahi_announce:
|
||||
run_once: True
|
||||
servicetype: _demo._tcp
|
||||
port: 1234
|
||||
txt:
|
||||
ProdName: grains.productname
|
||||
SerialNo: grains.serialnumber
|
||||
Comments: 'this is a test'
|
||||
```
|
||||
|
||||
New behavior:
|
||||
```
|
||||
beacons:
|
||||
avahi_announce:
|
||||
- run_once: True
|
||||
- servicetype: _demo._tcp
|
||||
- port: 1234
|
||||
- txt:
|
||||
ProdName: grains.productname
|
||||
SerialNo: grains.serialnumber
|
||||
Comments: 'this is a test'
|
||||
```
|
||||
|
||||
- ``bonjour_announce`` beacon
|
||||
|
||||
Old behavior:
|
||||
```
|
||||
beacons:
|
||||
bonjour_announce:
|
||||
run_once: True
|
||||
servicetype: _demo._tcp
|
||||
port: 1234
|
||||
txt:
|
||||
ProdName: grains.productname
|
||||
SerialNo: grains.serialnumber
|
||||
Comments: 'this is a test'
|
||||
```
|
||||
|
||||
New behavior:
|
||||
```
|
||||
beacons:
|
||||
bonjour_announce:
|
||||
- run_once: True
|
||||
- servicetype: _demo._tcp
|
||||
- port: 1234
|
||||
- txt:
|
||||
ProdName: grains.productname
|
||||
SerialNo: grains.serialnumber
|
||||
Comments: 'this is a test'
|
||||
```
|
||||
|
||||
- ``btmp`` beacon
|
||||
|
||||
Old behavior:
|
||||
```
|
||||
beacons:
|
||||
btmp: {}
|
||||
```
|
||||
|
||||
New behavior:
|
||||
```
|
||||
beacons:
|
||||
btmp: []
|
||||
|
||||
```
|
||||
|
||||
- ``glxinfo`` beacon
|
||||
|
||||
Old behavior:
|
||||
```
|
||||
beacons:
|
||||
glxinfo:
|
||||
user: frank
|
||||
screen_event: True
|
||||
```
|
||||
|
||||
New behavior:
|
||||
```
|
||||
beacons:
|
||||
glxinfo:
|
||||
- user: frank
|
||||
- screen_event: True
|
||||
```
|
||||
|
||||
- ``haproxy`` beacon
|
||||
|
||||
Old behavior:
|
||||
```
|
||||
beacons:
|
||||
haproxy:
|
||||
- www-backend:
|
||||
threshold: 45
|
||||
servers:
|
||||
- web1
|
||||
- web2
|
||||
- interval: 120
|
||||
```
|
||||
|
||||
New behavior:
|
||||
```
|
||||
beacons:
|
||||
haproxy:
|
||||
- backends:
|
||||
www-backend:
|
||||
threshold: 45
|
||||
servers:
|
||||
- web1
|
||||
- web2
|
||||
- interval: 120
|
||||
```
|
||||
|
||||
- ``inotify`` beacon
|
||||
|
||||
Old behavior:
|
||||
```
|
||||
beacons:
|
||||
inotify:
|
||||
/path/to/file/or/dir:
|
||||
mask:
|
||||
- open
|
||||
- create
|
||||
- close_write
|
||||
recurse: True
|
||||
auto_add: True
|
||||
exclude:
|
||||
- /path/to/file/or/dir/exclude1
|
||||
- /path/to/file/or/dir/exclude2
|
||||
- /path/to/file/or/dir/regex[a-m]*$:
|
||||
regex: True
|
||||
coalesce: True
|
||||
```
|
||||
|
||||
New behavior:
|
||||
```
|
||||
beacons:
|
||||
inotify:
|
||||
- files:
|
||||
/path/to/file/or/dir:
|
||||
mask:
|
||||
- open
|
||||
- create
|
||||
- close_write
|
||||
recurse: True
|
||||
auto_add: True
|
||||
exclude:
|
||||
- /path/to/file/or/dir/exclude1
|
||||
- /path/to/file/or/dir/exclude2
|
||||
- /path/to/file/or/dir/regex[a-m]*$:
|
||||
regex: True
|
||||
- coalesce: True
|
||||
```
|
||||
|
||||
- ``journald`` beacon
|
||||
|
||||
Old behavior:
|
||||
```
|
||||
beacons:
|
||||
journald:
|
||||
sshd:
|
||||
SYSLOG_IDENTIFIER: sshd
|
||||
PRIORITY: 6
|
||||
```
|
||||
|
||||
New behavior:
|
||||
```
|
||||
beacons:
|
||||
journald:
|
||||
- services:
|
||||
sshd:
|
||||
SYSLOG_IDENTIFIER: sshd
|
||||
PRIORITY: 6
|
||||
```
|
||||
|
||||
- ``load`` beacon
|
||||
|
||||
Old behavior:
|
||||
```
|
||||
beacons:
|
||||
load:
|
||||
1m:
|
||||
- 0.0
|
||||
- 2.0
|
||||
5m:
|
||||
- 0.0
|
||||
- 1.5
|
||||
15m:
|
||||
- 0.1
|
||||
- 1.0
|
||||
emitatstartup: True
|
||||
onchangeonly: False
|
||||
```
|
||||
|
||||
New behavior:
|
||||
```
|
||||
beacons:
|
||||
load:
|
||||
- averages:
|
||||
1m:
|
||||
- 0.0
|
||||
- 2.0
|
||||
5m:
|
||||
- 0.0
|
||||
- 1.5
|
||||
15m:
|
||||
- 0.1
|
||||
- 1.0
|
||||
- emitatstartup: True
|
||||
- onchangeonly: False
|
||||
```
|
||||
|
||||
- ``log`` beacon
|
||||
|
||||
Old behavior:
|
||||
```
|
||||
beacons:
|
||||
log:
|
||||
file: <path>
|
||||
<tag>:
|
||||
regex: <pattern>
|
||||
```
|
||||
|
||||
New behavior:
|
||||
```
|
||||
beacons:
|
||||
log:
|
||||
- file: <path>
|
||||
- tags:
|
||||
<tag>:
|
||||
regex: <pattern>
|
||||
```
|
||||
|
||||
- ``network_info`` beacon
|
||||
|
||||
Old behavior:
|
||||
```
|
||||
beacons:
|
||||
network_info:
|
||||
- eth0:
|
||||
type: equal
|
||||
bytes_sent: 100000
|
||||
bytes_recv: 100000
|
||||
packets_sent: 100000
|
||||
packets_recv: 100000
|
||||
errin: 100
|
||||
errout: 100
|
||||
dropin: 100
|
||||
dropout: 100
|
||||
```
|
||||
|
||||
New behavior:
|
||||
```
|
||||
beacons:
|
||||
network_info:
|
||||
- interfaces:
|
||||
eth0:
|
||||
type: equal
|
||||
bytes_sent: 100000
|
||||
bytes_recv: 100000
|
||||
packets_sent: 100000
|
||||
packets_recv: 100000
|
||||
errin: 100
|
||||
errout: 100
|
||||
dropin: 100
|
||||
dropout: 100
|
||||
```
|
||||
|
||||
- ``network_settings`` beacon
|
||||
|
||||
Old behavior:
|
||||
```
|
||||
beacons:
|
||||
network_settings:
|
||||
eth0:
|
||||
ipaddr:
|
||||
promiscuity:
|
||||
onvalue: 1
|
||||
eth1:
|
||||
linkmode:
|
||||
```
|
||||
|
||||
New behavior:
|
||||
```
|
||||
beacons:
|
||||
network_settings:
|
||||
- interfaces:
|
||||
- eth0:
|
||||
ipaddr:
|
||||
promiscuity:
|
||||
onvalue: 1
|
||||
- eth1:
|
||||
linkmode:
|
||||
```
|
||||
|
||||
- ``proxy_example`` beacon
|
||||
|
||||
Old behavior:
|
||||
```
|
||||
beacons:
|
||||
proxy_example:
|
||||
endpoint: beacon
|
||||
```
|
||||
|
||||
New behavior:
|
||||
```
|
||||
beacons:
|
||||
proxy_example:
|
||||
- endpoint: beacon
|
||||
```
|
||||
|
||||
- ``ps`` beacon
|
||||
|
||||
Old behavior:
|
||||
```
|
||||
beacons:
|
||||
ps:
|
||||
- salt-master: running
|
||||
- mysql: stopped
|
||||
```
|
||||
|
||||
New behavior:
|
||||
```
|
||||
beacons:
|
||||
ps:
|
||||
- processes:
|
||||
salt-master: running
|
||||
mysql: stopped
|
||||
```
|
||||
|
||||
- ``salt_proxy`` beacon
|
||||
|
||||
Old behavior:
|
||||
```
|
||||
beacons:
|
||||
salt_proxy:
|
||||
- p8000: {}
|
||||
- p8001: {}
|
||||
```
|
||||
|
||||
New behavior:
|
||||
```
|
||||
beacons:
|
||||
salt_proxy:
|
||||
- proxies:
|
||||
p8000: {}
|
||||
p8001: {}
|
||||
```
|
||||
|
||||
- ``sensehat`` beacon
|
||||
|
||||
Old behavior:
|
||||
```
|
||||
beacons:
|
||||
sensehat:
|
||||
humidity: 70%
|
||||
temperature: [20, 40]
|
||||
temperature_from_pressure: 40
|
||||
pressure: 1500
|
||||
```
|
||||
|
||||
New behavior:
|
||||
```
|
||||
beacons:
|
||||
sensehat:
|
||||
- sensors:
|
||||
humidity: 70%
|
||||
temperature: [20, 40]
|
||||
temperature_from_pressure: 40
|
||||
pressure: 1500
|
||||
```
|
||||
|
||||
- ``service`` beacon
|
||||
|
||||
Old behavior:
|
||||
```
|
||||
beacons:
|
||||
service:
|
||||
salt-master:
|
||||
mysql:
|
||||
|
||||
```
|
||||
|
||||
New behavior:
|
||||
```
|
||||
beacons:
|
||||
service:
|
||||
- services:
|
||||
nginx:
|
||||
onchangeonly: True
|
||||
delay: 30
|
||||
uncleanshutdown: /run/nginx.pid
|
||||
```
|
||||
|
||||
- ``sh`` beacon
|
||||
|
||||
Old behavior:
|
||||
```
|
||||
beacons:
|
||||
sh: {}
|
||||
```
|
||||
|
||||
New behavior:
|
||||
```
|
||||
beacons:
|
||||
sh: []
|
||||
```
|
||||
|
||||
- ``status`` beacon
|
||||
|
||||
Old behavior:
|
||||
```
|
||||
beacons:
|
||||
status: {}
|
||||
```
|
||||
|
||||
New behavior:
|
||||
```
|
||||
beacons:
|
||||
status: []
|
||||
```
|
||||
|
||||
- ``telegram_bot_msg`` beacon
|
||||
|
||||
Old behavior:
|
||||
```
|
||||
beacons:
|
||||
telegram_bot_msg:
|
||||
token: "<bot access token>"
|
||||
accept_from:
|
||||
- "<valid username>"
|
||||
interval: 10
|
||||
```
|
||||
|
||||
New behavior:
|
||||
```
|
||||
beacons:
|
||||
telegram_bot_msg:
|
||||
- token: "<bot access token>"
|
||||
- accept_from:
|
||||
- "<valid username>"
|
||||
- interval: 10
|
||||
```
|
||||
|
||||
- ``twilio_txt_msg`` beacon
|
||||
|
||||
Old behavior:
|
||||
```
|
||||
beacons:
|
||||
twilio_txt_msg:
|
||||
account_sid: "<account sid>"
|
||||
auth_token: "<auth token>"
|
||||
twilio_number: "+15555555555"
|
||||
interval: 10
|
||||
```
|
||||
|
||||
New behavior:
|
||||
```
|
||||
beacons:
|
||||
twilio_txt_msg:
|
||||
- account_sid: "<account sid>"
|
||||
- auth_token: "<auth token>"
|
||||
- twilio_number: "+15555555555"
|
||||
- interval: 10
|
||||
```
|
||||
|
||||
- ``wtmp`` beacon
|
||||
|
||||
Old behavior:
|
||||
```
|
||||
beacons:
|
||||
wtmp: {}
|
||||
```
|
||||
|
||||
New behavior:
|
||||
```
|
||||
beacons:
|
||||
wtmp: []
|
||||
```
|
||||
|
||||
Deprecations
|
||||
------------
|
||||
|
||||
|
@ -192,6 +691,18 @@ For ``smartos`` some grains have been deprecated. These grains will be removed i
|
|||
- The ``hypervisor_uuid`` has been replaced with ``mdata:sdc:server_uuid`` grain.
|
||||
- The ``datacenter`` has been replaced with ``mdata:sdc:datacenter_name`` grain.
|
||||
|
||||
Minion Blackout
|
||||
---------------
|
||||
|
||||
During a blackout, minions will not execute any remote execution commands,
|
||||
except for :mod:`saltutil.refresh_pillar <salt.modules.saltutil.refresh_pillar>`.
|
||||
Previously, support was added so that blackouts are enabled using a special
|
||||
pillar key, ``minion_blackout`` set to ``True`` and an optional pillar key
|
||||
``minion_blackout_whitelist`` to specify additional functions that are permitted
|
||||
during blackout. This release adds support for using this feature in the grains
|
||||
as well, by using special grains keys ``minion_blackout`` and
|
||||
``minion_blackout_whitelist``.
|
||||
|
||||
Utils Deprecations
|
||||
==================
|
||||
|
||||
|
|
|
@ -166,13 +166,15 @@ Ubuntu 14.04 LTS and Debian Wheezy (7.x) also have a compatible version packaged
|
|||
|
||||
# apt-get install python-git
|
||||
|
||||
If your master is running an older version (such as Ubuntu 12.04 LTS or Debian
|
||||
Squeeze), then you will need to install GitPython using either pip_ or
|
||||
easy_install (it is recommended to use pip). Version 0.3.2.RC1 is now marked as
|
||||
the stable release in PyPI, so it should be a simple matter of running ``pip
|
||||
install GitPython`` (or ``easy_install GitPython``) as root.
|
||||
GitPython_ requires the ``git`` CLI utility to work. If installed from a system
|
||||
package, then git should already be installed, but if installed via pip_ then
|
||||
it may still be necessary to install git separately. For MacOS users,
|
||||
GitPython_ comes bundled in with the Salt installer, but git must still be
|
||||
installed for it to work properly. Git can be installed in several ways,
|
||||
including by installing XCode_.
|
||||
|
||||
.. _`pip`: http://www.pip-installer.org/
|
||||
.. _pip: http://www.pip-installer.org/
|
||||
.. _XCode: https://developer.apple.com/xcode/
|
||||
|
||||
.. warning::
|
||||
|
||||
|
|
|
@ -110,7 +110,7 @@ To pass through a file that contains jinja + yaml templating (the default):
|
|||
method='POST',
|
||||
data_file='/srv/salt/somefile.jinja',
|
||||
data_render=True,
|
||||
template_data={'key1': 'value1', 'key2': 'value2'}
|
||||
template_dict={'key1': 'value1', 'key2': 'value2'}
|
||||
)
|
||||
|
||||
To pass through a file that contains mako templating:
|
||||
|
@ -123,7 +123,7 @@ To pass through a file that contains mako templating:
|
|||
data_file='/srv/salt/somefile.mako',
|
||||
data_render=True,
|
||||
data_renderer='mako',
|
||||
template_data={'key1': 'value1', 'key2': 'value2'}
|
||||
template_dict={'key1': 'value1', 'key2': 'value2'}
|
||||
)
|
||||
|
||||
Because this function uses Salt's own rendering system, any Salt renderer can
|
||||
|
@ -140,7 +140,7 @@ However, this can be changed to ``master`` if necessary.
|
|||
method='POST',
|
||||
data_file='/srv/salt/somefile.jinja',
|
||||
data_render=True,
|
||||
template_data={'key1': 'value1', 'key2': 'value2'},
|
||||
template_dict={'key1': 'value1', 'key2': 'value2'},
|
||||
opts=__opts__
|
||||
)
|
||||
|
||||
|
@ -149,7 +149,7 @@ However, this can be changed to ``master`` if necessary.
|
|||
method='POST',
|
||||
data_file='/srv/salt/somefile.jinja',
|
||||
data_render=True,
|
||||
template_data={'key1': 'value1', 'key2': 'value2'},
|
||||
template_dict={'key1': 'value1', 'key2': 'value2'},
|
||||
node='master'
|
||||
)
|
||||
|
||||
|
@ -170,11 +170,11 @@ a Python dict.
|
|||
header_file='/srv/salt/headers.jinja',
|
||||
header_render=True,
|
||||
header_renderer='jinja',
|
||||
template_data={'key1': 'value1', 'key2': 'value2'}
|
||||
template_dict={'key1': 'value1', 'key2': 'value2'}
|
||||
)
|
||||
|
||||
Because much of the data that would be templated between headers and data may be
|
||||
the same, the ``template_data`` is the same for both. Correcting possible
|
||||
the same, the ``template_dict`` is the same for both. Correcting possible
|
||||
variable name collisions is up to the user.
|
||||
|
||||
Authentication
|
||||
|
|
|
@ -28,9 +28,8 @@ Tutorials Index
|
|||
* :ref:`States tutorial, part 3 - Templating, Includes, Extends <tutorial-states-part-3>`
|
||||
* :ref:`States tutorial, part 4 <tutorial-states-part-4>`
|
||||
* :ref:`How to Convert Jinja Logic to an Execution Module <tutorial-jinja_to_execution-module>`
|
||||
* :ref:`Using Salt with Stormpath <tutorial-stormpath>`
|
||||
* :ref:`Syslog-ng usage <syslog-ng-sate-usage>`
|
||||
* :ref:`The macOS (Maverick) Developer Step By Step Guide To Salt Installation <tutorial-macos-walk-through>`
|
||||
* :ref:`SaltStack Walk-through <tutorial-salt-walk-through>`
|
||||
* :ref:`Writing Salt Tests <tutorial-salt-testing>`
|
||||
* :ref:`Multi-cloud orchestration with Apache Libcloud <tutorial-libcloud>`
|
||||
* :ref:`Multi-cloud orchestration with Apache Libcloud <tutorial-libcloud>`
|
||||
|
|
|
@ -1,198 +0,0 @@
|
|||
.. _tutorial-stormpath:
|
||||
|
||||
=========================
|
||||
Using Salt with Stormpath
|
||||
=========================
|
||||
|
||||
`Stormpath <https://stormpath.com/>`_ is a user management and authentication
|
||||
service. This tutorial covers using SaltStack to manage and take advantage of
|
||||
Stormpath's features.
|
||||
|
||||
External Authentication
|
||||
-----------------------
|
||||
Stormpath can be used for Salt's external authentication system. In order to do
|
||||
this, the master should be configured with an ``apiid``, ``apikey``, and the ID
|
||||
of the ``application`` that is associated with the users to be authenticated:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
stormpath:
|
||||
apiid: 367DFSF4FRJ8767FSF4G34FGH
|
||||
apikey: FEFREF43t3FEFRe/f323fwer4FWF3445gferWRWEer1
|
||||
application: 786786FREFrefreg435fr1
|
||||
|
||||
.. note::
|
||||
These values can be found in the `Stormpath dashboard
|
||||
<https://api.stormpath.com/ui2/index.html#/>`_`.
|
||||
|
||||
Users that are to be authenticated should be set up under the ``stormpath``
|
||||
dict under ``external_auth``:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
external_auth:
|
||||
stormpath:
|
||||
larry:
|
||||
- .*
|
||||
- '@runner'
|
||||
- '@wheel'
|
||||
|
||||
Keep in mind that while Stormpath defaults the username associated with the
|
||||
account to the email address, it is better to use a username without an ``@``
|
||||
sign in it.
|
||||
|
||||
|
||||
Configuring Stormpath Modules
|
||||
-----------------------------
|
||||
Stormpath accounts can be managed via either an execution or state module. In
|
||||
order to use either, a minion must be configured with an API ID and key.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
stormpath:
|
||||
apiid: 367DFSF4FRJ8767FSF4G34FGH
|
||||
apikey: FEFREF43t3FEFRe/f323fwer4FWF3445gferWRWEer1
|
||||
directory: efreg435fr1786786FREFr
|
||||
application: 786786FREFrefreg435fr1
|
||||
|
||||
Some functions in the ``stormpath`` modules can make use of other options. The
|
||||
following options are also available.
|
||||
|
||||
directory
|
||||
`````````
|
||||
The ID of the directory that is to be used with this minion. Many functions
|
||||
require an ID to be specified to do their work. However, if the ID of a
|
||||
``directory`` is specified, then Salt can often look up the resource in
|
||||
question.
|
||||
|
||||
application
|
||||
```````````
|
||||
The ID of the application that is to be used with this minion. Many functions
|
||||
require an ID to be specified to do their work. However, if the ID of a
|
||||
``application`` is specified, then Salt can often look up the resource in
|
||||
question.
|
||||
|
||||
|
||||
Managing Stormpath Accounts
|
||||
---------------------------
|
||||
With the ``stormpath`` configuration in place, Salt can be used to configure
|
||||
accounts (which may be thought of as users) on the Stormpath service. The
|
||||
following functions are available.
|
||||
|
||||
stormpath.create_account
|
||||
````````````````````````
|
||||
Create an account on the Stormpath service. This requires a ``directory_id`` as
|
||||
the first argument; it will not be retrieved from the minion configuration. An
|
||||
``email`` address, ``password``, first name (``givenName``) and last name
|
||||
(``surname``) are also required. For the full list of other parameters that may
|
||||
be specified, see:
|
||||
|
||||
http://docs.stormpath.com/rest/product-guide/#account-resource
|
||||
|
||||
When executed with no errors, this function will return the information about
|
||||
the account, from Stormpath.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt myminion stormpath.create_account <directory_id> shemp@example.com letmein Shemp Howard
|
||||
|
||||
|
||||
stormpath.list_accounts
|
||||
```````````````````````
|
||||
Show all accounts on the Stormpath service. This will return all accounts,
|
||||
regardless of directory, application, or group.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt myminion stormpath.list_accounts
|
||||
'''
|
||||
|
||||
stormpath.show_account
|
||||
``````````````````````
|
||||
Show the details for a specific Stormpath account. An ``account_id`` is normally
|
||||
required. However, if am ``email`` is provided instead, along with either a
|
||||
``directory_id``, ``application_id``, or ``group_id``, then Salt will search the
|
||||
specified resource to try and locate the ``account_id``.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt myminion stormpath.show_account <account_id>
|
||||
salt myminion stormpath.show_account email=<email> directory_id=<directory_id>
|
||||
|
||||
|
||||
stormpath.update_account
|
||||
````````````````````````
|
||||
Update one or more items for this account. Specifying an empty value will clear
|
||||
it for that account. This function may be used in one of two ways. In order to
|
||||
update only one key/value pair, specify them in order:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt myminion stormpath.update_account <account_id> givenName shemp
|
||||
salt myminion stormpath.update_account <account_id> middleName ''
|
||||
|
||||
In order to specify multiple items, they need to be passed in as a dict. From
|
||||
the command line, it is best to do this as a JSON string:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt myminion stormpath.update_account <account_id> items='{"givenName": "Shemp"}
|
||||
salt myminion stormpath.update_account <account_id> items='{"middlename": ""}
|
||||
|
||||
When executed with no errors, this function will return the information about
|
||||
the account, from Stormpath.
|
||||
|
||||
|
||||
stormpath.delete_account
|
||||
````````````````````````
|
||||
Delete an account from Stormpath.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt myminion stormpath.delete_account <account_id>
|
||||
|
||||
|
||||
stormpath.list_directories
|
||||
``````````````````````````
|
||||
Show all directories associated with this tenant.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt myminion stormpath.list_directories
|
||||
|
||||
|
||||
Using Stormpath States
|
||||
----------------------
|
||||
Stormpath resources may be managed using the state system. The following states
|
||||
are available.
|
||||
|
||||
stormpath_account.present
|
||||
`````````````````````````
|
||||
Ensure that an account exists on the Stormpath service. All options that are
|
||||
available with the ``stormpath.create_account`` function are available here.
|
||||
If an account needs to be created, then this function will require the same
|
||||
fields that ``stormpath.create_account`` requires, including the ``password``.
|
||||
However, if a password changes for an existing account, it will NOT be updated
|
||||
by this state.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
curly@example.com:
|
||||
stormpath_account.present:
|
||||
- directory_id: efreg435fr1786786FREFr
|
||||
- password: badpass
|
||||
- firstName: Curly
|
||||
- surname: Howard
|
||||
- nickname: curly
|
||||
|
||||
It is advisable to always set a ``nickname`` that is not also an email address,
|
||||
so that it can be used by Salt's external authentication module.
|
||||
|
||||
stormpath_account.absent
|
||||
````````````````````````
|
||||
Ensure that an account does not exist on Stormpath. As with
|
||||
``stormpath_account.present``, the ``name`` supplied to this state is the
|
||||
``email`` address associated with this account. Salt will use this, with or
|
||||
without the ``directory`` ID that is configured for the minion. However, lookups
|
||||
will be much faster with a directory ID specified.
|
||||
|
|
@ -7,7 +7,7 @@ CherryPy==11.0.0
|
|||
click==6.7
|
||||
enum34==1.1.6
|
||||
gitdb==0.6.4
|
||||
GitPython==2.1.5
|
||||
GitPython==2.1.1
|
||||
idna==2.5
|
||||
ipaddress==1.0.18
|
||||
Jinja2==2.9.6
|
||||
|
|
|
@ -101,8 +101,8 @@ import logging
|
|||
import os
|
||||
|
||||
# Import salt utils
|
||||
import salt.utils
|
||||
import salt.utils.files
|
||||
import salt.utils.versions
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -200,7 +200,7 @@ def _htpasswd(username, password, **kwargs):
|
|||
pwfile = HtpasswdFile(kwargs['filename'])
|
||||
|
||||
# passlib below version 1.6 uses 'verify' function instead of 'check_password'
|
||||
if salt.utils.version_cmp(kwargs['passlib_version'], '1.6') < 0:
|
||||
if salt.utils.versions.version_cmp(kwargs['passlib_version'], '1.6') < 0:
|
||||
return pwfile.verify(username, password)
|
||||
else:
|
||||
return pwfile.check_password(username, password)
|
||||
|
@ -222,7 +222,7 @@ def _htdigest(username, password, **kwargs):
|
|||
pwfile = HtdigestFile(kwargs['filename'])
|
||||
|
||||
# passlib below version 1.6 uses 'verify' function instead of 'check_password'
|
||||
if salt.utils.version_cmp(kwargs['passlib_version'], '1.6') < 0:
|
||||
if salt.utils.versions.version_cmp(kwargs['passlib_version'], '1.6') < 0:
|
||||
return pwfile.verify(username, realm, password)
|
||||
else:
|
||||
return pwfile.check_password(username, realm, password)
|
||||
|
|
|
@ -1,71 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
Provide authentication using Stormpath.
|
||||
|
||||
This driver requires some extra configuration beyond that which Stormpath
|
||||
normally requires.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
stormpath:
|
||||
apiid: 1234567890
|
||||
apikey: 1234567890/ABCDEF
|
||||
# Can use an application ID
|
||||
application: 6789012345
|
||||
# Or can use a directory ID
|
||||
directory: 3456789012
|
||||
# But not both
|
||||
|
||||
.. versionadded:: 2015.8.0
|
||||
'''
|
||||
|
||||
from __future__ import absolute_import
|
||||
import json
|
||||
import base64
|
||||
import urllib
|
||||
import salt.utils.http
|
||||
import logging
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def auth(username, password):
|
||||
'''
|
||||
Authenticate using a Stormpath directory or application
|
||||
'''
|
||||
apiid = __opts__.get('stormpath', {}).get('apiid', None)
|
||||
apikey = __opts__.get('stormpath', {}).get('apikey', None)
|
||||
application = __opts__.get('stormpath', {}).get('application', None)
|
||||
path = 'https://api.stormpath.com/v1'
|
||||
|
||||
if application is not None:
|
||||
path = '{0}/applications/{1}/loginAttempts'.format(path, application)
|
||||
else:
|
||||
return False
|
||||
|
||||
username = urllib.quote(username)
|
||||
data = {
|
||||
'type': 'basic',
|
||||
'value': base64.b64encode('{0}:{1}'.format(username, password))
|
||||
}
|
||||
log.debug('{0}:{1}'.format(username, password))
|
||||
log.debug(path)
|
||||
log.debug(data)
|
||||
log.debug(json.dumps(data))
|
||||
|
||||
result = salt.utils.http.query(
|
||||
path,
|
||||
method='POST',
|
||||
username=apiid,
|
||||
password=apikey,
|
||||
data=json.dumps(data),
|
||||
header_dict={'Content-type': 'application/json;charset=UTF-8'},
|
||||
decode=False,
|
||||
status=True,
|
||||
opts=__opts__,
|
||||
)
|
||||
log.debug(result)
|
||||
if result.get('status', 403) == 200:
|
||||
return True
|
||||
|
||||
return False
|
|
@ -37,8 +37,9 @@ class Beacon(object):
|
|||
.. code_block:: yaml
|
||||
beacons:
|
||||
inotify:
|
||||
- /etc/fstab: {}
|
||||
- /var/cache/foo: {}
|
||||
- files:
|
||||
- /etc/fstab: {}
|
||||
- /var/cache/foo: {}
|
||||
'''
|
||||
ret = []
|
||||
b_config = copy.deepcopy(config)
|
||||
|
@ -69,6 +70,7 @@ class Beacon(object):
|
|||
|
||||
log.trace('Beacon processing: {0}'.format(mod))
|
||||
fun_str = '{0}.beacon'.format(mod)
|
||||
validate_str = '{0}.validate'.format(mod)
|
||||
if fun_str in self.beacons:
|
||||
runonce = self._determine_beacon_config(current_beacon_config, 'run_once')
|
||||
interval = self._determine_beacon_config(current_beacon_config, 'interval')
|
||||
|
@ -95,6 +97,17 @@ class Beacon(object):
|
|||
continue
|
||||
# Update __grains__ on the beacon
|
||||
self.beacons[fun_str].__globals__['__grains__'] = grains
|
||||
|
||||
# Run the validate function if it's available,
|
||||
# otherwise there is a warning about it being missing
|
||||
if validate_str in self.beacons:
|
||||
valid, vcomment = self.beacons[validate_str](b_config[mod])
|
||||
|
||||
if not valid:
|
||||
log.info('Beacon %s configuration invalid, '
|
||||
'not running.\n%s', mod, vcomment)
|
||||
continue
|
||||
|
||||
# Fire the beacon!
|
||||
raw = self.beacons[fun_str](b_config[mod])
|
||||
for data in raw:
|
||||
|
@ -193,6 +206,8 @@ class Beacon(object):
|
|||
# Fire the complete event back along with the list of beacons
|
||||
evt = salt.utils.event.get_event('minion', opts=self.opts)
|
||||
b_conf = self.functions['config.merge']('beacons')
|
||||
if not isinstance(self.opts['beacons'], dict):
|
||||
self.opts['beacons'] = {}
|
||||
self.opts['beacons'].update(b_conf)
|
||||
evt.fire_event({'complete': True, 'beacons': self.opts['beacons']},
|
||||
tag='/salt/minion/minion_beacons_list_complete')
|
||||
|
|
|
@ -10,8 +10,8 @@ from __future__ import absolute_import
|
|||
import logging
|
||||
|
||||
# Salt libs
|
||||
import salt.utils
|
||||
import salt.utils.path
|
||||
from salt.ext.six.moves import map
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -29,25 +29,34 @@ def __virtual__():
|
|||
return __virtualname__
|
||||
|
||||
|
||||
def __validate__(config):
|
||||
def validate(config):
|
||||
'''
|
||||
Validate the beacon configuration
|
||||
'''
|
||||
# Configuration for adb beacon should be a dictionary with states array
|
||||
if not isinstance(config, dict):
|
||||
log.info('Configuration for adb beacon must be a dict.')
|
||||
return False, ('Configuration for adb beacon must be a dict.')
|
||||
elif 'states' not in config.keys():
|
||||
if not isinstance(config, list):
|
||||
log.info('Configuration for adb beacon must be a list.')
|
||||
return False, ('Configuration for adb beacon must be a list.')
|
||||
|
||||
_config = {}
|
||||
list(map(_config.update, config))
|
||||
|
||||
if 'states' not in _config:
|
||||
log.info('Configuration for adb beacon must include a states array.')
|
||||
return False, ('Configuration for adb beacon must include a states array.')
|
||||
else:
|
||||
states = ['offline', 'bootloader', 'device', 'host', 'recovery', 'no permissions',
|
||||
'sideload', 'unauthorized', 'unknown', 'missing']
|
||||
if any(s not in states for s in config['states']):
|
||||
log.info('Need a one of the following adb '
|
||||
'states: {0}'.format(', '.join(states)))
|
||||
return False, ('Need a one of the following adb '
|
||||
'states: {0}'.format(', '.join(states)))
|
||||
if not isinstance(_config['states'], list):
|
||||
log.info('Configuration for adb beacon must include a states array.')
|
||||
return False, ('Configuration for adb beacon must include a states array.')
|
||||
else:
|
||||
states = ['offline', 'bootloader', 'device', 'host',
|
||||
'recovery', 'no permissions',
|
||||
'sideload', 'unauthorized', 'unknown', 'missing']
|
||||
if any(s not in states for s in _config['states']):
|
||||
log.info('Need a one of the following adb '
|
||||
'states: {0}'.format(', '.join(states)))
|
||||
return False, ('Need a one of the following adb '
|
||||
'states: {0}'.format(', '.join(states)))
|
||||
return True, 'Valid beacon configuration'
|
||||
|
||||
|
||||
|
@ -75,11 +84,10 @@ def beacon(config):
|
|||
log.trace('adb beacon starting')
|
||||
ret = []
|
||||
|
||||
_validate = __validate__(config)
|
||||
if not _validate[0]:
|
||||
return ret
|
||||
_config = {}
|
||||
list(map(_config.update, config))
|
||||
|
||||
out = __salt__['cmd.run']('adb devices', runas=config.get('user', None))
|
||||
out = __salt__['cmd.run']('adb devices', runas=_config.get('user', None))
|
||||
|
||||
lines = out.split('\n')[1:]
|
||||
last_state_devices = list(last_state.keys())
|
||||
|
@ -91,21 +99,21 @@ def beacon(config):
|
|||
found_devices.append(device)
|
||||
if device not in last_state_devices or \
|
||||
('state' in last_state[device] and last_state[device]['state'] != state):
|
||||
if state in config['states']:
|
||||
if state in _config['states']:
|
||||
ret.append({'device': device, 'state': state, 'tag': state})
|
||||
last_state[device] = {'state': state}
|
||||
|
||||
if 'battery_low' in config:
|
||||
if 'battery_low' in _config:
|
||||
val = last_state.get(device, {})
|
||||
cmd = 'adb -s {0} shell cat /sys/class/power_supply/*/capacity'.format(device)
|
||||
battery_levels = __salt__['cmd.run'](cmd, runas=config.get('user', None)).split('\n')
|
||||
battery_levels = __salt__['cmd.run'](cmd, runas=_config.get('user', None)).split('\n')
|
||||
|
||||
for l in battery_levels:
|
||||
battery_level = int(l)
|
||||
if 0 < battery_level < 100:
|
||||
if 'battery' not in val or battery_level != val['battery']:
|
||||
if ('battery' not in val or val['battery'] > config['battery_low']) and \
|
||||
battery_level <= config['battery_low']:
|
||||
if ('battery' not in val or val['battery'] > _config['battery_low']) and \
|
||||
battery_level <= _config['battery_low']:
|
||||
ret.append({'device': device, 'battery_level': battery_level, 'tag': 'battery_low'})
|
||||
|
||||
if device not in last_state:
|
||||
|
@ -119,13 +127,13 @@ def beacon(config):
|
|||
# Find missing devices and remove them / send an event
|
||||
for device in last_state_devices:
|
||||
if device not in found_devices:
|
||||
if 'missing' in config['states']:
|
||||
if 'missing' in _config['states']:
|
||||
ret.append({'device': device, 'state': 'missing', 'tag': 'missing'})
|
||||
|
||||
del last_state[device]
|
||||
|
||||
# Maybe send an event if we don't have any devices
|
||||
if 'no_devices_event' in config and config['no_devices_event'] is True:
|
||||
if 'no_devices_event' in _config and _config['no_devices_event'] is True:
|
||||
if len(found_devices) == 0 and not last_state_extra['no_devices']:
|
||||
ret.append({'tag': 'no_devices'})
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ Dependencies
|
|||
from __future__ import absolute_import
|
||||
import logging
|
||||
import time
|
||||
from salt.ext.six.moves import map
|
||||
|
||||
# Import 3rd Party libs
|
||||
try:
|
||||
|
@ -54,17 +55,23 @@ def __virtual__():
|
|||
'\'python-avahi\' dependency is missing.'.format(__virtualname__)
|
||||
|
||||
|
||||
def __validate__(config):
|
||||
def validate(config):
|
||||
'''
|
||||
Validate the beacon configuration
|
||||
'''
|
||||
if not isinstance(config, dict):
|
||||
return False, ('Configuration for avahi_announcement '
|
||||
'beacon must be a dictionary')
|
||||
elif not all(x in list(config.keys()) for x in ('servicetype', 'port', 'txt')):
|
||||
_config = {}
|
||||
list(map(_config.update, config))
|
||||
|
||||
if not isinstance(config, list):
|
||||
return False, ('Configuration for avahi_announce '
|
||||
'beacon must be a list.')
|
||||
|
||||
elif not all(x in _config for x in ('servicetype',
|
||||
'port',
|
||||
'txt')):
|
||||
return False, ('Configuration for avahi_announce beacon '
|
||||
'must contain servicetype, port and txt items')
|
||||
return True, 'Valid beacon configuration'
|
||||
'must contain servicetype, port and txt items.')
|
||||
return True, 'Valid beacon configuration.'
|
||||
|
||||
|
||||
def _enforce_txt_record_maxlen(key, value):
|
||||
|
@ -138,13 +145,13 @@ def beacon(config):
|
|||
|
||||
beacons:
|
||||
avahi_announce:
|
||||
run_once: True
|
||||
servicetype: _demo._tcp
|
||||
port: 1234
|
||||
txt:
|
||||
ProdName: grains.productname
|
||||
SerialNo: grains.serialnumber
|
||||
Comments: 'this is a test'
|
||||
- run_once: True
|
||||
- servicetype: _demo._tcp
|
||||
- port: 1234
|
||||
- txt:
|
||||
ProdName: grains.productname
|
||||
SerialNo: grains.serialnumber
|
||||
Comments: 'this is a test'
|
||||
'''
|
||||
ret = []
|
||||
changes = {}
|
||||
|
@ -152,30 +159,27 @@ def beacon(config):
|
|||
|
||||
global LAST_GRAINS
|
||||
|
||||
_validate = __validate__(config)
|
||||
if not _validate[0]:
|
||||
log.warning('Beacon {0} configuration invalid, '
|
||||
'not adding. {1}'.format(__virtualname__, _validate[1]))
|
||||
return ret
|
||||
_config = {}
|
||||
list(map(_config.update, config))
|
||||
|
||||
if 'servicename' in config:
|
||||
servicename = config['servicename']
|
||||
if 'servicename' in _config:
|
||||
servicename = _config['servicename']
|
||||
else:
|
||||
servicename = __grains__['host']
|
||||
# Check for hostname change
|
||||
if LAST_GRAINS and LAST_GRAINS['host'] != servicename:
|
||||
changes['servicename'] = servicename
|
||||
|
||||
if LAST_GRAINS and config.get('reset_on_change', False):
|
||||
if LAST_GRAINS and _config.get('reset_on_change', False):
|
||||
# Check for IP address change in the case when we reset on change
|
||||
if LAST_GRAINS.get('ipv4', []) != __grains__.get('ipv4', []):
|
||||
changes['ipv4'] = __grains__.get('ipv4', [])
|
||||
if LAST_GRAINS.get('ipv6', []) != __grains__.get('ipv6', []):
|
||||
changes['ipv6'] = __grains__.get('ipv6', [])
|
||||
|
||||
for item in config['txt']:
|
||||
if config['txt'][item].startswith('grains.'):
|
||||
grain = config['txt'][item][7:]
|
||||
for item in _config['txt']:
|
||||
if _config['txt'][item].startswith('grains.'):
|
||||
grain = _config['txt'][item][7:]
|
||||
grain_index = None
|
||||
square_bracket = grain.find('[')
|
||||
if square_bracket != -1 and grain[-1] == ']':
|
||||
|
@ -192,7 +196,7 @@ def beacon(config):
|
|||
if LAST_GRAINS and (LAST_GRAINS.get(grain, '') != __grains__.get(grain, '')):
|
||||
changes[str('txt.' + item)] = txt[item]
|
||||
else:
|
||||
txt[item] = _enforce_txt_record_maxlen(item, config['txt'][item])
|
||||
txt[item] = _enforce_txt_record_maxlen(item, _config['txt'][item])
|
||||
|
||||
if not LAST_GRAINS:
|
||||
changes[str('txt.' + item)] = txt[item]
|
||||
|
@ -200,33 +204,33 @@ def beacon(config):
|
|||
if changes:
|
||||
if not LAST_GRAINS:
|
||||
changes['servicename'] = servicename
|
||||
changes['servicetype'] = config['servicetype']
|
||||
changes['port'] = config['port']
|
||||
changes['servicetype'] = _config['servicetype']
|
||||
changes['port'] = _config['port']
|
||||
changes['ipv4'] = __grains__.get('ipv4', [])
|
||||
changes['ipv6'] = __grains__.get('ipv6', [])
|
||||
GROUP.AddService(avahi.IF_UNSPEC, avahi.PROTO_UNSPEC, dbus.UInt32(0),
|
||||
servicename, config['servicetype'], '', '',
|
||||
dbus.UInt16(config['port']), avahi.dict_to_txt_array(txt))
|
||||
servicename, _config['servicetype'], '', '',
|
||||
dbus.UInt16(_config['port']), avahi.dict_to_txt_array(txt))
|
||||
GROUP.Commit()
|
||||
elif config.get('reset_on_change', False) or 'servicename' in changes:
|
||||
elif _config.get('reset_on_change', False) or 'servicename' in changes:
|
||||
# A change in 'servicename' requires a reset because we can only
|
||||
# directly update TXT records
|
||||
GROUP.Reset()
|
||||
reset_wait = config.get('reset_wait', 0)
|
||||
reset_wait = _config.get('reset_wait', 0)
|
||||
if reset_wait > 0:
|
||||
time.sleep(reset_wait)
|
||||
GROUP.AddService(avahi.IF_UNSPEC, avahi.PROTO_UNSPEC, dbus.UInt32(0),
|
||||
servicename, config['servicetype'], '', '',
|
||||
dbus.UInt16(config['port']), avahi.dict_to_txt_array(txt))
|
||||
servicename, _config['servicetype'], '', '',
|
||||
dbus.UInt16(_config['port']), avahi.dict_to_txt_array(txt))
|
||||
GROUP.Commit()
|
||||
else:
|
||||
GROUP.UpdateServiceTxt(avahi.IF_UNSPEC, avahi.PROTO_UNSPEC, dbus.UInt32(0),
|
||||
servicename, config['servicetype'], '',
|
||||
servicename, _config['servicetype'], '',
|
||||
avahi.dict_to_txt_array(txt))
|
||||
|
||||
ret.append({'tag': 'result', 'changes': changes})
|
||||
|
||||
if config.get('copy_grains', False):
|
||||
if _config.get('copy_grains', False):
|
||||
LAST_GRAINS = __grains__.copy()
|
||||
else:
|
||||
LAST_GRAINS = __grains__
|
||||
|
|
|
@ -9,6 +9,7 @@ import atexit
|
|||
import logging
|
||||
import select
|
||||
import time
|
||||
from salt.ext.six.moves import map
|
||||
|
||||
# Import 3rd Party libs
|
||||
try:
|
||||
|
@ -47,17 +48,23 @@ def _register_callback(sdRef, flags, errorCode, name, regtype, domain): # pylin
|
|||
log.error('Bonjour registration failed with error code {0}'.format(errorCode))
|
||||
|
||||
|
||||
def __validate__(config):
|
||||
def validate(config):
|
||||
'''
|
||||
Validate the beacon configuration
|
||||
'''
|
||||
if not isinstance(config, dict):
|
||||
return False, ('Configuration for bonjour_announcement '
|
||||
'beacon must be a dictionary')
|
||||
elif not all(x in list(config.keys()) for x in ('servicetype', 'port', 'txt')):
|
||||
_config = {}
|
||||
list(map(_config.update, config))
|
||||
|
||||
if not isinstance(config, list):
|
||||
return False, ('Configuration for bonjour_announce '
|
||||
'beacon must be a list.')
|
||||
|
||||
elif not all(x in _config for x in ('servicetype',
|
||||
'port',
|
||||
'txt')):
|
||||
return False, ('Configuration for bonjour_announce beacon '
|
||||
'must contain servicetype, port and txt items')
|
||||
return True, 'Valid beacon configuration'
|
||||
'must contain servicetype, port and txt items.')
|
||||
return True, 'Valid beacon configuration.'
|
||||
|
||||
|
||||
def _enforce_txt_record_maxlen(key, value):
|
||||
|
@ -131,13 +138,13 @@ def beacon(config):
|
|||
|
||||
beacons:
|
||||
bonjour_announce:
|
||||
run_once: True
|
||||
servicetype: _demo._tcp
|
||||
port: 1234
|
||||
txt:
|
||||
ProdName: grains.productname
|
||||
SerialNo: grains.serialnumber
|
||||
Comments: 'this is a test'
|
||||
- run_once: True
|
||||
- servicetype: _demo._tcp
|
||||
- port: 1234
|
||||
- txt:
|
||||
ProdName: grains.productname
|
||||
SerialNo: grains.serialnumber
|
||||
Comments: 'this is a test'
|
||||
'''
|
||||
ret = []
|
||||
changes = {}
|
||||
|
@ -146,30 +153,27 @@ def beacon(config):
|
|||
global LAST_GRAINS
|
||||
global SD_REF
|
||||
|
||||
_validate = __validate__(config)
|
||||
if not _validate[0]:
|
||||
log.warning('Beacon {0} configuration invalid, '
|
||||
'not adding. {1}'.format(__virtualname__, _validate[1]))
|
||||
return ret
|
||||
_config = {}
|
||||
list(map(_config.update, config))
|
||||
|
||||
if 'servicename' in config:
|
||||
servicename = config['servicename']
|
||||
if 'servicename' in _config:
|
||||
servicename = _config['servicename']
|
||||
else:
|
||||
servicename = __grains__['host']
|
||||
# Check for hostname change
|
||||
if LAST_GRAINS and LAST_GRAINS['host'] != servicename:
|
||||
changes['servicename'] = servicename
|
||||
|
||||
if LAST_GRAINS and config.get('reset_on_change', False):
|
||||
if LAST_GRAINS and _config.get('reset_on_change', False):
|
||||
# Check for IP address change in the case when we reset on change
|
||||
if LAST_GRAINS.get('ipv4', []) != __grains__.get('ipv4', []):
|
||||
changes['ipv4'] = __grains__.get('ipv4', [])
|
||||
if LAST_GRAINS.get('ipv6', []) != __grains__.get('ipv6', []):
|
||||
changes['ipv6'] = __grains__.get('ipv6', [])
|
||||
|
||||
for item in config['txt']:
|
||||
if config['txt'][item].startswith('grains.'):
|
||||
grain = config['txt'][item][7:]
|
||||
for item in _config['txt']:
|
||||
if _config['txt'][item].startswith('grains.'):
|
||||
grain = _config['txt'][item][7:]
|
||||
grain_index = None
|
||||
square_bracket = grain.find('[')
|
||||
if square_bracket != -1 and grain[-1] == ']':
|
||||
|
@ -186,7 +190,7 @@ def beacon(config):
|
|||
if LAST_GRAINS and (LAST_GRAINS.get(grain, '') != __grains__.get(grain, '')):
|
||||
changes[str('txt.' + item)] = txt[item]
|
||||
else:
|
||||
txt[item] = _enforce_txt_record_maxlen(item, config['txt'][item])
|
||||
txt[item] = _enforce_txt_record_maxlen(item, _config['txt'][item])
|
||||
|
||||
if not LAST_GRAINS:
|
||||
changes[str('txt.' + item)] = txt[item]
|
||||
|
@ -195,32 +199,32 @@ def beacon(config):
|
|||
txt_record = pybonjour.TXTRecord(items=txt)
|
||||
if not LAST_GRAINS:
|
||||
changes['servicename'] = servicename
|
||||
changes['servicetype'] = config['servicetype']
|
||||
changes['port'] = config['port']
|
||||
changes['servicetype'] = _config['servicetype']
|
||||
changes['port'] = _config['port']
|
||||
changes['ipv4'] = __grains__.get('ipv4', [])
|
||||
changes['ipv6'] = __grains__.get('ipv6', [])
|
||||
SD_REF = pybonjour.DNSServiceRegister(
|
||||
name=servicename,
|
||||
regtype=config['servicetype'],
|
||||
port=config['port'],
|
||||
regtype=_config['servicetype'],
|
||||
port=_config['port'],
|
||||
txtRecord=txt_record,
|
||||
callBack=_register_callback)
|
||||
atexit.register(_close_sd_ref)
|
||||
ready = select.select([SD_REF], [], [])
|
||||
if SD_REF in ready[0]:
|
||||
pybonjour.DNSServiceProcessResult(SD_REF)
|
||||
elif config.get('reset_on_change', False) or 'servicename' in changes:
|
||||
elif _config.get('reset_on_change', False) or 'servicename' in changes:
|
||||
# A change in 'servicename' requires a reset because we can only
|
||||
# directly update TXT records
|
||||
SD_REF.close()
|
||||
SD_REF = None
|
||||
reset_wait = config.get('reset_wait', 0)
|
||||
reset_wait = _config.get('reset_wait', 0)
|
||||
if reset_wait > 0:
|
||||
time.sleep(reset_wait)
|
||||
SD_REF = pybonjour.DNSServiceRegister(
|
||||
name=servicename,
|
||||
regtype=config['servicetype'],
|
||||
port=config['port'],
|
||||
regtype=_config['servicetype'],
|
||||
port=_config['port'],
|
||||
txtRecord=txt_record,
|
||||
callBack=_register_callback)
|
||||
ready = select.select([SD_REF], [], [])
|
||||
|
@ -236,7 +240,7 @@ def beacon(config):
|
|||
|
||||
ret.append({'tag': 'result', 'changes': changes})
|
||||
|
||||
if config.get('copy_grains', False):
|
||||
if _config.get('copy_grains', False):
|
||||
LAST_GRAINS = __grains__.copy()
|
||||
else:
|
||||
LAST_GRAINS = __grains__
|
||||
|
|
|
@ -5,7 +5,7 @@ Beacon to fire events at failed login of users
|
|||
.. code-block:: yaml
|
||||
|
||||
beacons:
|
||||
btmp: {}
|
||||
btmp: []
|
||||
'''
|
||||
|
||||
# Import python libs
|
||||
|
@ -52,14 +52,14 @@ def _get_loc():
|
|||
return __context__[LOC_KEY]
|
||||
|
||||
|
||||
def __validate__(config):
|
||||
def validate(config):
|
||||
'''
|
||||
Validate the beacon configuration
|
||||
'''
|
||||
# Configuration for load beacon should be a list of dicts
|
||||
if not isinstance(config, dict):
|
||||
if not isinstance(config, list):
|
||||
return False, ('Configuration for btmp beacon must '
|
||||
'be a list of dictionaries.')
|
||||
'be a list.')
|
||||
return True, 'Valid beacon configuration'
|
||||
|
||||
|
||||
|
@ -71,7 +71,7 @@ def beacon(config):
|
|||
.. code-block:: yaml
|
||||
|
||||
beacons:
|
||||
btmp: {}
|
||||
btmp: []
|
||||
'''
|
||||
ret = []
|
||||
with salt.utils.files.fopen(BTMP, 'rb') as fp_:
|
||||
|
|
|
@ -31,14 +31,14 @@ def __virtual__():
|
|||
return __virtualname__
|
||||
|
||||
|
||||
def __validate__(config):
|
||||
def validate(config):
|
||||
'''
|
||||
Validate the beacon configuration
|
||||
'''
|
||||
# Configuration for diskusage beacon should be a list of dicts
|
||||
if not isinstance(config, dict):
|
||||
if not isinstance(config, list):
|
||||
return False, ('Configuration for diskusage beacon '
|
||||
'must be a dictionary.')
|
||||
'must be a list.')
|
||||
return True, 'Valid beacon configuration'
|
||||
|
||||
|
||||
|
@ -86,25 +86,24 @@ def beacon(config):
|
|||
parts = psutil.disk_partitions(all=False)
|
||||
ret = []
|
||||
for mounts in config:
|
||||
mount = mounts.keys()[0]
|
||||
mount = next(iter(mounts))
|
||||
|
||||
try:
|
||||
_current_usage = psutil.disk_usage(mount)
|
||||
except OSError:
|
||||
# Ensure a valid mount point
|
||||
log.warning('{0} is not a valid mount point, try regex.'.format(mount))
|
||||
for part in parts:
|
||||
if re.match(mount, part.mountpoint):
|
||||
row = {}
|
||||
row[part.mountpoint] = mounts[mount]
|
||||
config.append(row)
|
||||
continue
|
||||
for part in parts:
|
||||
if re.match(mount, part.mountpoint):
|
||||
_mount = part.mountpoint
|
||||
|
||||
current_usage = _current_usage.percent
|
||||
monitor_usage = mounts[mount]
|
||||
if '%' in monitor_usage:
|
||||
monitor_usage = re.sub('%', '', monitor_usage)
|
||||
monitor_usage = float(monitor_usage)
|
||||
if current_usage >= monitor_usage:
|
||||
ret.append({'diskusage': current_usage, 'mount': mount})
|
||||
try:
|
||||
_current_usage = psutil.disk_usage(mount)
|
||||
except OSError:
|
||||
log.warning('{0} is not a valid mount point.'.format(mount))
|
||||
continue
|
||||
|
||||
current_usage = _current_usage.percent
|
||||
monitor_usage = mounts[mount]
|
||||
log.info('current_usage {}'.format(current_usage))
|
||||
if '%' in monitor_usage:
|
||||
monitor_usage = re.sub('%', '', monitor_usage)
|
||||
monitor_usage = float(monitor_usage)
|
||||
if current_usage >= monitor_usage:
|
||||
ret.append({'diskusage': current_usage, 'mount': _mount})
|
||||
return ret
|
||||
|
|
|
@ -11,6 +11,7 @@ import logging
|
|||
|
||||
# Salt libs
|
||||
import salt.utils.path
|
||||
from salt.ext.six.moves import map
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -28,14 +29,18 @@ def __virtual__():
|
|||
return __virtualname__
|
||||
|
||||
|
||||
def __validate__(config):
|
||||
def validate(config):
|
||||
'''
|
||||
Validate the beacon configuration
|
||||
'''
|
||||
# Configuration for glxinfo beacon should be a dictionary
|
||||
if not isinstance(config, dict):
|
||||
return False, ('Configuration for glxinfo beacon must be a dict.')
|
||||
if 'user' not in config:
|
||||
if not isinstance(config, list):
|
||||
return False, ('Configuration for glxinfo beacon must be a list.')
|
||||
|
||||
_config = {}
|
||||
list(map(_config.update, config))
|
||||
|
||||
if 'user' not in _config:
|
||||
return False, ('Configuration for glxinfo beacon must '
|
||||
'include a user as glxinfo is not available to root.')
|
||||
return True, 'Valid beacon configuration'
|
||||
|
@ -45,27 +50,28 @@ def beacon(config):
|
|||
'''
|
||||
Emit the status of a connected display to the minion
|
||||
|
||||
Mainly this is used to detect when the display fails to connect for whatever reason.
|
||||
Mainly this is used to detect when the display fails to connect
|
||||
for whatever reason.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
beacons:
|
||||
glxinfo:
|
||||
user: frank
|
||||
screen_event: True
|
||||
- user: frank
|
||||
- screen_event: True
|
||||
|
||||
'''
|
||||
|
||||
log.trace('glxinfo beacon starting')
|
||||
ret = []
|
||||
|
||||
_validate = __validate__(config)
|
||||
if not _validate[0]:
|
||||
return ret
|
||||
_config = {}
|
||||
list(map(_config.update, config))
|
||||
|
||||
retcode = __salt__['cmd.retcode']('DISPLAY=:0 glxinfo', runas=config['user'], python_shell=True)
|
||||
retcode = __salt__['cmd.retcode']('DISPLAY=:0 glxinfo',
|
||||
runas=_config['user'], python_shell=True)
|
||||
|
||||
if 'screen_event' in config and config['screen_event']:
|
||||
if 'screen_event' in _config and _config['screen_event']:
|
||||
last_value = last_state.get('screen_available', False)
|
||||
screen_available = retcode == 0
|
||||
if last_value != screen_available or 'screen_available' not in last_state:
|
||||
|
|
|
@ -9,7 +9,7 @@ Fire an event when over a specified threshold.
|
|||
# Import Python libs
|
||||
from __future__ import absolute_import
|
||||
import logging
|
||||
|
||||
from salt.ext.six.moves import map
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -23,17 +23,39 @@ def __virtual__():
|
|||
if 'haproxy.get_sessions' in __salt__:
|
||||
return __virtualname__
|
||||
else:
|
||||
log.debug('Not loading haproxy beacon')
|
||||
return False
|
||||
|
||||
|
||||
def __validate__(config):
|
||||
def validate(config):
|
||||
'''
|
||||
Validate the beacon configuration
|
||||
'''
|
||||
if not isinstance(config, dict):
|
||||
return False, ('Configuration for haproxy beacon must be a dictionary.')
|
||||
if 'haproxy' not in config:
|
||||
return False, ('Configuration for haproxy beacon requires a list of backends and servers')
|
||||
if not isinstance(config, list):
|
||||
return False, ('Configuration for haproxy beacon must '
|
||||
'be a list.')
|
||||
else:
|
||||
_config = {}
|
||||
list(map(_config.update, config))
|
||||
|
||||
if 'backends' not in _config:
|
||||
return False, ('Configuration for haproxy beacon '
|
||||
'requires backends.')
|
||||
else:
|
||||
if not isinstance(_config['backends'], dict):
|
||||
return False, ('Backends for haproxy beacon '
|
||||
'must be a dictionary.')
|
||||
else:
|
||||
for backend in _config['backends']:
|
||||
log.debug('_config {}'.format(_config['backends'][backend]))
|
||||
if 'servers' not in _config['backends'][backend]:
|
||||
return False, ('Backends for haproxy beacon '
|
||||
'require servers.')
|
||||
else:
|
||||
_servers = _config['backends'][backend]['servers']
|
||||
if not isinstance(_servers, list):
|
||||
return False, ('Servers for haproxy beacon '
|
||||
'must be a list.')
|
||||
return True, 'Valid beacon configuration'
|
||||
|
||||
|
||||
|
@ -46,22 +68,23 @@ def beacon(config):
|
|||
|
||||
beacons:
|
||||
haproxy:
|
||||
- www-backend:
|
||||
threshold: 45
|
||||
servers:
|
||||
- web1
|
||||
- web2
|
||||
- backends:
|
||||
www-backend:
|
||||
threshold: 45
|
||||
servers:
|
||||
- web1
|
||||
- web2
|
||||
- interval: 120
|
||||
'''
|
||||
log.debug('haproxy beacon starting')
|
||||
ret = []
|
||||
_validate = __validate__(config)
|
||||
if not _validate:
|
||||
log.debug('haproxy beacon unable to validate')
|
||||
return ret
|
||||
for backend in config:
|
||||
threshold = config[backend]['threshold']
|
||||
for server in config[backend]['servers']:
|
||||
|
||||
_config = {}
|
||||
list(map(_config.update, config))
|
||||
|
||||
for backend in _config.get('backends', ()):
|
||||
backend_config = _config['backends'][backend]
|
||||
threshold = backend_config['threshold']
|
||||
for server in backend_config['servers']:
|
||||
scur = __salt__['haproxy.get_sessions'](server, backend)
|
||||
if scur:
|
||||
if int(scur) > int(threshold):
|
||||
|
@ -69,6 +92,10 @@ def beacon(config):
|
|||
'scur': scur,
|
||||
'threshold': threshold,
|
||||
}
|
||||
log.debug('Emit because {0} > {1} for {2} in {3}'.format(scur, threshold, server, backend))
|
||||
log.debug('Emit because {0} > {1}'
|
||||
' for {2} in {3}'.format(scur,
|
||||
threshold,
|
||||
server,
|
||||
backend))
|
||||
ret.append(_server)
|
||||
return ret
|
||||
|
|
|
@ -23,6 +23,7 @@ import re
|
|||
|
||||
# Import salt libs
|
||||
import salt.ext.six
|
||||
from salt.ext.six.moves import map
|
||||
|
||||
# Import third party libs
|
||||
try:
|
||||
|
@ -79,7 +80,7 @@ def _get_notifier(config):
|
|||
return __context__['inotify.notifier']
|
||||
|
||||
|
||||
def __validate__(config):
|
||||
def validate(config):
|
||||
'''
|
||||
Validate the beacon configuration
|
||||
'''
|
||||
|
@ -105,37 +106,45 @@ def __validate__(config):
|
|||
]
|
||||
|
||||
# Configuration for inotify beacon should be a dict of dicts
|
||||
log.debug('config {0}'.format(config))
|
||||
if not isinstance(config, dict):
|
||||
return False, 'Configuration for inotify beacon must be a dictionary.'
|
||||
if not isinstance(config, list):
|
||||
return False, 'Configuration for inotify beacon must be a list.'
|
||||
else:
|
||||
for config_item in config:
|
||||
if not isinstance(config[config_item], dict):
|
||||
return False, ('Configuration for inotify beacon must '
|
||||
'be a dictionary of dictionaries.')
|
||||
else:
|
||||
if not any(j in ['mask', 'recurse', 'auto_add'] for j in config[config_item]):
|
||||
_config = {}
|
||||
list(map(_config.update, config))
|
||||
|
||||
if 'files' not in _config:
|
||||
return False, 'Configuration for inotify beacon must include files.'
|
||||
else:
|
||||
for path in _config.get('files'):
|
||||
|
||||
if not isinstance(_config['files'][path], dict):
|
||||
return False, ('Configuration for inotify beacon must '
|
||||
'contain mask, recurse or auto_add items.')
|
||||
'be a list of dictionaries.')
|
||||
else:
|
||||
if not any(j in ['mask',
|
||||
'recurse',
|
||||
'auto_add'] for j in _config['files'][path]):
|
||||
return False, ('Configuration for inotify beacon must '
|
||||
'contain mask, recurse or auto_add items.')
|
||||
|
||||
if 'auto_add' in config[config_item]:
|
||||
if not isinstance(config[config_item]['auto_add'], bool):
|
||||
return False, ('Configuration for inotify beacon '
|
||||
'auto_add must be boolean.')
|
||||
if 'auto_add' in _config['files'][path]:
|
||||
if not isinstance(_config['files'][path]['auto_add'], bool):
|
||||
return False, ('Configuration for inotify beacon '
|
||||
'auto_add must be boolean.')
|
||||
|
||||
if 'recurse' in config[config_item]:
|
||||
if not isinstance(config[config_item]['recurse'], bool):
|
||||
return False, ('Configuration for inotify beacon '
|
||||
'recurse must be boolean.')
|
||||
if 'recurse' in _config['files'][path]:
|
||||
if not isinstance(_config['files'][path]['recurse'], bool):
|
||||
return False, ('Configuration for inotify beacon '
|
||||
'recurse must be boolean.')
|
||||
|
||||
if 'mask' in config[config_item]:
|
||||
if not isinstance(config[config_item]['mask'], list):
|
||||
return False, ('Configuration for inotify beacon '
|
||||
'mask must be list.')
|
||||
for mask in config[config_item]['mask']:
|
||||
if mask not in VALID_MASK:
|
||||
return False, ('Configuration for inotify beacon '
|
||||
'invalid mask option {0}.'.format(mask))
|
||||
if 'mask' in _config['files'][path]:
|
||||
if not isinstance(_config['files'][path]['mask'], list):
|
||||
return False, ('Configuration for inotify beacon '
|
||||
'mask must be list.')
|
||||
for mask in _config['files'][path]['mask']:
|
||||
if mask not in VALID_MASK:
|
||||
return False, ('Configuration for inotify beacon '
|
||||
'invalid mask option {0}.'.format(mask))
|
||||
return True, 'Valid beacon configuration'
|
||||
|
||||
|
||||
|
@ -149,19 +158,20 @@ def beacon(config):
|
|||
|
||||
beacons:
|
||||
inotify:
|
||||
/path/to/file/or/dir:
|
||||
mask:
|
||||
- open
|
||||
- create
|
||||
- close_write
|
||||
recurse: True
|
||||
auto_add: True
|
||||
exclude:
|
||||
- /path/to/file/or/dir/exclude1
|
||||
- /path/to/file/or/dir/exclude2
|
||||
- /path/to/file/or/dir/regex[a-m]*$:
|
||||
regex: True
|
||||
coalesce: True
|
||||
- files:
|
||||
/path/to/file/or/dir:
|
||||
mask:
|
||||
- open
|
||||
- create
|
||||
- close_write
|
||||
recurse: True
|
||||
auto_add: True
|
||||
exclude:
|
||||
- /path/to/file/or/dir/exclude1
|
||||
- /path/to/file/or/dir/exclude2
|
||||
- /path/to/file/or/dir/regex[a-m]*$:
|
||||
regex: True
|
||||
- coalesce: True
|
||||
|
||||
The mask list can contain the following events (the default mask is create,
|
||||
delete, and modify):
|
||||
|
@ -203,8 +213,11 @@ def beacon(config):
|
|||
affects all paths that are being watched. This is due to this option
|
||||
being at the Notifier level in pyinotify.
|
||||
'''
|
||||
_config = {}
|
||||
list(map(_config.update, config))
|
||||
|
||||
ret = []
|
||||
notifier = _get_notifier(config)
|
||||
notifier = _get_notifier(_config)
|
||||
wm = notifier._watch_manager
|
||||
|
||||
# Read in existing events
|
||||
|
@ -219,11 +232,13 @@ def beacon(config):
|
|||
# Find the matching path in config
|
||||
path = event.path
|
||||
while path != '/':
|
||||
if path in config:
|
||||
if path in _config.get('files', {}):
|
||||
break
|
||||
path = os.path.dirname(path)
|
||||
|
||||
excludes = config[path].get('exclude', '')
|
||||
for path in _config.get('files', {}):
|
||||
excludes = _config['files'][path].get('exclude', '')
|
||||
|
||||
if excludes and isinstance(excludes, list):
|
||||
for exclude in excludes:
|
||||
if isinstance(exclude, dict):
|
||||
|
@ -257,9 +272,10 @@ def beacon(config):
|
|||
|
||||
# Update existing watches and add new ones
|
||||
# TODO: make the config handle more options
|
||||
for path in config:
|
||||
if isinstance(config[path], dict):
|
||||
mask = config[path].get('mask', DEFAULT_MASK)
|
||||
for path in _config.get('files', ()):
|
||||
|
||||
if isinstance(_config['files'][path], dict):
|
||||
mask = _config['files'][path].get('mask', DEFAULT_MASK)
|
||||
if isinstance(mask, list):
|
||||
r_mask = 0
|
||||
for sub in mask:
|
||||
|
@ -269,8 +285,8 @@ def beacon(config):
|
|||
else:
|
||||
r_mask = mask
|
||||
mask = r_mask
|
||||
rec = config[path].get('recurse', False)
|
||||
auto_add = config[path].get('auto_add', False)
|
||||
rec = _config['files'][path].get('recurse', False)
|
||||
auto_add = _config['files'][path].get('auto_add', False)
|
||||
else:
|
||||
mask = DEFAULT_MASK
|
||||
rec = False
|
||||
|
@ -287,7 +303,7 @@ def beacon(config):
|
|||
if update:
|
||||
wm.update_watch(wd, mask=mask, rec=rec, auto_add=auto_add)
|
||||
elif os.path.exists(path):
|
||||
excludes = config[path].get('exclude', '')
|
||||
excludes = _config['files'][path].get('exclude', '')
|
||||
excl = None
|
||||
if isinstance(excludes, list):
|
||||
excl = []
|
||||
|
|
|
@ -10,6 +10,7 @@ from __future__ import absolute_import
|
|||
import salt.utils
|
||||
import salt.utils.locales
|
||||
import salt.ext.six
|
||||
from salt.ext.six.moves import map
|
||||
|
||||
# Import third party libs
|
||||
try:
|
||||
|
@ -43,18 +44,21 @@ def _get_journal():
|
|||
return __context__['systemd.journald']
|
||||
|
||||
|
||||
def __validate__(config):
|
||||
def validate(config):
|
||||
'''
|
||||
Validate the beacon configuration
|
||||
'''
|
||||
# Configuration for journald beacon should be a list of dicts
|
||||
if not isinstance(config, dict):
|
||||
return False
|
||||
if not isinstance(config, list):
|
||||
return (False, 'Configuration for journald beacon must be a list.')
|
||||
else:
|
||||
for item in config:
|
||||
if not isinstance(config[item], dict):
|
||||
return False, ('Configuration for journald beacon must '
|
||||
'be a dictionary of dictionaries.')
|
||||
_config = {}
|
||||
list(map(_config.update, config))
|
||||
|
||||
for name in _config.get('services', {}):
|
||||
if not isinstance(_config['services'][name], dict):
|
||||
return False, ('Services configuration for journald beacon '
|
||||
'must be a list of dictionaries.')
|
||||
return True, 'Valid beacon configuration'
|
||||
|
||||
|
||||
|
@ -69,25 +73,31 @@ def beacon(config):
|
|||
|
||||
beacons:
|
||||
journald:
|
||||
sshd:
|
||||
SYSLOG_IDENTIFIER: sshd
|
||||
PRIORITY: 6
|
||||
- services:
|
||||
sshd:
|
||||
SYSLOG_IDENTIFIER: sshd
|
||||
PRIORITY: 6
|
||||
'''
|
||||
ret = []
|
||||
journal = _get_journal()
|
||||
|
||||
_config = {}
|
||||
list(map(_config.update, config))
|
||||
|
||||
while True:
|
||||
cur = journal.get_next()
|
||||
if not cur:
|
||||
break
|
||||
for name in config:
|
||||
|
||||
for name in _config.get('services', {}):
|
||||
n_flag = 0
|
||||
for key in config[name]:
|
||||
for key in _config['services'][name]:
|
||||
if isinstance(key, salt.ext.six.string_types):
|
||||
key = salt.utils.locales.sdecode(key)
|
||||
if key in cur:
|
||||
if config[name][key] == cur[key]:
|
||||
if _config['services'][name][key] == cur[key]:
|
||||
n_flag += 1
|
||||
if n_flag == len(config[name]):
|
||||
if n_flag == len(_config['services'][name]):
|
||||
# Match!
|
||||
sub = salt.utils.simple_types_filter(cur)
|
||||
sub.update({'tag': name})
|
||||
|
|
|
@ -10,6 +10,7 @@ import os
|
|||
|
||||
# Import Salt libs
|
||||
import salt.utils.platform
|
||||
from salt.ext.six.moves import map
|
||||
|
||||
# Import Py3 compat
|
||||
from salt.ext.six.moves import zip
|
||||
|
@ -28,7 +29,7 @@ def __virtual__():
|
|||
return __virtualname__
|
||||
|
||||
|
||||
def __validate__(config):
|
||||
def validate(config):
|
||||
'''
|
||||
Validate the beacon configuration
|
||||
'''
|
||||
|
@ -37,25 +38,26 @@ def __validate__(config):
|
|||
if not isinstance(config, list):
|
||||
return False, ('Configuration for load beacon must be a list.')
|
||||
else:
|
||||
for config_item in config:
|
||||
if not isinstance(config_item, dict):
|
||||
return False, ('Configuration for load beacon must '
|
||||
'be a list of dictionaries.')
|
||||
else:
|
||||
if not all(j in ['1m', '5m', '15m'] for j in config_item.keys()):
|
||||
return False, ('Configuration for load beacon must '
|
||||
'contain 1m, 5m or 15m items.')
|
||||
_config = {}
|
||||
list(map(_config.update, config))
|
||||
|
||||
if 'averages' not in _config:
|
||||
return False, ('Averages configuration is required'
|
||||
' for load beacon.')
|
||||
else:
|
||||
|
||||
if not any(j in ['1m', '5m', '15m'] for j
|
||||
in _config.get('averages', {})):
|
||||
return False, ('Averages configuration for load beacon '
|
||||
'must contain 1m, 5m or 15m items.')
|
||||
|
||||
for item in ['1m', '5m', '15m']:
|
||||
if item not in config_item:
|
||||
continue
|
||||
|
||||
if not isinstance(config_item[item], list):
|
||||
return False, ('Configuration for load beacon: '
|
||||
if not isinstance(_config['averages'][item], list):
|
||||
return False, ('Averages configuration for load beacon: '
|
||||
'1m, 5m and 15m items must be '
|
||||
'a list of two items.')
|
||||
else:
|
||||
if len(config_item[item]) != 2:
|
||||
if len(_config['averages'][item]) != 2:
|
||||
return False, ('Configuration for load beacon: '
|
||||
'1m, 5m and 15m items must be '
|
||||
'a list of two items.')
|
||||
|
@ -82,33 +84,37 @@ def beacon(config):
|
|||
|
||||
beacons:
|
||||
load:
|
||||
1m:
|
||||
- 0.0
|
||||
- 2.0
|
||||
5m:
|
||||
- 0.0
|
||||
- 1.5
|
||||
15m:
|
||||
- 0.1
|
||||
- 1.0
|
||||
emitatstartup: True
|
||||
onchangeonly: False
|
||||
- averages:
|
||||
1m:
|
||||
- 0.0
|
||||
- 2.0
|
||||
5m:
|
||||
- 0.0
|
||||
- 1.5
|
||||
15m:
|
||||
- 0.1
|
||||
- 1.0
|
||||
- emitatstartup: True
|
||||
- onchangeonly: False
|
||||
|
||||
'''
|
||||
log.trace('load beacon starting')
|
||||
|
||||
_config = {}
|
||||
list(map(_config.update, config))
|
||||
|
||||
# Default config if not present
|
||||
if 'emitatstartup' not in config:
|
||||
config['emitatstartup'] = True
|
||||
if 'onchangeonly' not in config:
|
||||
config['onchangeonly'] = False
|
||||
if 'emitatstartup' not in _config:
|
||||
_config['emitatstartup'] = True
|
||||
if 'onchangeonly' not in _config:
|
||||
_config['onchangeonly'] = False
|
||||
|
||||
ret = []
|
||||
avgs = os.getloadavg()
|
||||
avg_keys = ['1m', '5m', '15m']
|
||||
avg_dict = dict(zip(avg_keys, avgs))
|
||||
|
||||
if config['onchangeonly']:
|
||||
if _config['onchangeonly']:
|
||||
if not LAST_STATUS:
|
||||
for k in ['1m', '5m', '15m']:
|
||||
LAST_STATUS[k] = avg_dict[k]
|
||||
|
@ -120,27 +126,40 @@ def beacon(config):
|
|||
|
||||
# Check each entry for threshold
|
||||
for k in ['1m', '5m', '15m']:
|
||||
if k in config:
|
||||
if config['onchangeonly']:
|
||||
# Emit if current is more that threshold and old value less that threshold
|
||||
if float(avg_dict[k]) > float(config[k][1]) and float(LAST_STATUS[k]) < float(config[k][1]):
|
||||
log.debug('Emit because {0} > {1} and last was {2}'.format(float(avg_dict[k]), float(config[k][1]), float(LAST_STATUS[k])))
|
||||
if k in _config.get('averages', {}):
|
||||
if _config['onchangeonly']:
|
||||
# Emit if current is more that threshold and old value less
|
||||
# that threshold
|
||||
if float(avg_dict[k]) > float(_config['averages'][k][1]) and \
|
||||
float(LAST_STATUS[k]) < float(_config['averages'][k][1]):
|
||||
log.debug('Emit because {0} > {1} and last was '
|
||||
'{2}'.format(float(avg_dict[k]),
|
||||
float(_config['averages'][k][1]),
|
||||
float(LAST_STATUS[k])))
|
||||
send_beacon = True
|
||||
break
|
||||
# Emit if current is less that threshold and old value more that threshold
|
||||
if float(avg_dict[k]) < float(config[k][0]) and float(LAST_STATUS[k]) > float(config[k][0]):
|
||||
log.debug('Emit because {0} < {1} and last was {2}'.format(float(avg_dict[k]), float(config[k][0]), float(LAST_STATUS[k])))
|
||||
# Emit if current is less that threshold and old value more
|
||||
# that threshold
|
||||
if float(avg_dict[k]) < float(_config['averages'][k][0]) and \
|
||||
float(LAST_STATUS[k]) > float(_config['averages'][k][0]):
|
||||
log.debug('Emit because {0} < {1} and last was'
|
||||
'{2}'.format(float(avg_dict[k]),
|
||||
float(_config['averages'][k][0]),
|
||||
float(LAST_STATUS[k])))
|
||||
send_beacon = True
|
||||
break
|
||||
else:
|
||||
# Emit no matter LAST_STATUS
|
||||
if float(avg_dict[k]) < float(config[k][0]) or \
|
||||
float(avg_dict[k]) > float(config[k][1]):
|
||||
log.debug('Emit because {0} < {1} or > {2}'.format(float(avg_dict[k]), float(config[k][0]), float(config[k][1])))
|
||||
if float(avg_dict[k]) < float(_config['averages'][k][0]) or \
|
||||
float(avg_dict[k]) > float(_config['averages'][k][1]):
|
||||
log.debug('Emit because {0} < {1} or > '
|
||||
'{2}'.format(float(avg_dict[k]),
|
||||
float(_config['averages'][k][0]),
|
||||
float(_config['averages'][k][1])))
|
||||
send_beacon = True
|
||||
break
|
||||
|
||||
if config['onchangeonly']:
|
||||
if _config['onchangeonly']:
|
||||
for k in ['1m', '5m', '15m']:
|
||||
LAST_STATUS[k] = avg_dict[k]
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ import logging
|
|||
# Import salt libs
|
||||
import salt.utils.files
|
||||
import salt.utils.platform
|
||||
from salt.ext.six.moves import map
|
||||
|
||||
|
||||
try:
|
||||
|
@ -48,13 +49,20 @@ def _get_loc():
|
|||
return __context__[LOC_KEY]
|
||||
|
||||
|
||||
def __validate__(config):
|
||||
def validate(config):
|
||||
'''
|
||||
Validate the beacon configuration
|
||||
'''
|
||||
_config = {}
|
||||
list(map(_config.update, config))
|
||||
|
||||
# Configuration for log beacon should be a list of dicts
|
||||
if not isinstance(config, dict):
|
||||
return False, ('Configuration for log beacon must be a dictionary.')
|
||||
if not isinstance(config, list):
|
||||
return False, ('Configuration for log beacon must be a list.')
|
||||
|
||||
if 'file' not in _config:
|
||||
return False, ('Configuration for log beacon '
|
||||
'must contain file option.')
|
||||
return True, 'Valid beacon configuration'
|
||||
|
||||
|
||||
|
@ -67,20 +75,24 @@ def beacon(config):
|
|||
|
||||
beacons:
|
||||
log:
|
||||
file: <path>
|
||||
<tag>:
|
||||
regex: <pattern>
|
||||
- file: <path>
|
||||
- tags:
|
||||
<tag>:
|
||||
regex: <pattern>
|
||||
'''
|
||||
_config = {}
|
||||
list(map(_config.update, config))
|
||||
|
||||
ret = []
|
||||
|
||||
if 'file' not in config:
|
||||
if 'file' not in _config:
|
||||
event = SKEL.copy()
|
||||
event['tag'] = 'global'
|
||||
event['error'] = 'file not defined in config'
|
||||
ret.append(event)
|
||||
return ret
|
||||
|
||||
with salt.utils.files.fopen(config['file'], 'r') as fp_:
|
||||
with salt.utils.files.fopen(_config['file'], 'r') as fp_:
|
||||
loc = __context__.get(LOC_KEY, 0)
|
||||
if loc == 0:
|
||||
fp_.seek(0, 2)
|
||||
|
@ -92,16 +104,17 @@ def beacon(config):
|
|||
fp_.seek(loc)
|
||||
|
||||
txt = fp_.read()
|
||||
log.info('txt {}'.format(txt))
|
||||
|
||||
d = {}
|
||||
for tag in config:
|
||||
if 'regex' not in config[tag]:
|
||||
for tag in _config.get('tags', {}):
|
||||
if 'regex' not in _config['tags'][tag]:
|
||||
continue
|
||||
if len(config[tag]['regex']) < 1:
|
||||
if len(_config['tags'][tag]['regex']) < 1:
|
||||
continue
|
||||
try:
|
||||
d[tag] = re.compile(r'{0}'.format(config[tag]['regex']))
|
||||
except Exception:
|
||||
d[tag] = re.compile(r'{0}'.format(_config['tags'][tag]['regex']))
|
||||
except Exception as e:
|
||||
event = SKEL.copy()
|
||||
event['tag'] = tag
|
||||
event['error'] = 'bad regex'
|
||||
|
|
|
@ -11,6 +11,7 @@ Beacon to monitor memory usage.
|
|||
from __future__ import absolute_import
|
||||
import logging
|
||||
import re
|
||||
from salt.ext.six.moves import map
|
||||
|
||||
# Import Third Party Libs
|
||||
try:
|
||||
|
@ -31,14 +32,22 @@ def __virtual__():
|
|||
return __virtualname__
|
||||
|
||||
|
||||
def __validate__(config):
|
||||
def validate(config):
|
||||
'''
|
||||
Validate the beacon configuration
|
||||
'''
|
||||
# Configuration for memusage beacon should be a list of dicts
|
||||
if not isinstance(config, dict):
|
||||
if not isinstance(config, list):
|
||||
return False, ('Configuration for memusage '
|
||||
'beacon must be a dictionary.')
|
||||
'beacon must be a list.')
|
||||
else:
|
||||
_config = {}
|
||||
list(map(_config.update, config))
|
||||
|
||||
if 'percent' not in _config:
|
||||
return False, ('Configuration for memusage beacon '
|
||||
'requires percent.')
|
||||
|
||||
return True, 'Valid beacon configuration'
|
||||
|
||||
|
||||
|
@ -46,7 +55,8 @@ def beacon(config):
|
|||
'''
|
||||
Monitor the memory usage of the minion
|
||||
|
||||
Specify thresholds for percent used and only emit a beacon if it is exceeded.
|
||||
Specify thresholds for percent used and only emit a beacon
|
||||
if it is exceeded.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
|
@ -55,15 +65,17 @@ def beacon(config):
|
|||
- percent: 63%
|
||||
'''
|
||||
ret = []
|
||||
for memusage in config:
|
||||
mount = memusage.keys()[0]
|
||||
_current_usage = psutil.virtual_memory()
|
||||
|
||||
current_usage = _current_usage.percent
|
||||
monitor_usage = memusage[mount]
|
||||
if '%' in monitor_usage:
|
||||
monitor_usage = re.sub('%', '', monitor_usage)
|
||||
monitor_usage = float(monitor_usage)
|
||||
if current_usage >= monitor_usage:
|
||||
ret.append({'memusage': current_usage})
|
||||
_config = {}
|
||||
list(map(_config.update, config))
|
||||
|
||||
_current_usage = psutil.virtual_memory()
|
||||
|
||||
current_usage = _current_usage.percent
|
||||
monitor_usage = _config['percent']
|
||||
if '%' in monitor_usage:
|
||||
monitor_usage = re.sub('%', '', monitor_usage)
|
||||
monitor_usage = float(monitor_usage)
|
||||
if current_usage >= monitor_usage:
|
||||
ret.append({'memusage': current_usage})
|
||||
return ret
|
||||
|
|
|
@ -16,6 +16,9 @@ try:
|
|||
HAS_PSUTIL = True
|
||||
except ImportError:
|
||||
HAS_PSUTIL = False
|
||||
|
||||
from salt.ext.six.moves import map
|
||||
|
||||
# pylint: enable=import-error
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
@ -45,7 +48,7 @@ def __virtual__():
|
|||
return __virtualname__
|
||||
|
||||
|
||||
def __validate__(config):
|
||||
def validate(config):
|
||||
'''
|
||||
Validate the beacon configuration
|
||||
'''
|
||||
|
@ -57,15 +60,19 @@ def __validate__(config):
|
|||
]
|
||||
|
||||
# Configuration for load beacon should be a list of dicts
|
||||
if not isinstance(config, dict):
|
||||
return False, ('Configuration for load beacon must be a dictionary.')
|
||||
if not isinstance(config, list):
|
||||
return False, ('Configuration for network_info beacon must be a list.')
|
||||
else:
|
||||
for item in config:
|
||||
if not isinstance(config[item], dict):
|
||||
return False, ('Configuration for load beacon must '
|
||||
'be a dictionary of dictionaries.')
|
||||
|
||||
_config = {}
|
||||
list(map(_config.update, config))
|
||||
|
||||
for item in _config.get('interfaces', {}):
|
||||
if not isinstance(_config['interfaces'][item], dict):
|
||||
return False, ('Configuration for network_info beacon must '
|
||||
'be a list of dictionaries.')
|
||||
else:
|
||||
if not any(j in VALID_ITEMS for j in config[item]):
|
||||
if not any(j in VALID_ITEMS for j in _config['interfaces'][item]):
|
||||
return False, ('Invalid configuration item in '
|
||||
'Beacon configuration.')
|
||||
return True, 'Valid beacon configuration'
|
||||
|
@ -86,16 +93,17 @@ def beacon(config):
|
|||
|
||||
beacons:
|
||||
network_info:
|
||||
- eth0:
|
||||
type: equal
|
||||
bytes_sent: 100000
|
||||
bytes_recv: 100000
|
||||
packets_sent: 100000
|
||||
packets_recv: 100000
|
||||
errin: 100
|
||||
errout: 100
|
||||
dropin: 100
|
||||
dropout: 100
|
||||
- interfaces:
|
||||
eth0:
|
||||
type: equal
|
||||
bytes_sent: 100000
|
||||
bytes_recv: 100000
|
||||
packets_sent: 100000
|
||||
packets_recv: 100000
|
||||
errin: 100
|
||||
errout: 100
|
||||
dropin: 100
|
||||
dropout: 100
|
||||
|
||||
Emit beacon when any values are greater
|
||||
than configured values.
|
||||
|
@ -104,46 +112,53 @@ def beacon(config):
|
|||
|
||||
beacons:
|
||||
network_info:
|
||||
- eth0:
|
||||
type: greater
|
||||
bytes_sent: 100000
|
||||
bytes_recv: 100000
|
||||
packets_sent: 100000
|
||||
packets_recv: 100000
|
||||
errin: 100
|
||||
errout: 100
|
||||
dropin: 100
|
||||
dropout: 100
|
||||
- interfaces:
|
||||
eth0:
|
||||
type: greater
|
||||
bytes_sent: 100000
|
||||
bytes_recv: 100000
|
||||
packets_sent: 100000
|
||||
packets_recv: 100000
|
||||
errin: 100
|
||||
errout: 100
|
||||
dropin: 100
|
||||
dropout: 100
|
||||
|
||||
|
||||
'''
|
||||
ret = []
|
||||
|
||||
_config = {}
|
||||
list(map(_config.update, config))
|
||||
|
||||
log.debug('psutil.net_io_counters {}'.format(psutil.net_io_counters))
|
||||
|
||||
_stats = psutil.net_io_counters(pernic=True)
|
||||
|
||||
for interface_config in config:
|
||||
interface = interface_config.keys()[0]
|
||||
log.debug('_stats {}'.format(_stats))
|
||||
for interface in _config.get('interfaces', {}):
|
||||
if interface in _stats:
|
||||
interface_config = _config['interfaces'][interface]
|
||||
_if_stats = _stats[interface]
|
||||
_diff = False
|
||||
for attr in __attrs:
|
||||
if attr in interface_config[interface]:
|
||||
if 'type' in interface_config[interface] and \
|
||||
interface_config[interface]['type'] == 'equal':
|
||||
if attr in interface_config:
|
||||
if 'type' in interface_config and \
|
||||
interface_config['type'] == 'equal':
|
||||
if getattr(_if_stats, attr, None) == \
|
||||
int(interface_config[interface][attr]):
|
||||
int(interface_config[attr]):
|
||||
_diff = True
|
||||
elif 'type' in interface_config[interface] and \
|
||||
interface_config[interface]['type'] == 'greater':
|
||||
elif 'type' in interface_config and \
|
||||
interface_config['type'] == 'greater':
|
||||
if getattr(_if_stats, attr, None) > \
|
||||
int(interface_config[interface][attr]):
|
||||
int(interface_config[attr]):
|
||||
_diff = True
|
||||
else:
|
||||
log.debug('attr {}'.format(getattr(_if_stats,
|
||||
attr, None)))
|
||||
else:
|
||||
if getattr(_if_stats, attr, None) == \
|
||||
int(interface_config[interface][attr]):
|
||||
int(interface_config[attr]):
|
||||
_diff = True
|
||||
if _diff:
|
||||
ret.append({'interface': interface,
|
||||
|
|
|
@ -18,6 +18,8 @@ import ast
|
|||
import re
|
||||
import salt.loader
|
||||
import logging
|
||||
from salt.ext.six.moves import map
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
__virtual_name__ = 'network_settings'
|
||||
|
@ -45,22 +47,23 @@ def __virtual__():
|
|||
return False
|
||||
|
||||
|
||||
def __validate__(config):
|
||||
def validate(config):
|
||||
'''
|
||||
Validate the beacon configuration
|
||||
'''
|
||||
if not isinstance(config, dict):
|
||||
if not isinstance(config, list):
|
||||
return False, ('Configuration for network_settings '
|
||||
'beacon must be a dictionary.')
|
||||
'beacon must be a list.')
|
||||
else:
|
||||
for item in config:
|
||||
if item == 'coalesce':
|
||||
continue
|
||||
if not isinstance(config[item], dict):
|
||||
return False, ('Configuration for network_settings beacon must be a '
|
||||
'dictionary of dictionaries.')
|
||||
_config = {}
|
||||
list(map(_config.update, config))
|
||||
|
||||
for item in _config.get('interfaces', {}):
|
||||
if not isinstance(_config['interfaces'][item], dict):
|
||||
return False, ('Configuration for network_settings beacon '
|
||||
' must be a list of dictionaries.')
|
||||
else:
|
||||
if not all(j in ATTRS for j in config[item]):
|
||||
if not all(j in ATTRS for j in _config['interfaces'][item]):
|
||||
return False, ('Invalid configuration item in Beacon '
|
||||
'configuration.')
|
||||
return True, 'Valid beacon configuration'
|
||||
|
@ -75,9 +78,10 @@ def _copy_interfaces_info(interfaces):
|
|||
for interface in interfaces:
|
||||
_interface_attrs_cpy = set()
|
||||
for attr in ATTRS:
|
||||
attr_dict = Hashabledict()
|
||||
attr_dict[attr] = repr(interfaces[interface][attr])
|
||||
_interface_attrs_cpy.add(attr_dict)
|
||||
if attr in interfaces[interface]:
|
||||
attr_dict = Hashabledict()
|
||||
attr_dict[attr] = repr(interfaces[interface][attr])
|
||||
_interface_attrs_cpy.add(attr_dict)
|
||||
ret[interface] = _interface_attrs_cpy
|
||||
|
||||
return ret
|
||||
|
@ -89,8 +93,8 @@ def beacon(config):
|
|||
|
||||
By default, the beacon will emit when there is a value change on one of the
|
||||
settings on watch. The config also support the onvalue parameter for each
|
||||
setting, which instruct the beacon to only emit if the setting changed to the
|
||||
value defined.
|
||||
setting, which instruct the beacon to only emit if the setting changed to
|
||||
the value defined.
|
||||
|
||||
Example Config
|
||||
|
||||
|
@ -98,12 +102,13 @@ def beacon(config):
|
|||
|
||||
beacons:
|
||||
network_settings:
|
||||
eth0:
|
||||
ipaddr:
|
||||
promiscuity:
|
||||
onvalue: 1
|
||||
eth1:
|
||||
linkmode:
|
||||
- interfaces:
|
||||
- eth0:
|
||||
ipaddr:
|
||||
promiscuity:
|
||||
onvalue: 1
|
||||
- eth1:
|
||||
linkmode:
|
||||
|
||||
The config above will check for value changes on eth0 ipaddr and eth1 linkmode. It will also
|
||||
emit if the promiscuity value changes to 1.
|
||||
|
@ -118,12 +123,16 @@ def beacon(config):
|
|||
|
||||
beacons:
|
||||
network_settings:
|
||||
coalesce: True
|
||||
eth0:
|
||||
ipaddr:
|
||||
promiscuity:
|
||||
- coalesce: True
|
||||
- interfaces:
|
||||
- eth0:
|
||||
ipaddr:
|
||||
promiscuity:
|
||||
|
||||
'''
|
||||
_config = {}
|
||||
list(map(_config.update, config))
|
||||
|
||||
ret = []
|
||||
interfaces = []
|
||||
expanded_config = {}
|
||||
|
@ -137,45 +146,51 @@ def beacon(config):
|
|||
if not LAST_STATS:
|
||||
LAST_STATS = _stats
|
||||
|
||||
if 'coalesce' in config and config['coalesce']:
|
||||
if 'coalesce' in _config and _config['coalesce']:
|
||||
coalesce = True
|
||||
changes = {}
|
||||
|
||||
log.debug('_stats {}'.format(_stats))
|
||||
# Get list of interfaces included in config that are registered in the
|
||||
# system, including interfaces defined by wildcards (eth*, wlan*)
|
||||
for item in config:
|
||||
if item == 'coalesce':
|
||||
continue
|
||||
if item in _stats:
|
||||
interfaces.append(item)
|
||||
for interface in _config.get('interfaces', {}):
|
||||
if interface in _stats:
|
||||
interfaces.append(interface)
|
||||
else:
|
||||
# No direct match, try with * wildcard regexp
|
||||
interface_regexp = item.replace('*', '[0-9]+')
|
||||
interface_regexp = interface.replace('*', '[0-9]+')
|
||||
for interface in _stats:
|
||||
match = re.search(interface_regexp, interface)
|
||||
if match:
|
||||
interfaces.append(match.group())
|
||||
expanded_config[match.group()] = config[item]
|
||||
expanded_config[match.group()] = config['interfaces'][interface]
|
||||
|
||||
if expanded_config:
|
||||
config.update(expanded_config)
|
||||
|
||||
# config updated so update _config
|
||||
list(map(_config.update, config))
|
||||
|
||||
log.debug('interfaces {}'.format(interfaces))
|
||||
for interface in interfaces:
|
||||
_send_event = False
|
||||
_diff_stats = _stats[interface] - LAST_STATS[interface]
|
||||
_ret_diff = {}
|
||||
interface_config = _config['interfaces'][interface]
|
||||
|
||||
log.debug('_diff_stats {}'.format(_diff_stats))
|
||||
if _diff_stats:
|
||||
_diff_stats_dict = {}
|
||||
LAST_STATS[interface] = _stats[interface]
|
||||
|
||||
for item in _diff_stats:
|
||||
_diff_stats_dict.update(item)
|
||||
for attr in config[interface]:
|
||||
for attr in interface_config:
|
||||
if attr in _diff_stats_dict:
|
||||
config_value = None
|
||||
if config[interface][attr] and 'onvalue' in config[interface][attr]:
|
||||
config_value = config[interface][attr]['onvalue']
|
||||
if interface_config[attr] and \
|
||||
'onvalue' in interface_config[attr]:
|
||||
config_value = interface_config[attr]['onvalue']
|
||||
new_value = ast.literal_eval(_diff_stats_dict[attr])
|
||||
if not config_value or config_value == new_value:
|
||||
_send_event = True
|
||||
|
@ -185,7 +200,9 @@ def beacon(config):
|
|||
if coalesce:
|
||||
changes[interface] = _ret_diff
|
||||
else:
|
||||
ret.append({'tag': interface, 'interface': interface, 'change': _ret_diff})
|
||||
ret.append({'tag': interface,
|
||||
'interface': interface,
|
||||
'change': _ret_diff})
|
||||
|
||||
if coalesce and changes:
|
||||
grains_info = salt.loader.grains(__opts__, True)
|
||||
|
|
|
@ -21,15 +21,25 @@ def __virtual__():
|
|||
return __virtualname__ if 'pkg.upgrade_available' in __salt__ else False
|
||||
|
||||
|
||||
def __validate__(config):
|
||||
def validate(config):
|
||||
'''
|
||||
Validate the beacon configuration
|
||||
'''
|
||||
# Configuration for pkg beacon should be a list
|
||||
if not isinstance(config, dict):
|
||||
return False, ('Configuration for pkg beacon must be a dictionary.')
|
||||
if 'pkgs' not in config:
|
||||
return False, ('Configuration for pkg beacon requires list of pkgs.')
|
||||
if not isinstance(config, list):
|
||||
return False, ('Configuration for pkg beacon must be a list.')
|
||||
|
||||
# Configuration for pkg beacon should contain pkgs
|
||||
pkgs_found = False
|
||||
pkgs_not_list = False
|
||||
for config_item in config:
|
||||
if 'pkgs' in config_item:
|
||||
pkgs_found = True
|
||||
if isinstance(config_item['pkgs'], list):
|
||||
pkgs_not_list = True
|
||||
|
||||
if not pkgs_found or not pkgs_not_list:
|
||||
return False, 'Configuration for pkg beacon requires list of pkgs.'
|
||||
return True, 'Valid beacon configuration'
|
||||
|
||||
|
||||
|
@ -48,14 +58,16 @@ def beacon(config):
|
|||
- refresh: True
|
||||
'''
|
||||
ret = []
|
||||
_validate = __validate__(config)
|
||||
if not _validate[0]:
|
||||
return ret
|
||||
|
||||
_refresh = False
|
||||
if 'refresh' in config and config['refresh']:
|
||||
_refresh = True
|
||||
for pkg in config['pkgs']:
|
||||
pkgs = []
|
||||
for config_item in config:
|
||||
if 'pkgs' in config_item:
|
||||
pkgs += config_item['pkgs']
|
||||
if 'refresh' in config and config['refresh']:
|
||||
_refresh = True
|
||||
|
||||
for pkg in pkgs:
|
||||
_installed = __salt__['pkg.version'](pkg)
|
||||
_latest = __salt__['pkg.latest_version'](pkg, refresh=_refresh)
|
||||
if _installed and _latest:
|
||||
|
|
|
@ -15,6 +15,7 @@ import logging
|
|||
|
||||
# Import salt libs
|
||||
import salt.utils.http
|
||||
from salt.ext.six.moves import map
|
||||
|
||||
# Important: If used with salt-proxy
|
||||
# this is required for the beacon to load!!!
|
||||
|
@ -33,12 +34,12 @@ def __virtual__():
|
|||
return True
|
||||
|
||||
|
||||
def __validate__(config):
|
||||
def validate(config):
|
||||
'''
|
||||
Validate the beacon configuration
|
||||
'''
|
||||
if not isinstance(config, dict):
|
||||
return False, ('Configuration for rest_example beacon must be a dictionary.')
|
||||
if not isinstance(config, list):
|
||||
return False, ('Configuration for proxy_example beacon must be a list.')
|
||||
return True, 'Valid beacon configuration'
|
||||
|
||||
|
||||
|
@ -51,7 +52,7 @@ def beacon(config):
|
|||
|
||||
beacons:
|
||||
proxy_example:
|
||||
endpoint: beacon
|
||||
- endpoint: beacon
|
||||
'''
|
||||
# Important!!!
|
||||
# Although this toy example makes an HTTP call
|
||||
|
@ -59,8 +60,11 @@ def beacon(config):
|
|||
# please be advised that doing CPU or IO intensive
|
||||
# operations in this method will cause the beacon loop
|
||||
# to block.
|
||||
_config = {}
|
||||
list(map(_config.update, config))
|
||||
|
||||
beacon_url = '{0}{1}'.format(__opts__['proxy']['url'],
|
||||
config['endpoint'])
|
||||
_config['endpoint'])
|
||||
ret = salt.utils.http.query(beacon_url,
|
||||
decode_type='json',
|
||||
decode=True)
|
||||
|
|
|
@ -14,6 +14,9 @@ try:
|
|||
HAS_PSUTIL = True
|
||||
except ImportError:
|
||||
HAS_PSUTIL = False
|
||||
|
||||
from salt.ext.six.moves import map
|
||||
|
||||
# pylint: enable=import-error
|
||||
|
||||
log = logging.getLogger(__name__) # pylint: disable=invalid-name
|
||||
|
@ -23,17 +26,27 @@ __virtualname__ = 'ps'
|
|||
|
||||
def __virtual__():
|
||||
if not HAS_PSUTIL:
|
||||
return (False, 'cannot load network_info beacon: psutil not available')
|
||||
return (False, 'cannot load ps beacon: psutil not available')
|
||||
return __virtualname__
|
||||
|
||||
|
||||
def __validate__(config):
|
||||
def validate(config):
|
||||
'''
|
||||
Validate the beacon configuration
|
||||
'''
|
||||
# Configuration for ps beacon should be a list of dicts
|
||||
if not isinstance(config, dict):
|
||||
return False, ('Configuration for ps beacon must be a dictionary.')
|
||||
if not isinstance(config, list):
|
||||
return False, ('Configuration for ps beacon must be a list.')
|
||||
else:
|
||||
_config = {}
|
||||
list(map(_config.update, config))
|
||||
|
||||
if 'processes' not in _config:
|
||||
return False, ('Configuration for ps beacon requires processes.')
|
||||
else:
|
||||
if not isinstance(_config['processes'], dict):
|
||||
return False, ('Processes for ps beacon must be a dictionary.')
|
||||
|
||||
return True, 'Valid beacon configuration'
|
||||
|
||||
|
||||
|
@ -47,8 +60,9 @@ def beacon(config):
|
|||
|
||||
beacons:
|
||||
ps:
|
||||
- salt-master: running
|
||||
- mysql: stopped
|
||||
- processes:
|
||||
salt-master: running
|
||||
mysql: stopped
|
||||
|
||||
The config above sets up beacons to check that
|
||||
processes are running or stopped.
|
||||
|
@ -60,19 +74,21 @@ def beacon(config):
|
|||
if _name not in procs:
|
||||
procs.append(_name)
|
||||
|
||||
for entry in config:
|
||||
for process in entry:
|
||||
ret_dict = {}
|
||||
if entry[process] == 'running':
|
||||
if process in procs:
|
||||
ret_dict[process] = 'Running'
|
||||
ret.append(ret_dict)
|
||||
elif entry[process] == 'stopped':
|
||||
if process not in procs:
|
||||
ret_dict[process] = 'Stopped'
|
||||
ret.append(ret_dict)
|
||||
else:
|
||||
if process not in procs:
|
||||
ret_dict[process] = False
|
||||
ret.append(ret_dict)
|
||||
_config = {}
|
||||
list(map(_config.update, config))
|
||||
|
||||
for process in _config.get('processes', {}):
|
||||
ret_dict = {}
|
||||
if _config['processes'][process] == 'running':
|
||||
if process in procs:
|
||||
ret_dict[process] = 'Running'
|
||||
ret.append(ret_dict)
|
||||
elif _config['processes'][process] == 'stopped':
|
||||
if process not in procs:
|
||||
ret_dict[process] = 'Stopped'
|
||||
ret.append(ret_dict)
|
||||
else:
|
||||
if process not in procs:
|
||||
ret_dict[process] = False
|
||||
ret.append(ret_dict)
|
||||
return ret
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
# Import python libs
|
||||
from __future__ import absolute_import
|
||||
import logging
|
||||
from salt.ext.six.moves import map
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -20,9 +21,7 @@ def _run_proxy_processes(proxies):
|
|||
aren't running
|
||||
'''
|
||||
ret = []
|
||||
for prox_ in proxies:
|
||||
# prox_ is a dict
|
||||
proxy = prox_.keys()[0]
|
||||
for proxy in proxies:
|
||||
result = {}
|
||||
if not __salt__['salt_proxy.is_running'](proxy)['result']:
|
||||
__salt__['salt_proxy.configure_proxy'](proxy, start=True)
|
||||
|
@ -35,7 +34,29 @@ def _run_proxy_processes(proxies):
|
|||
return ret
|
||||
|
||||
|
||||
def beacon(proxies):
|
||||
def validate(config):
|
||||
'''
|
||||
Validate the beacon configuration
|
||||
'''
|
||||
# Configuration for adb beacon should be a dictionary with states array
|
||||
if not isinstance(config, list):
|
||||
log.info('Configuration for salt_proxy beacon must be a list.')
|
||||
return False, ('Configuration for salt_proxy beacon must be a list.')
|
||||
|
||||
else:
|
||||
_config = {}
|
||||
list(map(_config.update, config))
|
||||
|
||||
if 'proxies' not in _config:
|
||||
return False, ('Configuration for salt_proxy'
|
||||
' beacon requires proxies.')
|
||||
else:
|
||||
if not isinstance(_config['proxies'], dict):
|
||||
return False, ('Proxies for salt_proxy '
|
||||
'beacon must be a dictionary.')
|
||||
|
||||
|
||||
def beacon(config):
|
||||
'''
|
||||
Handle configured proxies
|
||||
|
||||
|
@ -43,9 +64,13 @@ def beacon(proxies):
|
|||
|
||||
beacons:
|
||||
salt_proxy:
|
||||
- p8000: {}
|
||||
- p8001: {}
|
||||
- proxies:
|
||||
p8000: {}
|
||||
p8001: {}
|
||||
'''
|
||||
log.trace('salt proxy beacon called')
|
||||
|
||||
return _run_proxy_processes(proxies)
|
||||
_config = {}
|
||||
list(map(_config.update, config))
|
||||
|
||||
return _run_proxy_processes(_config['proxies'])
|
||||
|
|
|
@ -11,6 +11,7 @@ of a Raspberry Pi.
|
|||
from __future__ import absolute_import
|
||||
import logging
|
||||
import re
|
||||
from salt.ext.six.moves import map
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -19,14 +20,22 @@ def __virtual__():
|
|||
return 'sensehat.get_pressure' in __salt__
|
||||
|
||||
|
||||
def __validate__(config):
|
||||
def validate(config):
|
||||
'''
|
||||
Validate the beacon configuration
|
||||
'''
|
||||
# Configuration for sensehat beacon should be a dict
|
||||
if not isinstance(config, dict):
|
||||
# Configuration for sensehat beacon should be a list
|
||||
if not isinstance(config, list):
|
||||
return False, ('Configuration for sensehat beacon '
|
||||
'must be a dictionary.')
|
||||
'must be a list.')
|
||||
else:
|
||||
_config = {}
|
||||
list(map(_config.update, config))
|
||||
|
||||
if 'sensors' not in _config:
|
||||
return False, ('Configuration for sensehat'
|
||||
' beacon requires sensors.')
|
||||
|
||||
return True, 'Valid beacon configuration'
|
||||
|
||||
|
||||
|
@ -48,10 +57,11 @@ def beacon(config):
|
|||
|
||||
beacons:
|
||||
sensehat:
|
||||
humidity: 70%
|
||||
temperature: [20, 40]
|
||||
temperature_from_pressure: 40
|
||||
pressure: 1500
|
||||
- sensors:
|
||||
humidity: 70%
|
||||
temperature: [20, 40]
|
||||
temperature_from_pressure: 40
|
||||
pressure: 1500
|
||||
'''
|
||||
ret = []
|
||||
min_default = {
|
||||
|
@ -60,13 +70,16 @@ def beacon(config):
|
|||
'temperature': '-273.15'
|
||||
}
|
||||
|
||||
for sensor in config:
|
||||
_config = {}
|
||||
list(map(_config.update, config))
|
||||
|
||||
for sensor in _config.get('sensors', {}):
|
||||
sensor_function = 'sensehat.get_{0}'.format(sensor)
|
||||
if sensor_function not in __salt__:
|
||||
log.error('No sensor for meassuring {0}. Skipping.'.format(sensor))
|
||||
continue
|
||||
|
||||
sensor_config = config[sensor]
|
||||
sensor_config = _config['sensors'][sensor]
|
||||
if isinstance(sensor_config, list):
|
||||
sensor_min = str(sensor_config[0])
|
||||
sensor_max = str(sensor_config[1])
|
||||
|
|
|
@ -9,19 +9,35 @@ from __future__ import absolute_import
|
|||
import os
|
||||
import logging
|
||||
import time
|
||||
from salt.ext.six.moves import map
|
||||
|
||||
log = logging.getLogger(__name__) # pylint: disable=invalid-name
|
||||
|
||||
LAST_STATUS = {}
|
||||
|
||||
__virtualname__ = 'service'
|
||||
|
||||
def __validate__(config):
|
||||
|
||||
def validate(config):
|
||||
'''
|
||||
Validate the beacon configuration
|
||||
'''
|
||||
# Configuration for service beacon should be a list of dicts
|
||||
if not isinstance(config, dict):
|
||||
return False, ('Configuration for service beacon must be a dictionary.')
|
||||
if not isinstance(config, list):
|
||||
return False, ('Configuration for service beacon must be a list.')
|
||||
else:
|
||||
_config = {}
|
||||
list(map(_config.update, config))
|
||||
|
||||
if 'services' not in _config:
|
||||
return False, ('Configuration for service beacon'
|
||||
' requires services.')
|
||||
else:
|
||||
for config_item in _config['services']:
|
||||
if not isinstance(_config['services'][config_item], dict):
|
||||
return False, ('Configuration for service beacon must '
|
||||
'be a list of dictionaries.')
|
||||
|
||||
return True, 'Valid beacon configuration'
|
||||
|
||||
|
||||
|
@ -35,8 +51,9 @@ def beacon(config):
|
|||
|
||||
beacons:
|
||||
service:
|
||||
salt-master:
|
||||
mysql:
|
||||
- services:
|
||||
salt-master:
|
||||
mysql:
|
||||
|
||||
The config above sets up beacons to check for
|
||||
the salt-master and mysql services.
|
||||
|
@ -79,14 +96,21 @@ def beacon(config):
|
|||
|
||||
beacons:
|
||||
service:
|
||||
nginx:
|
||||
onchangeonly: True
|
||||
delay: 30
|
||||
uncleanshutdown: /run/nginx.pid
|
||||
- services:
|
||||
nginx:
|
||||
onchangeonly: True
|
||||
delay: 30
|
||||
uncleanshutdown: /run/nginx.pid
|
||||
'''
|
||||
ret = []
|
||||
for service in config:
|
||||
_config = {}
|
||||
list(map(_config.update, config))
|
||||
|
||||
for service in _config.get('services', {}):
|
||||
ret_dict = {}
|
||||
|
||||
service_config = _config['services'][service]
|
||||
|
||||
ret_dict[service] = {'running': __salt__['service.status'](service)}
|
||||
ret_dict['service_name'] = service
|
||||
ret_dict['tag'] = service
|
||||
|
@ -95,40 +119,43 @@ def beacon(config):
|
|||
# If no options is given to the service, we fall back to the defaults
|
||||
# assign a False value to oncleanshutdown and onchangeonly. Those
|
||||
# key:values are then added to the service dictionary.
|
||||
if 'oncleanshutdown' not in config[service]:
|
||||
config[service]['oncleanshutdown'] = False
|
||||
if 'emitatstartup' not in config[service]:
|
||||
config[service]['emitatstartup'] = True
|
||||
if 'onchangeonly' not in config[service]:
|
||||
config[service]['onchangeonly'] = False
|
||||
if 'delay' not in config[service]:
|
||||
config[service]['delay'] = 0
|
||||
if not service_config:
|
||||
service_config = {}
|
||||
if 'oncleanshutdown' not in service_config:
|
||||
service_config['oncleanshutdown'] = False
|
||||
if 'emitatstartup' not in service_config:
|
||||
service_config['emitatstartup'] = True
|
||||
if 'onchangeonly' not in service_config:
|
||||
service_config['onchangeonly'] = False
|
||||
if 'delay' not in service_config:
|
||||
service_config['delay'] = 0
|
||||
|
||||
# We only want to report the nature of the shutdown
|
||||
# if the current running status is False
|
||||
# as well as if the config for the beacon asks for it
|
||||
if 'uncleanshutdown' in config[service] and not ret_dict[service]['running']:
|
||||
filename = config[service]['uncleanshutdown']
|
||||
if 'uncleanshutdown' in service_config and not ret_dict[service]['running']:
|
||||
filename = service_config['uncleanshutdown']
|
||||
ret_dict[service]['uncleanshutdown'] = True if os.path.exists(filename) else False
|
||||
if 'onchangeonly' in config[service] and config[service]['onchangeonly'] is True:
|
||||
if 'onchangeonly' in service_config and service_config['onchangeonly'] is True:
|
||||
if service not in LAST_STATUS:
|
||||
LAST_STATUS[service] = ret_dict[service]
|
||||
if config[service]['delay'] > 0:
|
||||
if service_config['delay'] > 0:
|
||||
LAST_STATUS[service]['time'] = currtime
|
||||
elif not config[service]['emitatstartup']:
|
||||
elif not service_config['emitatstartup']:
|
||||
continue
|
||||
else:
|
||||
ret.append(ret_dict)
|
||||
|
||||
if LAST_STATUS[service]['running'] != ret_dict[service]['running']:
|
||||
LAST_STATUS[service] = ret_dict[service]
|
||||
if config[service]['delay'] > 0:
|
||||
if service_config['delay'] > 0:
|
||||
LAST_STATUS[service]['time'] = currtime
|
||||
else:
|
||||
ret.append(ret_dict)
|
||||
|
||||
if 'time' in LAST_STATUS[service]:
|
||||
elapsedtime = int(round(currtime - LAST_STATUS[service]['time']))
|
||||
if elapsedtime > config[service]['delay']:
|
||||
if elapsedtime > service_config['delay']:
|
||||
del LAST_STATUS[service]['time']
|
||||
ret.append(ret_dict)
|
||||
else:
|
||||
|
|
|
@ -41,13 +41,13 @@ def _get_shells():
|
|||
return __context__['sh.shells']
|
||||
|
||||
|
||||
def __validate__(config):
|
||||
def validate(config):
|
||||
'''
|
||||
Validate the beacon configuration
|
||||
'''
|
||||
# Configuration for sh beacon should be a list of dicts
|
||||
if not isinstance(config, dict):
|
||||
return False, ('Configuration for sh beacon must be a dictionary.')
|
||||
if not isinstance(config, list):
|
||||
return False, ('Configuration for sh beacon must be a list.')
|
||||
return True, 'Valid beacon configuration'
|
||||
|
||||
|
||||
|
@ -58,7 +58,7 @@ def beacon(config):
|
|||
.. code-block:: yaml
|
||||
|
||||
beacons:
|
||||
sh: {}
|
||||
sh: []
|
||||
'''
|
||||
ret = []
|
||||
pkey = 'sh.vt'
|
||||
|
|
|
@ -15,7 +15,7 @@ the minion config:
|
|||
.. code-block:: yaml
|
||||
|
||||
beacons:
|
||||
status: {}
|
||||
status: []
|
||||
|
||||
By default, all of the information from the following execution module
|
||||
functions will be returned:
|
||||
|
@ -103,12 +103,12 @@ log = logging.getLogger(__name__)
|
|||
__virtualname__ = 'status'
|
||||
|
||||
|
||||
def __validate__(config):
|
||||
def validate(config):
|
||||
'''
|
||||
Validate the the config is a dict
|
||||
'''
|
||||
if not isinstance(config, dict):
|
||||
return False, ('Configuration for status beacon must be a dictionary.')
|
||||
if not isinstance(config, list):
|
||||
return False, ('Configuration for status beacon must be a list.')
|
||||
return True, 'Valid beacon configuration'
|
||||
|
||||
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
Beacon to emit Telegram messages
|
||||
|
||||
Requires the python-telegram-bot library
|
||||
|
||||
'''
|
||||
|
||||
# Import Python libs
|
||||
from __future__ import absolute_import
|
||||
import logging
|
||||
from salt.ext.six.moves import map
|
||||
|
||||
# Import 3rd Party libs
|
||||
try:
|
||||
|
@ -28,20 +32,23 @@ def __virtual__():
|
|||
return False
|
||||
|
||||
|
||||
def __validate__(config):
|
||||
def validate(config):
|
||||
'''
|
||||
Validate the beacon configuration
|
||||
'''
|
||||
if not isinstance(config, dict):
|
||||
if not isinstance(config, list):
|
||||
return False, ('Configuration for telegram_bot_msg '
|
||||
'beacon must be a dictionary.')
|
||||
'beacon must be a list.')
|
||||
|
||||
if not all(config.get(required_config)
|
||||
_config = {}
|
||||
list(map(_config.update, config))
|
||||
|
||||
if not all(_config.get(required_config)
|
||||
for required_config in ['token', 'accept_from']):
|
||||
return False, ('Not all required configuration for '
|
||||
'telegram_bot_msg are set.')
|
||||
|
||||
if not isinstance(config.get('accept_from'), list):
|
||||
if not isinstance(_config.get('accept_from'), list):
|
||||
return False, ('Configuration for telegram_bot_msg, '
|
||||
'accept_from must be a list of usernames.')
|
||||
|
||||
|
@ -57,18 +64,22 @@ def beacon(config):
|
|||
|
||||
beacons:
|
||||
telegram_bot_msg:
|
||||
token: "<bot access token>"
|
||||
accept_from:
|
||||
- token: "<bot access token>"
|
||||
- accept_from:
|
||||
- "<valid username>"
|
||||
interval: 10
|
||||
- interval: 10
|
||||
|
||||
'''
|
||||
|
||||
_config = {}
|
||||
list(map(_config.update, config))
|
||||
|
||||
log.debug('telegram_bot_msg beacon starting')
|
||||
ret = []
|
||||
output = {}
|
||||
output['msgs'] = []
|
||||
|
||||
bot = telegram.Bot(config['token'])
|
||||
bot = telegram.Bot(_config['token'])
|
||||
updates = bot.get_updates(limit=100, timeout=0, network_delay=10)
|
||||
|
||||
log.debug('Num updates: {0}'.format(len(updates)))
|
||||
|
@ -83,7 +94,7 @@ def beacon(config):
|
|||
if update.update_id > latest_update_id:
|
||||
latest_update_id = update.update_id
|
||||
|
||||
if message.chat.username in config['accept_from']:
|
||||
if message.chat.username in _config['accept_from']:
|
||||
output['msgs'].append(message.to_dict())
|
||||
|
||||
# mark in the server that previous messages are processed
|
||||
|
|
|
@ -6,10 +6,17 @@ Beacon to emit Twilio text messages
|
|||
# Import Python libs
|
||||
from __future__ import absolute_import
|
||||
import logging
|
||||
from salt.ext.six.moves import map
|
||||
|
||||
# Import 3rd Party libs
|
||||
try:
|
||||
from twilio.rest import TwilioRestClient
|
||||
import twilio
|
||||
# Grab version, ensure elements are ints
|
||||
twilio_version = tuple([int(x) for x in twilio.__version_info__])
|
||||
if twilio_version > (5, ):
|
||||
from twilio.rest import Client as TwilioRestClient
|
||||
else:
|
||||
from twilio.rest import TwilioRestClient
|
||||
HAS_TWILIO = True
|
||||
except ImportError:
|
||||
HAS_TWILIO = False
|
||||
|
@ -26,14 +33,24 @@ def __virtual__():
|
|||
return False
|
||||
|
||||
|
||||
def __validate__(config):
|
||||
def validate(config):
|
||||
'''
|
||||
Validate the beacon configuration
|
||||
'''
|
||||
# Configuration for twilio_txt_msg beacon should be a list of dicts
|
||||
if not isinstance(config, dict):
|
||||
if not isinstance(config, list):
|
||||
return False, ('Configuration for twilio_txt_msg beacon '
|
||||
'must be a dictionary.')
|
||||
'must be a list.')
|
||||
else:
|
||||
_config = {}
|
||||
list(map(_config.update, config))
|
||||
|
||||
if not all(x in _config for x in ('account_sid',
|
||||
'auth_token',
|
||||
'twilio_number')):
|
||||
return False, ('Configuration for twilio_txt_msg beacon '
|
||||
'must contain account_sid, auth_token '
|
||||
'and twilio_number items.')
|
||||
return True, 'Valid beacon configuration'
|
||||
|
||||
|
||||
|
@ -46,20 +63,26 @@ def beacon(config):
|
|||
|
||||
beacons:
|
||||
twilio_txt_msg:
|
||||
account_sid: "<account sid>"
|
||||
auth_token: "<auth token>"
|
||||
twilio_number: "+15555555555"
|
||||
interval: 10
|
||||
- account_sid: "<account sid>"
|
||||
- auth_token: "<auth token>"
|
||||
- twilio_number: "+15555555555"
|
||||
- interval: 10
|
||||
|
||||
'''
|
||||
log.trace('twilio_txt_msg beacon starting')
|
||||
|
||||
_config = {}
|
||||
list(map(_config.update, config))
|
||||
|
||||
ret = []
|
||||
if not all([config['account_sid'], config['auth_token'], config['twilio_number']]):
|
||||
if not all([_config['account_sid'],
|
||||
_config['auth_token'],
|
||||
_config['twilio_number']]):
|
||||
return ret
|
||||
output = {}
|
||||
output['texts'] = []
|
||||
client = TwilioRestClient(config['account_sid'], config['auth_token'])
|
||||
messages = client.messages.list(to=config['twilio_number'])
|
||||
client = TwilioRestClient(_config['account_sid'], _config['auth_token'])
|
||||
messages = client.messages.list(to=_config['twilio_number'])
|
||||
log.trace('Num messages: {0}'.format(len(messages)))
|
||||
if len(messages) < 1:
|
||||
log.trace('Twilio beacon has no texts')
|
||||
|
|
|
@ -5,7 +5,7 @@ Beacon to fire events at login of users as registered in the wtmp file
|
|||
.. code-block:: yaml
|
||||
|
||||
beacons:
|
||||
wtmp: {}
|
||||
wtmp: []
|
||||
'''
|
||||
|
||||
# Import Python libs
|
||||
|
@ -55,13 +55,13 @@ def _get_loc():
|
|||
return __context__[LOC_KEY]
|
||||
|
||||
|
||||
def __validate__(config):
|
||||
def validate(config):
|
||||
'''
|
||||
Validate the beacon configuration
|
||||
'''
|
||||
# Configuration for wtmp beacon should be a list of dicts
|
||||
if not isinstance(config, dict):
|
||||
return False, ('Configuration for wtmp beacon must be a dictionary.')
|
||||
if not isinstance(config, list):
|
||||
return False, ('Configuration for wtmp beacon must be a list.')
|
||||
return True, 'Valid beacon configuration'
|
||||
|
||||
|
||||
|
@ -73,7 +73,7 @@ def beacon(config):
|
|||
.. code-block:: yaml
|
||||
|
||||
beacons:
|
||||
wtmp: {}
|
||||
wtmp: []
|
||||
'''
|
||||
ret = []
|
||||
with salt.utils.files.fopen(WTMP, 'rb') as fp_:
|
||||
|
|
|
@ -39,6 +39,7 @@ import salt.utils.files
|
|||
import salt.utils.minions
|
||||
import salt.utils.platform
|
||||
import salt.utils.verify
|
||||
import salt.utils.versions
|
||||
import salt.utils.jid
|
||||
import salt.syspaths as syspaths
|
||||
from salt.exceptions import (
|
||||
|
@ -314,7 +315,7 @@ class LocalClient(object):
|
|||
{'jid': '20131219215650131543', 'minions': ['jerry']}
|
||||
'''
|
||||
if u'expr_form' in kwargs:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
u'Fluorine',
|
||||
u'The target type should be passed using the \'tgt_type\' '
|
||||
u'argument instead of \'expr_form\'. Support for using '
|
||||
|
@ -378,7 +379,7 @@ class LocalClient(object):
|
|||
{'jid': '20131219215650131543', 'minions': ['jerry']}
|
||||
'''
|
||||
if u'expr_form' in kwargs:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
u'Fluorine',
|
||||
u'The target type should be passed using the \'tgt_type\' '
|
||||
u'argument instead of \'expr_form\'. Support for using '
|
||||
|
@ -435,7 +436,7 @@ class LocalClient(object):
|
|||
'20131219215921857715'
|
||||
'''
|
||||
if u'expr_form' in kwargs:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
u'Fluorine',
|
||||
u'The target type should be passed using the \'tgt_type\' '
|
||||
u'argument instead of \'expr_form\'. Support for using '
|
||||
|
@ -482,7 +483,7 @@ class LocalClient(object):
|
|||
{'jerry': True}
|
||||
'''
|
||||
if u'expr_form' in kwargs:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
u'Fluorine',
|
||||
u'The target type should be passed using the \'tgt_type\' '
|
||||
u'argument instead of \'expr_form\'. Support for using '
|
||||
|
@ -545,7 +546,7 @@ class LocalClient(object):
|
|||
{'stewart': {...}}
|
||||
'''
|
||||
if u'expr_form' in kwargs:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
u'Fluorine',
|
||||
u'The target type should be passed using the \'tgt_type\' '
|
||||
u'argument instead of \'expr_form\'. Support for using '
|
||||
|
@ -703,7 +704,7 @@ class LocalClient(object):
|
|||
function name.
|
||||
'''
|
||||
if u'expr_form' in kwargs:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
u'Fluorine',
|
||||
u'The target type should be passed using the \'tgt_type\' '
|
||||
u'argument instead of \'expr_form\'. Support for using '
|
||||
|
@ -772,7 +773,7 @@ class LocalClient(object):
|
|||
:returns: A generator
|
||||
'''
|
||||
if u'expr_form' in kwargs:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
u'Fluorine',
|
||||
u'The target type should be passed using the \'tgt_type\' '
|
||||
u'argument instead of \'expr_form\'. Support for using '
|
||||
|
@ -855,7 +856,7 @@ class LocalClient(object):
|
|||
{'stewart': {'ret': True}}
|
||||
'''
|
||||
if u'expr_form' in kwargs:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
u'Fluorine',
|
||||
u'The target type should be passed using the \'tgt_type\' '
|
||||
u'argument instead of \'expr_form\'. Support for using '
|
||||
|
@ -931,7 +932,7 @@ class LocalClient(object):
|
|||
{'stewart': {'ret': True}}
|
||||
'''
|
||||
if u'expr_form' in kwargs:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
u'Fluorine',
|
||||
u'The target type should be passed using the \'tgt_type\' '
|
||||
u'argument instead of \'expr_form\'. Support for using '
|
||||
|
@ -988,7 +989,7 @@ class LocalClient(object):
|
|||
Execute a salt command and return
|
||||
'''
|
||||
if u'expr_form' in kwargs:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
u'Fluorine',
|
||||
u'The target type should be passed using the \'tgt_type\' '
|
||||
u'argument instead of \'expr_form\'. Support for using '
|
||||
|
@ -1039,7 +1040,7 @@ class LocalClient(object):
|
|||
:returns: all of the information for the JID
|
||||
'''
|
||||
if u'expr_form' in kwargs:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
u'Fluorine',
|
||||
u'The target type should be passed using the \'tgt_type\' '
|
||||
u'argument instead of \'expr_form\'. Support for using '
|
||||
|
@ -1118,7 +1119,7 @@ class LocalClient(object):
|
|||
:returns: all of the information for the JID
|
||||
'''
|
||||
if u'expr_form' in kwargs:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
u'Fluorine',
|
||||
u'The target type should be passed using the \'tgt_type\' '
|
||||
u'argument instead of \'expr_form\'. Support for using '
|
||||
|
@ -1558,7 +1559,7 @@ class LocalClient(object):
|
|||
log.trace(u'func get_cli_event_returns()')
|
||||
|
||||
if u'expr_form' in kwargs:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
u'Fluorine',
|
||||
u'The target type should be passed using the \'tgt_type\' '
|
||||
u'argument instead of \'expr_form\'. Support for using '
|
||||
|
@ -1750,7 +1751,7 @@ class LocalClient(object):
|
|||
A set, the targets that the tgt passed should match.
|
||||
'''
|
||||
if u'expr_form' in kwargs:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
u'Fluorine',
|
||||
u'The target type should be passed using the \'tgt_type\' '
|
||||
u'argument instead of \'expr_form\'. Support for using '
|
||||
|
@ -1858,7 +1859,7 @@ class LocalClient(object):
|
|||
A set, the targets that the tgt passed should match.
|
||||
'''
|
||||
if u'expr_form' in kwargs:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
u'Fluorine',
|
||||
u'The target type should be passed using the \'tgt_type\' '
|
||||
u'argument instead of \'expr_form\'. Support for using '
|
||||
|
|
|
@ -25,6 +25,7 @@ import salt.utils.job
|
|||
import salt.utils.lazy
|
||||
import salt.utils.platform
|
||||
import salt.utils.process
|
||||
import salt.utils.versions
|
||||
import salt.transport
|
||||
import salt.log.setup
|
||||
from salt.ext import six
|
||||
|
@ -253,7 +254,7 @@ class SyncClientMixin(object):
|
|||
low[u'kwarg'] = low.pop(u'kwargs')
|
||||
|
||||
if msg:
|
||||
salt.utils.warn_until(u'Oxygen', u' '.join(msg))
|
||||
salt.utils.versions.warn_until(u'Oxygen', u' '.join(msg))
|
||||
|
||||
return self._low(fun, low, print_event=print_event, full_return=full_return)
|
||||
|
||||
|
@ -407,8 +408,6 @@ class SyncClientMixin(object):
|
|||
)
|
||||
data[u'success'] = False
|
||||
|
||||
namespaced_event.fire_event(data, u'ret')
|
||||
|
||||
if self.store_job:
|
||||
try:
|
||||
salt.utils.job.store_job(
|
||||
|
@ -426,6 +425,9 @@ class SyncClientMixin(object):
|
|||
log.error(u'Could not store job cache info. '
|
||||
u'Job details for this run may be unavailable.')
|
||||
|
||||
# Outputters _can_ mutate data so write to the job cache first!
|
||||
namespaced_event.fire_event(data, u'ret')
|
||||
|
||||
# if we fired an event, make sure to delete the event object.
|
||||
# This will ensure that we call destroy, which will do the 0MQ linger
|
||||
log.info(u'Runner completed: %s', data[u'jid'])
|
||||
|
@ -443,6 +445,7 @@ class SyncClientMixin(object):
|
|||
_use_fnmatch = True
|
||||
else:
|
||||
target_mod = arg + u'.' if not arg.endswith(u'.') else arg
|
||||
_use_fnmatch = False
|
||||
if _use_fnmatch:
|
||||
docs = [(fun, self.functions[fun].__doc__)
|
||||
for fun in fnmatch.filter(self.functions, target_mod)]
|
||||
|
|
|
@ -12,8 +12,8 @@ import logging
|
|||
# Import Salt libs
|
||||
import salt.config
|
||||
import salt.client
|
||||
import salt.utils
|
||||
import salt.utils.kinds as kinds
|
||||
import salt.utils.versions
|
||||
import salt.syspaths as syspaths
|
||||
|
||||
try:
|
||||
|
@ -50,7 +50,7 @@ class LocalClient(salt.client.LocalClient):
|
|||
Publish the command!
|
||||
'''
|
||||
if u'expr_form' in kwargs:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
u'Fluorine',
|
||||
u'The target type should be passed using the \'tgt_type\' '
|
||||
u'argument instead of \'expr_form\'. Support for using '
|
||||
|
|
|
@ -9,6 +9,7 @@ import random
|
|||
|
||||
# Import Salt libs
|
||||
import salt.config
|
||||
import salt.utils.versions
|
||||
import salt.syspaths as syspaths
|
||||
from salt.exceptions import SaltClientError # Temporary
|
||||
|
||||
|
@ -52,7 +53,7 @@ class SSHClient(object):
|
|||
Prepare the arguments
|
||||
'''
|
||||
if u'expr_form' in kwargs:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
u'Fluorine',
|
||||
u'The target type should be passed using the \'tgt_type\' '
|
||||
u'argument instead of \'expr_form\'. Support for using '
|
||||
|
@ -88,7 +89,7 @@ class SSHClient(object):
|
|||
.. versionadded:: 2015.5.0
|
||||
'''
|
||||
if u'expr_form' in kwargs:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
u'Fluorine',
|
||||
u'The target type should be passed using the \'tgt_type\' '
|
||||
u'argument instead of \'expr_form\'. Support for using '
|
||||
|
@ -122,7 +123,7 @@ class SSHClient(object):
|
|||
.. versionadded:: 2015.5.0
|
||||
'''
|
||||
if u'expr_form' in kwargs:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
u'Fluorine',
|
||||
u'The target type should be passed using the \'tgt_type\' '
|
||||
u'argument instead of \'expr_form\'. Support for using '
|
||||
|
@ -226,7 +227,7 @@ class SSHClient(object):
|
|||
.. versionadded:: 2017.7.0
|
||||
'''
|
||||
if u'expr_form' in kwargs:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
u'Fluorine',
|
||||
u'The target type should be passed using the \'tgt_type\' '
|
||||
u'argument instead of \'expr_form\'. Support for using '
|
||||
|
|
|
@ -17,6 +17,8 @@ import logging
|
|||
# Import salt libs
|
||||
import salt.client.ssh
|
||||
import salt.runner
|
||||
import salt.utils.args
|
||||
import salt.utils.versions
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -173,7 +175,7 @@ def publish(tgt,
|
|||
# remember to remove the expr_form argument from this function when
|
||||
# performing the cleanup on this deprecation.
|
||||
if expr_form is not None:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
u'Fluorine',
|
||||
u'the target type should be passed using the \'tgt_type\' '
|
||||
u'argument instead of \'expr_form\'. Support for using '
|
||||
|
@ -223,7 +225,7 @@ def full_data(tgt,
|
|||
# remember to remove the expr_form argument from this function when
|
||||
# performing the cleanup on this deprecation.
|
||||
if expr_form is not None:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
u'Fluorine',
|
||||
u'the target type should be passed using the \'tgt_type\' '
|
||||
u'argument instead of \'expr_form\'. Support for using '
|
||||
|
|
|
@ -729,18 +729,9 @@ class Cloud(object):
|
|||
continue
|
||||
|
||||
for vm_name, details in six.iteritems(vms):
|
||||
# If VM was created with use_fqdn with either of the softlayer drivers,
|
||||
# we need to strip the VM name and only search for the short hostname.
|
||||
if driver == 'softlayer' or driver == 'softlayer_hw':
|
||||
ret = []
|
||||
for name in names:
|
||||
name = name.split('.')[0]
|
||||
ret.append(name)
|
||||
if vm_name not in ret:
|
||||
continue
|
||||
# XXX: The logic below can be removed once the aws driver
|
||||
# is removed
|
||||
elif vm_name not in names:
|
||||
if vm_name not in names:
|
||||
continue
|
||||
|
||||
elif driver == 'ec2' and 'aws' in handled_drivers and \
|
||||
|
|
849
salt/cloud/clouds/oneandone.py
Normal file
849
salt/cloud/clouds/oneandone.py
Normal file
|
@ -0,0 +1,849 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
1&1 Cloud Server Module
|
||||
=======================
|
||||
|
||||
=======
|
||||
The 1&1 SaltStack cloud module allows a 1&1 server to
|
||||
be automatically deployed and bootstrapped with Salt.
|
||||
|
||||
:depends: 1and1 >= 1.2.0
|
||||
|
||||
The module requires the 1&1 api_token to be provided.
|
||||
The server should also be assigned a public LAN, a private LAN,
|
||||
or both along with SSH key pairs.
|
||||
...
|
||||
|
||||
Set up the cloud configuration at ``/etc/salt/cloud.providers`` or
|
||||
``/etc/salt/cloud.providers.d/oneandone.conf``:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
my-oneandone-config:
|
||||
driver: oneandone
|
||||
# The 1&1 api token
|
||||
api_token: <your-token>
|
||||
# SSH private key filename
|
||||
ssh_private_key: /path/to/private_key
|
||||
# SSH public key filename
|
||||
ssh_public_key: /path/to/public_key
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
my-oneandone-profile:
|
||||
provider: my-oneandone-config
|
||||
# Either provide fixed_instance_size_id or vcore, cores_per_processor, ram, and hdds.
|
||||
# Size of the ID desired for the server
|
||||
fixed_instance_size: S
|
||||
# Total amount of processors
|
||||
vcore: 2
|
||||
# Number of cores per processor
|
||||
cores_per_processor: 2
|
||||
# RAM memory size in GB
|
||||
ram: 4
|
||||
# Hard disks
|
||||
hdds:
|
||||
-
|
||||
is_main: true
|
||||
size: 20
|
||||
-
|
||||
is_main: false
|
||||
size: 20
|
||||
# ID of the appliance image that will be installed on server
|
||||
appliance_id: <ID>
|
||||
# ID of the datacenter where the server will be created
|
||||
datacenter_id: <ID>
|
||||
# Description of the server
|
||||
description: My server description
|
||||
# Password of the server. Password must contain more than 8 characters
|
||||
# using uppercase letters, numbers and other special symbols.
|
||||
password: P4$$w0rD
|
||||
# Power on server after creation - default True
|
||||
power_on: true
|
||||
# Firewall policy ID. If it is not provided, the server will assign
|
||||
# the best firewall policy, creating a new one if necessary.
|
||||
# If the parameter is sent with a 0 value, the server will be created with all ports blocked.
|
||||
firewall_policy_id: <ID>
|
||||
# IP address ID
|
||||
ip_id: <ID>
|
||||
# Load balancer ID
|
||||
load_balancer_id: <ID>
|
||||
# Monitoring policy ID
|
||||
monitoring_policy_id: <ID>
|
||||
|
||||
Set ``deploy`` to False if Salt should not be installed on the node.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
my-oneandone-profile:
|
||||
deploy: False
|
||||
'''
|
||||
|
||||
# Import python libs
|
||||
from __future__ import absolute_import
|
||||
import logging
|
||||
import os
|
||||
import pprint
|
||||
import time
|
||||
|
||||
# Import salt libs
|
||||
import salt.utils
|
||||
import salt.config as config
|
||||
from salt.exceptions import (
|
||||
SaltCloudConfigError,
|
||||
SaltCloudNotFound,
|
||||
SaltCloudExecutionFailure,
|
||||
SaltCloudExecutionTimeout,
|
||||
SaltCloudSystemExit
|
||||
)
|
||||
|
||||
# Import salt.cloud libs
|
||||
import salt.utils.cloud
|
||||
from salt.ext import six
|
||||
|
||||
try:
|
||||
from oneandone.client import (
|
||||
OneAndOneService, Server, Hdd
|
||||
)
|
||||
HAS_ONEANDONE = True
|
||||
except ImportError:
|
||||
HAS_ONEANDONE = False
|
||||
|
||||
# Get logging started
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
__virtualname__ = 'oneandone'
|
||||
|
||||
|
||||
# Only load in this module if the 1&1 configurations are in place
|
||||
def __virtual__():
|
||||
'''
|
||||
Check for 1&1 configurations.
|
||||
'''
|
||||
if get_configured_provider() is False:
|
||||
return False
|
||||
|
||||
if get_dependencies() is False:
|
||||
return False
|
||||
|
||||
return __virtualname__
|
||||
|
||||
|
||||
def get_configured_provider():
|
||||
'''
|
||||
Return the first configured instance.
|
||||
'''
|
||||
return config.is_provider_configured(
|
||||
__opts__,
|
||||
__active_provider_name__ or __virtualname__,
|
||||
('api_token',)
|
||||
)
|
||||
|
||||
|
||||
def get_dependencies():
|
||||
'''
|
||||
Warn if dependencies are not met.
|
||||
'''
|
||||
return config.check_driver_dependencies(
|
||||
__virtualname__,
|
||||
{'oneandone': HAS_ONEANDONE}
|
||||
)
|
||||
|
||||
|
||||
def get_conn():
|
||||
'''
|
||||
Return a conn object for the passed VM data
|
||||
'''
|
||||
return OneAndOneService(
|
||||
api_token=config.get_cloud_config_value(
|
||||
'api_token',
|
||||
get_configured_provider(),
|
||||
__opts__,
|
||||
search_global=False
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def get_size(vm_):
|
||||
'''
|
||||
Return the VM's size object
|
||||
'''
|
||||
vm_size = config.get_cloud_config_value(
|
||||
'fixed_instance_size', vm_, __opts__, default=None,
|
||||
search_global=False
|
||||
)
|
||||
sizes = avail_sizes()
|
||||
|
||||
if not vm_size:
|
||||
size = next((item for item in sizes if item['name'] == 'S'), None)
|
||||
return size
|
||||
|
||||
size = next((item for item in sizes if item['name'] == vm_size or item['id'] == vm_size), None)
|
||||
if size:
|
||||
return size
|
||||
|
||||
raise SaltCloudNotFound(
|
||||
'The specified size, \'{0}\', could not be found.'.format(vm_size)
|
||||
)
|
||||
|
||||
|
||||
def get_image(vm_):
|
||||
'''
|
||||
Return the image object to use
|
||||
'''
|
||||
vm_image = config.get_cloud_config_value('image', vm_, __opts__).encode(
|
||||
'ascii', 'salt-cloud-force-ascii'
|
||||
)
|
||||
|
||||
images = avail_images()
|
||||
for key, value in six.iteritems(images):
|
||||
if vm_image and vm_image in (images[key]['id'], images[key]['name']):
|
||||
return images[key]
|
||||
|
||||
raise SaltCloudNotFound(
|
||||
'The specified image, \'{0}\', could not be found.'.format(vm_image)
|
||||
)
|
||||
|
||||
|
||||
def avail_locations(conn=None, call=None):
|
||||
'''
|
||||
List available locations/datacenters for 1&1
|
||||
'''
|
||||
if call == 'action':
|
||||
raise SaltCloudSystemExit(
|
||||
'The avail_locations function must be called with '
|
||||
'-f or --function, or with the --list-locations option'
|
||||
)
|
||||
|
||||
datacenters = []
|
||||
|
||||
if not conn:
|
||||
conn = get_conn()
|
||||
|
||||
for datacenter in conn.list_datacenters():
|
||||
datacenters.append({datacenter['country_code']: datacenter})
|
||||
|
||||
return {'Locations': datacenters}
|
||||
|
||||
|
||||
def avail_images(conn=None, call=None):
|
||||
'''
|
||||
Return a list of the server appliances that are on the provider
|
||||
'''
|
||||
if call == 'action':
|
||||
raise SaltCloudSystemExit(
|
||||
'The avail_images function must be called with '
|
||||
'-f or --function, or with the --list-images option'
|
||||
)
|
||||
|
||||
if not conn:
|
||||
conn = get_conn()
|
||||
|
||||
ret = {}
|
||||
|
||||
for appliance in conn.list_appliances():
|
||||
ret[appliance['name']] = appliance
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def avail_sizes(call=None):
|
||||
'''
|
||||
Return a dict of all available VM sizes on the cloud provider with
|
||||
relevant data.
|
||||
'''
|
||||
if call == 'action':
|
||||
raise SaltCloudSystemExit(
|
||||
'The avail_sizes function must be called with '
|
||||
'-f or --function, or with the --list-sizes option'
|
||||
)
|
||||
|
||||
conn = get_conn()
|
||||
|
||||
sizes = conn.fixed_server_flavors()
|
||||
|
||||
return sizes
|
||||
|
||||
|
||||
def script(vm_):
|
||||
'''
|
||||
Return the script deployment object
|
||||
'''
|
||||
return salt.utils.cloud.os_script(
|
||||
config.get_cloud_config_value('script', vm_, __opts__),
|
||||
vm_,
|
||||
__opts__,
|
||||
salt.utils.cloud.salt_config_to_yaml(
|
||||
salt.utils.cloud.minion_config(__opts__, vm_)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def list_nodes(conn=None, call=None):
|
||||
'''
|
||||
Return a list of VMs that are on the provider
|
||||
'''
|
||||
if call == 'action':
|
||||
raise SaltCloudSystemExit(
|
||||
'The list_nodes function must be called with -f or --function.'
|
||||
)
|
||||
|
||||
if not conn:
|
||||
conn = get_conn()
|
||||
|
||||
ret = {}
|
||||
nodes = conn.list_servers()
|
||||
|
||||
for node in nodes:
|
||||
public_ips = []
|
||||
private_ips = []
|
||||
ret = {}
|
||||
|
||||
size = node.get('hardware').get('fixed_instance_size_id', 'Custom size')
|
||||
|
||||
if node.get('private_networks') and len(node['private_networks']) > 0:
|
||||
for private_ip in node['private_networks']:
|
||||
private_ips.append(private_ip)
|
||||
|
||||
if node.get('ips') and len(node['ips']) > 0:
|
||||
for public_ip in node['ips']:
|
||||
public_ips.append(public_ip['ip'])
|
||||
|
||||
server = {
|
||||
'id': node['id'],
|
||||
'image': node['image']['id'],
|
||||
'size': size,
|
||||
'state': node['status']['state'],
|
||||
'private_ips': private_ips,
|
||||
'public_ips': public_ips
|
||||
}
|
||||
ret[node['name']] = server
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def list_nodes_full(conn=None, call=None):
|
||||
'''
|
||||
Return a list of the VMs that are on the provider, with all fields
|
||||
'''
|
||||
if call == 'action':
|
||||
raise SaltCloudSystemExit(
|
||||
'The list_nodes_full function must be called with -f or '
|
||||
'--function.'
|
||||
)
|
||||
|
||||
if not conn:
|
||||
conn = get_conn()
|
||||
|
||||
ret = {}
|
||||
nodes = conn.list_servers()
|
||||
|
||||
for node in nodes:
|
||||
ret[node['name']] = node
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def list_nodes_select(conn=None, call=None):
|
||||
'''
|
||||
Return a list of the VMs that are on the provider, with select fields
|
||||
'''
|
||||
if not conn:
|
||||
conn = get_conn()
|
||||
|
||||
return salt.utils.cloud.list_nodes_select(
|
||||
list_nodes_full(conn, 'function'),
|
||||
__opts__['query.selection'],
|
||||
call,
|
||||
)
|
||||
|
||||
|
||||
def show_instance(name, call=None):
|
||||
'''
|
||||
Show the details from the provider concerning an instance
|
||||
'''
|
||||
if call != 'action':
|
||||
raise SaltCloudSystemExit(
|
||||
'The show_instance action must be called with -a or --action.'
|
||||
)
|
||||
|
||||
nodes = list_nodes_full()
|
||||
__utils__['cloud.cache_node'](
|
||||
nodes[name],
|
||||
__active_provider_name__,
|
||||
__opts__
|
||||
)
|
||||
return nodes[name]
|
||||
|
||||
|
||||
def _get_server(vm_):
|
||||
'''
|
||||
Construct server instance from cloud profile config
|
||||
'''
|
||||
description = config.get_cloud_config_value(
|
||||
'description', vm_, __opts__, default=None,
|
||||
search_global=False
|
||||
)
|
||||
|
||||
ssh_key = load_public_key(vm_)
|
||||
|
||||
vcore = None
|
||||
cores_per_processor = None
|
||||
ram = None
|
||||
fixed_instance_size_id = None
|
||||
|
||||
if 'fixed_instance_size' in vm_:
|
||||
fixed_instance_size = get_size(vm_)
|
||||
fixed_instance_size_id = fixed_instance_size['id']
|
||||
elif (vm_['vcore'] and vm_['cores_per_processor'] and
|
||||
vm_['ram'] and vm_['hdds']):
|
||||
vcore = config.get_cloud_config_value(
|
||||
'vcore', vm_, __opts__, default=None,
|
||||
search_global=False
|
||||
)
|
||||
cores_per_processor = config.get_cloud_config_value(
|
||||
'cores_per_processor', vm_, __opts__, default=None,
|
||||
search_global=False
|
||||
)
|
||||
ram = config.get_cloud_config_value(
|
||||
'ram', vm_, __opts__, default=None,
|
||||
search_global=False
|
||||
)
|
||||
else:
|
||||
raise SaltCloudConfigError("'fixed_instance_size' or 'vcore',"
|
||||
"'cores_per_processor', 'ram', and 'hdds'"
|
||||
"must be provided.")
|
||||
|
||||
appliance_id = config.get_cloud_config_value(
|
||||
'appliance_id', vm_, __opts__, default=None,
|
||||
search_global=False
|
||||
)
|
||||
|
||||
password = config.get_cloud_config_value(
|
||||
'password', vm_, __opts__, default=None,
|
||||
search_global=False
|
||||
)
|
||||
|
||||
firewall_policy_id = config.get_cloud_config_value(
|
||||
'firewall_policy_id', vm_, __opts__, default=None,
|
||||
search_global=False
|
||||
)
|
||||
|
||||
ip_id = config.get_cloud_config_value(
|
||||
'ip_id', vm_, __opts__, default=None,
|
||||
search_global=False
|
||||
)
|
||||
|
||||
load_balancer_id = config.get_cloud_config_value(
|
||||
'load_balancer_id', vm_, __opts__, default=None,
|
||||
search_global=False
|
||||
)
|
||||
|
||||
monitoring_policy_id = config.get_cloud_config_value(
|
||||
'monitoring_policy_id', vm_, __opts__, default=None,
|
||||
search_global=False
|
||||
)
|
||||
|
||||
datacenter_id = config.get_cloud_config_value(
|
||||
'datacenter_id', vm_, __opts__, default=None,
|
||||
search_global=False
|
||||
)
|
||||
|
||||
private_network_id = config.get_cloud_config_value(
|
||||
'private_network_id', vm_, __opts__, default=None,
|
||||
search_global=False
|
||||
)
|
||||
|
||||
power_on = config.get_cloud_config_value(
|
||||
'power_on', vm_, __opts__, default=True,
|
||||
search_global=False
|
||||
)
|
||||
|
||||
# Contruct server object
|
||||
return Server(
|
||||
name=vm_['name'],
|
||||
description=description,
|
||||
fixed_instance_size_id=fixed_instance_size_id,
|
||||
vcore=vcore,
|
||||
cores_per_processor=cores_per_processor,
|
||||
ram=ram,
|
||||
appliance_id=appliance_id,
|
||||
password=password,
|
||||
power_on=power_on,
|
||||
firewall_policy_id=firewall_policy_id,
|
||||
ip_id=ip_id,
|
||||
load_balancer_id=load_balancer_id,
|
||||
monitoring_policy_id=monitoring_policy_id,
|
||||
datacenter_id=datacenter_id,
|
||||
rsa_key=ssh_key,
|
||||
private_network_id=private_network_id
|
||||
)
|
||||
|
||||
|
||||
def _get_hdds(vm_):
|
||||
'''
|
||||
Construct VM hdds from cloud profile config
|
||||
'''
|
||||
_hdds = config.get_cloud_config_value(
|
||||
'hdds', vm_, __opts__, default=None,
|
||||
search_global=False
|
||||
)
|
||||
|
||||
hdds = []
|
||||
|
||||
for hdd in _hdds:
|
||||
hdds.append(
|
||||
Hdd(
|
||||
size=hdd['size'],
|
||||
is_main=hdd['is_main']
|
||||
)
|
||||
)
|
||||
|
||||
return hdds
|
||||
|
||||
|
||||
def create(vm_):
|
||||
'''
|
||||
Create a single VM from a data dict
|
||||
'''
|
||||
try:
|
||||
# Check for required profile parameters before sending any API calls.
|
||||
if (vm_['profile'] and
|
||||
config.is_profile_configured(__opts__,
|
||||
(__active_provider_name__ or
|
||||
'oneandone'),
|
||||
vm_['profile']) is False):
|
||||
return False
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
data = None
|
||||
conn = get_conn()
|
||||
hdds = []
|
||||
|
||||
# Assemble the composite server object.
|
||||
server = _get_server(vm_)
|
||||
|
||||
if not bool(server.specs['hardware']['fixed_instance_size_id']):
|
||||
# Assemble the hdds object.
|
||||
hdds = _get_hdds(vm_)
|
||||
|
||||
__utils__['cloud.fire_event'](
|
||||
'event',
|
||||
'requesting instance',
|
||||
'salt/cloud/{0}/requesting'.format(vm_['name']),
|
||||
args={'name': vm_['name']},
|
||||
sock_dir=__opts__['sock_dir'],
|
||||
transport=__opts__['transport']
|
||||
)
|
||||
|
||||
try:
|
||||
data = conn.create_server(server=server, hdds=hdds)
|
||||
|
||||
_wait_for_completion(conn,
|
||||
get_wait_timeout(vm_),
|
||||
data['id'])
|
||||
except Exception as exc: # pylint: disable=W0703
|
||||
log.error(
|
||||
'Error creating {0} on 1and1\n\n'
|
||||
'The following exception was thrown by the 1and1 library '
|
||||
'when trying to run the initial deployment: \n{1}'.format(
|
||||
vm_['name'], exc
|
||||
),
|
||||
exc_info_on_loglevel=logging.DEBUG
|
||||
)
|
||||
return False
|
||||
|
||||
vm_['server_id'] = data['id']
|
||||
password = data['first_password']
|
||||
|
||||
def __query_node_data(vm_, data):
|
||||
'''
|
||||
Query node data until node becomes available.
|
||||
'''
|
||||
running = False
|
||||
try:
|
||||
data = show_instance(vm_['name'], 'action')
|
||||
if not data:
|
||||
return False
|
||||
log.debug(
|
||||
'Loaded node data for {0}:\nname: {1}\nstate: {2}'.format(
|
||||
vm_['name'],
|
||||
pprint.pformat(data['name']),
|
||||
data['status']['state']
|
||||
)
|
||||
)
|
||||
except Exception as err:
|
||||
log.error(
|
||||
'Failed to get nodes list: {0}'.format(
|
||||
err
|
||||
),
|
||||
# Show the trackback if the debug logging level is enabled
|
||||
exc_info_on_loglevel=logging.DEBUG
|
||||
)
|
||||
# Trigger a failure in the wait for IP function
|
||||
return False
|
||||
|
||||
running = data['status']['state'].lower() == 'powered_on'
|
||||
if not running:
|
||||
# Still not running, trigger another iteration
|
||||
return
|
||||
|
||||
vm_['ssh_host'] = data['ips'][0]['ip']
|
||||
|
||||
return data
|
||||
|
||||
try:
|
||||
data = salt.utils.cloud.wait_for_ip(
|
||||
__query_node_data,
|
||||
update_args=(vm_, data),
|
||||
timeout=config.get_cloud_config_value(
|
||||
'wait_for_ip_timeout', vm_, __opts__, default=10 * 60),
|
||||
interval=config.get_cloud_config_value(
|
||||
'wait_for_ip_interval', vm_, __opts__, default=10),
|
||||
)
|
||||
except (SaltCloudExecutionTimeout, SaltCloudExecutionFailure) as exc:
|
||||
try:
|
||||
# It might be already up, let's destroy it!
|
||||
destroy(vm_['name'])
|
||||
except SaltCloudSystemExit:
|
||||
pass
|
||||
finally:
|
||||
raise SaltCloudSystemExit(str(exc.message))
|
||||
|
||||
log.debug('VM is now running')
|
||||
log.info('Created Cloud VM {0}'.format(vm_))
|
||||
log.debug(
|
||||
'{0} VM creation details:\n{1}'.format(
|
||||
vm_, pprint.pformat(data)
|
||||
)
|
||||
)
|
||||
|
||||
__utils__['cloud.fire_event'](
|
||||
'event',
|
||||
'created instance',
|
||||
'salt/cloud/{0}/created'.format(vm_['name']),
|
||||
args={
|
||||
'name': vm_['name'],
|
||||
'profile': vm_['profile'],
|
||||
'provider': vm_['driver'],
|
||||
},
|
||||
sock_dir=__opts__['sock_dir'],
|
||||
transport=__opts__['transport']
|
||||
)
|
||||
|
||||
if 'ssh_host' in vm_:
|
||||
vm_['password'] = password
|
||||
vm_['key_filename'] = get_key_filename(vm_)
|
||||
ret = __utils__['cloud.bootstrap'](vm_, __opts__)
|
||||
ret.update(data)
|
||||
return ret
|
||||
else:
|
||||
raise SaltCloudSystemExit('A valid IP address was not found.')
|
||||
|
||||
|
||||
def destroy(name, call=None):
|
||||
'''
|
||||
destroy a server by name
|
||||
|
||||
:param name: name given to the server
|
||||
:param call: call value in this case is 'action'
|
||||
:return: array of booleans , true if successfully stopped and true if
|
||||
successfully removed
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-cloud -d vm_name
|
||||
|
||||
'''
|
||||
if call == 'function':
|
||||
raise SaltCloudSystemExit(
|
||||
'The destroy action must be called with -d, --destroy, '
|
||||
'-a or --action.'
|
||||
)
|
||||
|
||||
__utils__['cloud.fire_event'](
|
||||
'event',
|
||||
'destroying instance',
|
||||
'salt/cloud/{0}/destroying'.format(name),
|
||||
args={'name': name},
|
||||
sock_dir=__opts__['sock_dir'],
|
||||
transport=__opts__['transport']
|
||||
)
|
||||
|
||||
conn = get_conn()
|
||||
node = get_node(conn, name)
|
||||
|
||||
conn.delete_server(server_id=node['id'])
|
||||
|
||||
__utils__['cloud.fire_event'](
|
||||
'event',
|
||||
'destroyed instance',
|
||||
'salt/cloud/{0}/destroyed'.format(name),
|
||||
args={'name': name},
|
||||
sock_dir=__opts__['sock_dir'],
|
||||
transport=__opts__['transport']
|
||||
)
|
||||
|
||||
if __opts__.get('update_cachedir', False) is True:
|
||||
__utils__['cloud.delete_minion_cachedir'](
|
||||
name,
|
||||
__active_provider_name__.split(':')[0],
|
||||
__opts__
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def reboot(name, call=None):
|
||||
'''
|
||||
reboot a server by name
|
||||
:param name: name given to the machine
|
||||
:param call: call value in this case is 'action'
|
||||
:return: true if successful
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-cloud -a reboot vm_name
|
||||
'''
|
||||
conn = get_conn()
|
||||
node = get_node(conn, name)
|
||||
|
||||
conn.modify_server_status(server_id=node['id'], action='REBOOT')
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def stop(name, call=None):
|
||||
'''
|
||||
stop a server by name
|
||||
:param name: name given to the machine
|
||||
:param call: call value in this case is 'action'
|
||||
:return: true if successful
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-cloud -a stop vm_name
|
||||
'''
|
||||
conn = get_conn()
|
||||
node = get_node(conn, name)
|
||||
|
||||
conn.stop_server(server_id=node['id'])
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def start(name, call=None):
|
||||
'''
|
||||
start a server by name
|
||||
:param name: name given to the machine
|
||||
:param call: call value in this case is 'action'
|
||||
:return: true if successful
|
||||
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-cloud -a start vm_name
|
||||
'''
|
||||
conn = get_conn()
|
||||
node = get_node(conn, name)
|
||||
|
||||
conn.start_server(server_id=node['id'])
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def get_node(conn, name):
|
||||
'''
|
||||
Return a node for the named VM
|
||||
'''
|
||||
for node in conn.list_servers(per_page=1000):
|
||||
if node['name'] == name:
|
||||
return node
|
||||
|
||||
|
||||
def get_key_filename(vm_):
|
||||
'''
|
||||
Check SSH private key file and return absolute path if exists.
|
||||
'''
|
||||
key_filename = config.get_cloud_config_value(
|
||||
'ssh_private_key', vm_, __opts__, search_global=False, default=None
|
||||
)
|
||||
if key_filename is not None:
|
||||
key_filename = os.path.expanduser(key_filename)
|
||||
if not os.path.isfile(key_filename):
|
||||
raise SaltCloudConfigError(
|
||||
'The defined ssh_private_key \'{0}\' does not exist'.format(
|
||||
key_filename
|
||||
)
|
||||
)
|
||||
|
||||
return key_filename
|
||||
|
||||
|
||||
def load_public_key(vm_):
|
||||
'''
|
||||
Load the public key file if exists.
|
||||
'''
|
||||
public_key_filename = config.get_cloud_config_value(
|
||||
'ssh_public_key', vm_, __opts__, search_global=False, default=None
|
||||
)
|
||||
if public_key_filename is not None:
|
||||
public_key_filename = os.path.expanduser(public_key_filename)
|
||||
if not os.path.isfile(public_key_filename):
|
||||
raise SaltCloudConfigError(
|
||||
'The defined ssh_public_key \'{0}\' does not exist'.format(
|
||||
public_key_filename
|
||||
)
|
||||
)
|
||||
|
||||
with salt.utils.fopen(public_key_filename, 'r') as public_key:
|
||||
key = public_key.read().replace('\n', '')
|
||||
|
||||
return key
|
||||
|
||||
|
||||
def get_wait_timeout(vm_):
|
||||
'''
|
||||
Return the wait_for_timeout for resource provisioning.
|
||||
'''
|
||||
return config.get_cloud_config_value(
|
||||
'wait_for_timeout', vm_, __opts__, default=15 * 60,
|
||||
search_global=False
|
||||
)
|
||||
|
||||
|
||||
def _wait_for_completion(conn, wait_timeout, server_id):
|
||||
'''
|
||||
Poll request status until resource is provisioned.
|
||||
'''
|
||||
wait_timeout = time.time() + wait_timeout
|
||||
while wait_timeout > time.time():
|
||||
time.sleep(5)
|
||||
|
||||
server = conn.get_server(server_id)
|
||||
server_state = server['status']['state'].lower()
|
||||
|
||||
if server_state == "powered_on":
|
||||
return
|
||||
elif server_state == 'failed':
|
||||
raise Exception('Server creation failed for {0}'.format(server_id))
|
||||
elif server_state in ('active',
|
||||
'enabled',
|
||||
'deploying',
|
||||
'configuring'):
|
||||
continue
|
||||
else:
|
||||
raise Exception(
|
||||
'Unknown server state {0}'.format(server_state))
|
||||
raise Exception(
|
||||
'Timed out waiting for server create completion for {0}'.format(server_id)
|
||||
)
|
|
@ -151,7 +151,9 @@ import os
|
|||
import logging
|
||||
import socket
|
||||
import pprint
|
||||
from salt.utils.versions import LooseVersion as _LooseVersion
|
||||
|
||||
# This import needs to be here so the version check can be done below
|
||||
import salt.utils.versions
|
||||
|
||||
# Import libcloud
|
||||
try:
|
||||
|
@ -170,7 +172,8 @@ try:
|
|||
# However, older versions of libcloud must still be supported with this work-around.
|
||||
# This work-around can be removed when the required minimum version of libcloud is
|
||||
# 2.0.0 (See PR #40837 - which is implemented in Salt Oxygen).
|
||||
if _LooseVersion(libcloud.__version__) < _LooseVersion('1.4.0'):
|
||||
if salt.utils.versions.LooseVersion(libcloud.__version__) < \
|
||||
salt.utils.versions.LooseVersion('1.4.0'):
|
||||
# See https://github.com/saltstack/salt/issues/32743
|
||||
import libcloud.security
|
||||
libcloud.security.CA_CERTS_PATH.append('/etc/ssl/certs/YaST-CA.pem')
|
||||
|
@ -182,7 +185,7 @@ except Exception:
|
|||
from salt.cloud.libcloudfuncs import * # pylint: disable=W0614,W0401
|
||||
|
||||
# Import salt libs
|
||||
import salt.utils # Can be removed once namespaced_function and warn_until have been moved
|
||||
import salt.utils # Can be removed once namespaced_function has been moved
|
||||
import salt.utils.cloud
|
||||
import salt.utils.files
|
||||
import salt.utils.pycrypto
|
||||
|
@ -237,7 +240,7 @@ def __virtual__():
|
|||
if get_dependencies() is False:
|
||||
return False
|
||||
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Oxygen',
|
||||
'This driver has been deprecated and will be removed in the '
|
||||
'{version} release of Salt. Please use the nova driver instead.'
|
||||
|
|
|
@ -17,8 +17,16 @@ from __future__ import absolute_import
|
|||
import logging
|
||||
|
||||
# Import salt libs
|
||||
import salt.utils
|
||||
import salt.config as config
|
||||
from salt.exceptions import SaltCloudException
|
||||
import salt.netapi
|
||||
import salt.ext.six as six
|
||||
if six.PY3:
|
||||
import ipaddress
|
||||
else:
|
||||
import salt.ext.ipaddress as ipaddress
|
||||
|
||||
from salt.exceptions import SaltCloudException, SaltCloudSystemExit
|
||||
|
||||
# Get logging started
|
||||
log = logging.getLogger(__name__)
|
||||
|
@ -47,28 +55,188 @@ def __virtual__():
|
|||
return True
|
||||
|
||||
|
||||
def list_nodes():
|
||||
def _get_connection_info():
|
||||
'''
|
||||
Because this module is not specific to any cloud providers, there will be
|
||||
no nodes to list.
|
||||
Return connection information for the passed VM data
|
||||
'''
|
||||
vm_ = get_configured_provider()
|
||||
|
||||
try:
|
||||
ret = {'username': vm_['username'],
|
||||
'password': vm_['password'],
|
||||
'eauth': vm_['eauth'],
|
||||
'vm': vm_,
|
||||
}
|
||||
except KeyError:
|
||||
raise SaltCloudException(
|
||||
'Configuration must define salt-api "username", "password" and "eauth"')
|
||||
return ret
|
||||
|
||||
|
||||
def avail_locations(call=None):
|
||||
'''
|
||||
This function returns a list of locations available.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-cloud --list-locations my-cloud-provider
|
||||
|
||||
[ saltify will always returns an empty dictionary ]
|
||||
'''
|
||||
|
||||
return {}
|
||||
|
||||
|
||||
def avail_images(call=None):
|
||||
'''
|
||||
This function returns a list of images available for this cloud provider.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-cloud --list-images saltify
|
||||
|
||||
returns a list of available profiles.
|
||||
|
||||
..versionadded:: Oxygen
|
||||
|
||||
'''
|
||||
vm_ = get_configured_provider()
|
||||
return {'Profiles': [profile for profile in vm_['profiles']]}
|
||||
|
||||
|
||||
def avail_sizes(call=None):
|
||||
'''
|
||||
This function returns a list of sizes available for this cloud provider.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-cloud --list-sizes saltify
|
||||
|
||||
[ saltify always returns an empty dictionary ]
|
||||
'''
|
||||
return {}
|
||||
|
||||
|
||||
def list_nodes_full():
|
||||
def list_nodes(call=None):
|
||||
'''
|
||||
Because this module is not specific to any cloud providers, there will be
|
||||
no nodes to list.
|
||||
List the nodes which have salt-cloud:driver:saltify grains.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-cloud -Q
|
||||
|
||||
returns a list of dictionaries of defined standard fields.
|
||||
|
||||
salt-api setup required for operation.
|
||||
|
||||
..versionadded:: Oxygen
|
||||
|
||||
'''
|
||||
return {}
|
||||
nodes = _list_nodes_full(call)
|
||||
return _build_required_items(nodes)
|
||||
|
||||
|
||||
def list_nodes_select():
|
||||
def _build_required_items(nodes):
|
||||
ret = {}
|
||||
for name, grains in nodes.items():
|
||||
if grains:
|
||||
private_ips = []
|
||||
public_ips = []
|
||||
ips = grains['ipv4'] + grains['ipv6']
|
||||
for adrs in ips:
|
||||
ip_ = ipaddress.ip_address(adrs)
|
||||
if not ip_.is_loopback:
|
||||
if ip_.is_private:
|
||||
private_ips.append(adrs)
|
||||
else:
|
||||
public_ips.append(adrs)
|
||||
|
||||
ret[name] = {
|
||||
'id': grains['id'],
|
||||
'image': grains['salt-cloud']['profile'],
|
||||
'private_ips': private_ips,
|
||||
'public_ips': public_ips,
|
||||
'size': '',
|
||||
'state': 'running'
|
||||
}
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def list_nodes_full(call=None):
|
||||
'''
|
||||
Because this module is not specific to any cloud providers, there will be
|
||||
no nodes to list.
|
||||
Lists complete information for all nodes.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-cloud -F
|
||||
|
||||
returns a list of dictionaries.
|
||||
for 'saltify' minions, returns dict of grains (enhanced).
|
||||
salt-api setup required for operation.
|
||||
|
||||
..versionadded:: Oxygen
|
||||
'''
|
||||
return {}
|
||||
|
||||
ret = _list_nodes_full(call)
|
||||
|
||||
for key, grains in ret.items(): # clean up some hyperverbose grains -- everything is too much
|
||||
try:
|
||||
del grains['cpu_flags'], grains['disks'], grains['pythonpath'], grains['dns'], grains['gpus']
|
||||
except KeyError:
|
||||
pass # ignore absence of things we are eliminating
|
||||
except TypeError:
|
||||
del ret[key] # eliminate all reference to unexpected (None) values.
|
||||
|
||||
reqs = _build_required_items(ret)
|
||||
|
||||
for name in ret:
|
||||
ret[name].update(reqs[name])
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def _list_nodes_full(call=None):
|
||||
'''
|
||||
List the nodes, ask all 'saltify' minions, return dict of grains.
|
||||
'''
|
||||
local = salt.netapi.NetapiClient(__opts__)
|
||||
cmd = {'client': 'local',
|
||||
'tgt': 'salt-cloud:driver:saltify',
|
||||
'fun': 'grains.items',
|
||||
'arg': '',
|
||||
'tgt_type': 'grain',
|
||||
}
|
||||
cmd.update(_get_connection_info())
|
||||
|
||||
return local.run(cmd)
|
||||
|
||||
|
||||
def list_nodes_select(call=None):
|
||||
'''
|
||||
Return a list of the minions that have salt-cloud grains, with
|
||||
select fields.
|
||||
'''
|
||||
return salt.utils.cloud.list_nodes_select(
|
||||
list_nodes_full('function'), __opts__['query.selection'], call,
|
||||
)
|
||||
|
||||
|
||||
def show_instance(name, call=None):
|
||||
'''
|
||||
List the a single node, return dict of grains.
|
||||
'''
|
||||
local = salt.netapi.NetapiClient(__opts__)
|
||||
cmd = {'client': 'local',
|
||||
'tgt': 'name',
|
||||
'fun': 'grains.items',
|
||||
'arg': '',
|
||||
'tgt_type': 'glob',
|
||||
}
|
||||
cmd.update(_get_connection_info())
|
||||
ret = local.run(cmd)
|
||||
ret.update(_build_required_items(ret))
|
||||
return ret
|
||||
|
||||
|
||||
def create(vm_):
|
||||
|
@ -190,3 +358,130 @@ def _verify(vm_):
|
|||
except SaltCloudException as exc:
|
||||
log.error('Exception: %s', exc)
|
||||
return False
|
||||
|
||||
|
||||
def destroy(name, call=None):
|
||||
''' Destroy a node.
|
||||
|
||||
.. versionadded:: Oxygen
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-cloud --destroy mymachine
|
||||
|
||||
salt-api setup required for operation.
|
||||
|
||||
'''
|
||||
if call == 'function':
|
||||
raise SaltCloudSystemExit(
|
||||
'The destroy action must be called with -d, --destroy, '
|
||||
'-a, or --action.'
|
||||
)
|
||||
|
||||
opts = __opts__
|
||||
|
||||
__utils__['cloud.fire_event'](
|
||||
'event',
|
||||
'destroying instance',
|
||||
'salt/cloud/{0}/destroying'.format(name),
|
||||
args={'name': name},
|
||||
sock_dir=opts['sock_dir'],
|
||||
transport=opts['transport']
|
||||
)
|
||||
|
||||
local = salt.netapi.NetapiClient(opts)
|
||||
cmd = {'client': 'local',
|
||||
'tgt': name,
|
||||
'fun': 'grains.get',
|
||||
'arg': ['salt-cloud'],
|
||||
}
|
||||
cmd.update(_get_connection_info())
|
||||
vm_ = cmd['vm']
|
||||
my_info = local.run(cmd)
|
||||
try:
|
||||
vm_.update(my_info[name]) # get profile name to get config value
|
||||
except (IndexError, TypeError):
|
||||
pass
|
||||
if config.get_cloud_config_value(
|
||||
'remove_config_on_destroy', vm_, opts, default=True
|
||||
):
|
||||
cmd.update({'fun': 'service.disable', 'arg': ['salt-minion']})
|
||||
ret = local.run(cmd) # prevent generating new keys on restart
|
||||
if ret and ret[name]:
|
||||
log.info('disabled salt-minion service on %s', name)
|
||||
cmd.update({'fun': 'config.get', 'arg': ['conf_file']})
|
||||
ret = local.run(cmd)
|
||||
if ret and ret[name]:
|
||||
confile = ret[name]
|
||||
cmd.update({'fun': 'file.remove', 'arg': [confile]})
|
||||
ret = local.run(cmd)
|
||||
if ret and ret[name]:
|
||||
log.info('removed minion %s configuration file %s',
|
||||
name, confile)
|
||||
cmd.update({'fun': 'config.get', 'arg': ['pki_dir']})
|
||||
ret = local.run(cmd)
|
||||
if ret and ret[name]:
|
||||
pki_dir = ret[name]
|
||||
cmd.update({'fun': 'file.remove', 'arg': [pki_dir]})
|
||||
ret = local.run(cmd)
|
||||
if ret and ret[name]:
|
||||
log.info(
|
||||
'removed minion %s key files in %s',
|
||||
name,
|
||||
pki_dir)
|
||||
|
||||
if config.get_cloud_config_value(
|
||||
'shutdown_on_destroy', vm_, opts, default=False
|
||||
):
|
||||
cmd.update({'fun': 'system.shutdown', 'arg': ''})
|
||||
ret = local.run(cmd)
|
||||
if ret and ret[name]:
|
||||
log.info('system.shutdown for minion %s successful', name)
|
||||
|
||||
__utils__['cloud.fire_event'](
|
||||
'event',
|
||||
'destroyed instance',
|
||||
'salt/cloud/{0}/destroyed'.format(name),
|
||||
args={'name': name},
|
||||
sock_dir=opts['sock_dir'],
|
||||
transport=opts['transport']
|
||||
)
|
||||
|
||||
return {'Destroyed': '{0} was destroyed.'.format(name)}
|
||||
|
||||
|
||||
def reboot(name, call=None):
|
||||
'''
|
||||
Reboot a saltify minion.
|
||||
|
||||
salt-api setup required for operation.
|
||||
|
||||
..versionadded:: Oxygen
|
||||
|
||||
name
|
||||
The name of the VM to reboot.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-cloud -a reboot vm_name
|
||||
'''
|
||||
|
||||
if call != 'action':
|
||||
raise SaltCloudException(
|
||||
'The reboot action must be called with -a or --action.'
|
||||
)
|
||||
|
||||
local = salt.netapi.NetapiClient(__opts__)
|
||||
cmd = {'client': 'local',
|
||||
'tgt': name,
|
||||
'fun': 'system.reboot',
|
||||
'arg': '',
|
||||
}
|
||||
cmd.update(_get_connection_info())
|
||||
ret = local.run(cmd)
|
||||
|
||||
return ret
|
||||
|
|
|
@ -511,7 +511,7 @@ def list_nodes_full(mask='mask[id]', call=None):
|
|||
conn = get_conn(service='SoftLayer_Account')
|
||||
response = conn.getVirtualGuests()
|
||||
for node_id in response:
|
||||
hostname = node_id['hostname'].split('.')[0]
|
||||
hostname = node_id['hostname']
|
||||
ret[hostname] = node_id
|
||||
__utils__['cloud.cache_node_list'](ret, __active_provider_name__.split(':')[0], __opts__)
|
||||
return ret
|
||||
|
@ -597,9 +597,6 @@ def destroy(name, call=None):
|
|||
transport=__opts__['transport']
|
||||
)
|
||||
|
||||
# If the VM was created with use_fqdn, the short hostname will be used instead.
|
||||
name = name.split('.')[0]
|
||||
|
||||
node = show_instance(name, call='action')
|
||||
conn = get_conn()
|
||||
response = conn.deleteObject(id=node['id'])
|
||||
|
|
|
@ -526,9 +526,6 @@ def destroy(name, call=None):
|
|||
transport=__opts__['transport']
|
||||
)
|
||||
|
||||
# If the VM was created with use_fqdn, the short hostname will be used instead.
|
||||
name = name.split('.')[0]
|
||||
|
||||
node = show_instance(name, call='action')
|
||||
conn = get_conn(service='SoftLayer_Ticket')
|
||||
response = conn.createCancelServerTicket(
|
||||
|
|
|
@ -2344,7 +2344,7 @@ def syndic_config(master_config_path,
|
|||
'pki_dir', 'cachedir', 'pidfile', 'sock_dir', 'extension_modules',
|
||||
'autosign_file', 'autoreject_file', 'token_dir'
|
||||
]
|
||||
for config_key in ('syndic_log_file', 'log_file', 'key_logfile'):
|
||||
for config_key in ('log_file', 'key_logfile', 'syndic_log_file'):
|
||||
# If this is not a URI and instead a local path
|
||||
if urlparse(opts.get(config_key, '')).scheme == '':
|
||||
prepend_root_dirs.append(config_key)
|
||||
|
@ -3235,12 +3235,12 @@ def is_profile_configured(opts, provider, profile_name, vm_=None):
|
|||
alias, driver = provider.split(':')
|
||||
|
||||
# Most drivers need an image to be specified, but some do not.
|
||||
non_image_drivers = ['nova', 'virtualbox', 'libvirt', 'softlayer']
|
||||
non_image_drivers = ['nova', 'virtualbox', 'libvirt', 'softlayer', 'oneandone']
|
||||
|
||||
# Most drivers need a size, but some do not.
|
||||
non_size_drivers = ['opennebula', 'parallels', 'proxmox', 'scaleway',
|
||||
'softlayer', 'softlayer_hw', 'vmware', 'vsphere',
|
||||
'virtualbox', 'profitbricks', 'libvirt']
|
||||
'virtualbox', 'profitbricks', 'libvirt', 'oneandone']
|
||||
|
||||
provider_key = opts['providers'][alias][driver]
|
||||
profile_key = opts['providers'][alias][driver]['profiles'][profile_name]
|
||||
|
|
|
@ -33,6 +33,7 @@ import salt.utils.platform
|
|||
import salt.utils.stringutils
|
||||
import salt.utils.templates
|
||||
import salt.utils.url
|
||||
import salt.utils.versions
|
||||
from salt.utils.locales import sdecode
|
||||
from salt.utils.openstack.swift import SaltSwift
|
||||
|
||||
|
@ -736,7 +737,7 @@ class Client(object):
|
|||
Cache a file then process it as a template
|
||||
'''
|
||||
if u'env' in kwargs:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
u'Oxygen',
|
||||
u'Parameter \'env\' has been detected in the argument list. This '
|
||||
u'parameter is no longer used and has been replaced by \'saltenv\' '
|
||||
|
@ -1286,7 +1287,7 @@ class RemoteClient(Client):
|
|||
u'specified file %s is not present to generate hash: %s',
|
||||
path, err
|
||||
)
|
||||
return {}
|
||||
return {}, None
|
||||
else:
|
||||
ret = {}
|
||||
hash_type = self.opts.get(u'hash_type', u'md5')
|
||||
|
|
|
@ -15,10 +15,10 @@ import time
|
|||
|
||||
# Import salt libs
|
||||
import salt.loader
|
||||
import salt.utils
|
||||
import salt.utils.files
|
||||
import salt.utils.locales
|
||||
import salt.utils.url
|
||||
import salt.utils.versions
|
||||
from salt.utils.args import get_function_argspec as _argspec
|
||||
|
||||
# Import 3rd-party libs
|
||||
|
@ -553,7 +553,7 @@ class Fileserver(object):
|
|||
kwargs[args[0]] = args[1]
|
||||
|
||||
if 'env' in kwargs:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Oxygen',
|
||||
'Parameter \'env\' has been detected in the argument list. This '
|
||||
'parameter is no longer used and has been replaced by \'saltenv\' '
|
||||
|
@ -583,7 +583,7 @@ class Fileserver(object):
|
|||
'dest': ''}
|
||||
|
||||
if 'env' in load:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Oxygen',
|
||||
'Parameter \'env\' has been detected in the argument list. This '
|
||||
'parameter is no longer used and has been replaced by \'saltenv\' '
|
||||
|
@ -609,7 +609,7 @@ class Fileserver(object):
|
|||
Common code for hashing and stating files
|
||||
'''
|
||||
if 'env' in load:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Oxygen',
|
||||
'Parameter \'env\' has been detected in the argument list. '
|
||||
'This parameter is no longer used and has been replaced by '
|
||||
|
@ -656,7 +656,7 @@ class Fileserver(object):
|
|||
Deletes the file_lists cache files
|
||||
'''
|
||||
if 'env' in load:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Oxygen',
|
||||
'Parameter \'env\' has been detected in the argument list. This '
|
||||
'parameter is no longer used and has been replaced by \'saltenv\' '
|
||||
|
@ -738,7 +738,7 @@ class Fileserver(object):
|
|||
Return a list of files from the dominant environment
|
||||
'''
|
||||
if 'env' in load:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Oxygen',
|
||||
'Parameter \'env\' has been detected in the argument list. This '
|
||||
'parameter is no longer used and has been replaced by \'saltenv\' '
|
||||
|
@ -769,7 +769,7 @@ class Fileserver(object):
|
|||
List all emptydirs in the given environment
|
||||
'''
|
||||
if 'env' in load:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Oxygen',
|
||||
'Parameter \'env\' has been detected in the argument list. This '
|
||||
'parameter is no longer used and has been replaced by \'saltenv\' '
|
||||
|
@ -800,7 +800,7 @@ class Fileserver(object):
|
|||
List all directories in the given environment
|
||||
'''
|
||||
if 'env' in load:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Oxygen',
|
||||
'Parameter \'env\' has been detected in the argument list. This '
|
||||
'parameter is no longer used and has been replaced by \'saltenv\' '
|
||||
|
@ -831,7 +831,7 @@ class Fileserver(object):
|
|||
Return a list of symlinked files and dirs
|
||||
'''
|
||||
if 'env' in load:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Oxygen',
|
||||
'Parameter \'env\' has been detected in the argument list. This '
|
||||
'parameter is no longer used and has been replaced by \'saltenv\' '
|
||||
|
|
|
@ -63,6 +63,7 @@ import salt.utils
|
|||
import salt.utils.files
|
||||
import salt.utils.gzip_util
|
||||
import salt.utils.url
|
||||
import salt.utils.versions
|
||||
import salt.fileserver
|
||||
from salt.utils.event import tagify
|
||||
|
||||
|
@ -569,7 +570,7 @@ def _env_is_exposed(env):
|
|||
blacklist.
|
||||
'''
|
||||
if __opts__['hgfs_env_whitelist']:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Neon',
|
||||
'The hgfs_env_whitelist config option has been renamed to '
|
||||
'hgfs_saltenv_whitelist. Please update your configuration.'
|
||||
|
@ -579,7 +580,7 @@ def _env_is_exposed(env):
|
|||
whitelist = __opts__['hgfs_saltenv_whitelist']
|
||||
|
||||
if __opts__['hgfs_env_blacklist']:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Neon',
|
||||
'The hgfs_env_blacklist config option has been renamed to '
|
||||
'hgfs_saltenv_blacklist. Please update your configuration.'
|
||||
|
@ -735,7 +736,7 @@ def serve_file(load, fnd):
|
|||
Return a chunk from a file based on the data received
|
||||
'''
|
||||
if 'env' in load:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Oxygen',
|
||||
'Parameter \'env\' has been detected in the argument list. This '
|
||||
'parameter is no longer used and has been replaced by \'saltenv\' '
|
||||
|
@ -769,7 +770,7 @@ def file_hash(load, fnd):
|
|||
Return a file hash, the hash type is set in the master config file
|
||||
'''
|
||||
if 'env' in load:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Oxygen',
|
||||
'Parameter \'env\' has been detected in the argument list. This '
|
||||
'parameter is no longer used and has been replaced by \'saltenv\' '
|
||||
|
@ -803,7 +804,7 @@ def _file_lists(load, form):
|
|||
Return a dict containing the file lists for files and dirs
|
||||
'''
|
||||
if 'env' in load:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Oxygen',
|
||||
'Parameter \'env\' has been detected in the argument list. This '
|
||||
'parameter is no longer used and has been replaced by \'saltenv\' '
|
||||
|
@ -851,7 +852,7 @@ def _get_file_list(load):
|
|||
Get a list of all files on the file server in a specified environment
|
||||
'''
|
||||
if 'env' in load:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Oxygen',
|
||||
'Parameter \'env\' has been detected in the argument list. This '
|
||||
'parameter is no longer used and has been replaced by \'saltenv\' '
|
||||
|
@ -896,7 +897,7 @@ def _get_dir_list(load):
|
|||
Get a list of all directories on the master
|
||||
'''
|
||||
if 'env' in load:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Oxygen',
|
||||
'Parameter \'env\' has been detected in the argument list. This '
|
||||
'parameter is no longer used and has been replaced by \'saltenv\' '
|
||||
|
|
|
@ -35,6 +35,7 @@ import salt.utils
|
|||
import salt.utils.files
|
||||
import salt.utils.gzip_util
|
||||
import salt.utils.url
|
||||
import salt.utils.versions
|
||||
|
||||
# Import third party libs
|
||||
from salt.ext import six
|
||||
|
@ -164,7 +165,7 @@ def file_hash(load, fnd):
|
|||
ret = {}
|
||||
|
||||
if 'env' in load:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Oxygen',
|
||||
'Parameter \'env\' has been detected in the argument list. This '
|
||||
'parameter is no longer used and has been replaced by \'saltenv\' '
|
||||
|
@ -234,7 +235,7 @@ def file_list(load):
|
|||
Return a list of all files on the file server in a specified environment
|
||||
'''
|
||||
if 'env' in load:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Oxygen',
|
||||
'Parameter \'env\' has been detected in the argument list. This '
|
||||
'parameter is no longer used and has been replaced by \'saltenv\' '
|
||||
|
@ -318,7 +319,7 @@ def dir_list(load):
|
|||
- source-minion/absolute/path
|
||||
'''
|
||||
if 'env' in load:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Oxygen',
|
||||
'Parameter \'env\' has been detected in the argument list. This '
|
||||
'parameter is no longer used and has been replaced by \'saltenv\' '
|
||||
|
|
|
@ -24,11 +24,12 @@ import logging
|
|||
|
||||
# Import salt libs
|
||||
import salt.fileserver
|
||||
import salt.utils
|
||||
import salt.utils # Can be removed once is_bin_file and get_hash are moved
|
||||
import salt.utils.event
|
||||
import salt.utils.files
|
||||
import salt.utils.gzip_util
|
||||
import salt.utils.path
|
||||
import salt.utils.versions
|
||||
from salt.ext import six
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
@ -39,7 +40,7 @@ def find_file(path, saltenv='base', **kwargs):
|
|||
Search the environment for the relative path.
|
||||
'''
|
||||
if 'env' in kwargs:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Oxygen',
|
||||
'Parameter \'env\' has been detected in the argument list. This '
|
||||
'parameter is no longer used and has been replaced by \'saltenv\' '
|
||||
|
@ -116,7 +117,7 @@ def serve_file(load, fnd):
|
|||
Return a chunk from a file based on the data received
|
||||
'''
|
||||
if 'env' in load:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Oxygen',
|
||||
'Parameter \'env\' has been detected in the argument list. This '
|
||||
'parameter is no longer used and has been replaced by \'saltenv\' '
|
||||
|
@ -217,7 +218,7 @@ def file_hash(load, fnd):
|
|||
Return a file hash, the hash type is set in the master config file
|
||||
'''
|
||||
if 'env' in load:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Oxygen',
|
||||
'Parameter \'env\' has been detected in the argument list. This '
|
||||
'parameter is no longer used and has been replaced by \'saltenv\' '
|
||||
|
@ -297,7 +298,7 @@ def _file_lists(load, form):
|
|||
Return a dict containing the file lists for files, dirs, emtydirs and symlinks
|
||||
'''
|
||||
if 'env' in load:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Oxygen',
|
||||
'Parameter \'env\' has been detected in the argument list. This '
|
||||
'parameter is no longer used and has been replaced by \'saltenv\' '
|
||||
|
@ -443,7 +444,7 @@ def symlink_list(load):
|
|||
Return a dict of all symlinks based on a given path on the Master
|
||||
'''
|
||||
if 'env' in load:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Oxygen',
|
||||
'Parameter \'env\' has been detected in the argument list. This '
|
||||
'parameter is no longer used and has been replaced by \'saltenv\' '
|
||||
|
|
|
@ -71,6 +71,7 @@ import salt.modules
|
|||
import salt.utils
|
||||
import salt.utils.files
|
||||
import salt.utils.gzip_util
|
||||
import salt.utils.versions
|
||||
|
||||
# Import 3rd-party libs
|
||||
# pylint: disable=import-error,no-name-in-module,redefined-builtin
|
||||
|
@ -125,7 +126,7 @@ def find_file(path, saltenv='base', **kwargs):
|
|||
is missing, or if the MD5 does not match.
|
||||
'''
|
||||
if 'env' in kwargs:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Oxygen',
|
||||
'Parameter \'env\' has been detected in the argument list. This '
|
||||
'parameter is no longer used and has been replaced by \'saltenv\' '
|
||||
|
@ -167,7 +168,7 @@ def file_hash(load, fnd):
|
|||
Return an MD5 file hash
|
||||
'''
|
||||
if 'env' in load:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Oxygen',
|
||||
'Parameter \'env\' has been detected in the argument list. This '
|
||||
'parameter is no longer used and has been replaced by \'saltenv\' '
|
||||
|
@ -200,7 +201,7 @@ def serve_file(load, fnd):
|
|||
Return a chunk from a file based on the data received
|
||||
'''
|
||||
if 'env' in load:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Oxygen',
|
||||
'Parameter \'env\' has been detected in the argument list. This '
|
||||
'parameter is no longer used and has been replaced by \'saltenv\' '
|
||||
|
@ -244,7 +245,7 @@ def file_list(load):
|
|||
Return a list of all files on the file server in a specified environment
|
||||
'''
|
||||
if 'env' in load:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Oxygen',
|
||||
'Parameter \'env\' has been detected in the argument list. This '
|
||||
'parameter is no longer used and has been replaced by \'saltenv\' '
|
||||
|
@ -285,7 +286,7 @@ def dir_list(load):
|
|||
Return a list of all directories on the master
|
||||
'''
|
||||
if 'env' in load:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Oxygen',
|
||||
'Parameter \'env\' has been detected in the argument list. This '
|
||||
'parameter is no longer used and has been replaced by \'saltenv\' '
|
||||
|
|
|
@ -58,6 +58,7 @@ import salt.utils
|
|||
import salt.utils.files
|
||||
import salt.utils.gzip_util
|
||||
import salt.utils.url
|
||||
import salt.utils.versions
|
||||
import salt.fileserver
|
||||
from salt.utils.event import tagify
|
||||
|
||||
|
@ -484,7 +485,7 @@ def _env_is_exposed(env):
|
|||
blacklist.
|
||||
'''
|
||||
if __opts__['svnfs_env_whitelist']:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Neon',
|
||||
'The svnfs_env_whitelist config option has been renamed to '
|
||||
'svnfs_saltenv_whitelist. Please update your configuration.'
|
||||
|
@ -494,7 +495,7 @@ def _env_is_exposed(env):
|
|||
whitelist = __opts__['svnfs_saltenv_whitelist']
|
||||
|
||||
if __opts__['svnfs_env_blacklist']:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Neon',
|
||||
'The svnfs_env_blacklist config option has been renamed to '
|
||||
'svnfs_saltenv_blacklist. Please update your configuration.'
|
||||
|
@ -630,7 +631,7 @@ def serve_file(load, fnd):
|
|||
Return a chunk from a file based on the data received
|
||||
'''
|
||||
if 'env' in load:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Oxygen',
|
||||
'Parameter \'env\' has been detected in the argument list. This '
|
||||
'parameter is no longer used and has been replaced by \'saltenv\' '
|
||||
|
@ -664,7 +665,7 @@ def file_hash(load, fnd):
|
|||
Return a file hash, the hash type is set in the master config file
|
||||
'''
|
||||
if 'env' in load:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Oxygen',
|
||||
'Parameter \'env\' has been detected in the argument list. This '
|
||||
'parameter is no longer used and has been replaced by \'saltenv\' '
|
||||
|
@ -722,7 +723,7 @@ def _file_lists(load, form):
|
|||
Return a dict containing the file lists for files, dirs, emptydirs and symlinks
|
||||
'''
|
||||
if 'env' in load:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Oxygen',
|
||||
'Parameter \'env\' has been detected in the argument list. This '
|
||||
'parameter is no longer used and has been replaced by \'saltenv\' '
|
||||
|
|
|
@ -21,7 +21,6 @@ from zipimport import zipimporter
|
|||
# Import salt libs
|
||||
import salt.config
|
||||
import salt.syspaths
|
||||
import salt.utils # Can be removed after warn_until is moved
|
||||
import salt.utils.context
|
||||
import salt.utils.dictupdate
|
||||
import salt.utils.event
|
||||
|
@ -29,6 +28,7 @@ import salt.utils.files
|
|||
import salt.utils.lazy
|
||||
import salt.utils.odict
|
||||
import salt.utils.platform
|
||||
import salt.utils.versions
|
||||
from salt.exceptions import LoaderError
|
||||
from salt.template import check_render_pipe_str
|
||||
from salt.utils.decorators import Depends
|
||||
|
@ -82,14 +82,11 @@ else:
|
|||
# which simplifies code readability, it adds some unsupported functions into
|
||||
# the driver's module scope.
|
||||
# We list un-supported functions here. These will be removed from the loaded.
|
||||
# TODO: remove the need for this cross-module code. Maybe use NotImplemented
|
||||
LIBCLOUD_FUNCS_NOT_SUPPORTED = (
|
||||
u'parallels.avail_sizes',
|
||||
u'parallels.avail_locations',
|
||||
u'proxmox.avail_sizes',
|
||||
u'saltify.destroy',
|
||||
u'saltify.avail_sizes',
|
||||
u'saltify.avail_images',
|
||||
u'saltify.avail_locations',
|
||||
u'rackspace.reboot',
|
||||
u'openstack.list_locations',
|
||||
u'rackspace.list_locations'
|
||||
|
@ -513,7 +510,7 @@ def beacons(opts, functions, context=None, proxy=None):
|
|||
opts,
|
||||
tag=u'beacons',
|
||||
pack={u'__context__': context, u'__salt__': functions, u'__proxy__': proxy or {}},
|
||||
virtual_funcs=[u'__validate__'],
|
||||
virtual_funcs=[],
|
||||
)
|
||||
|
||||
|
||||
|
@ -1484,7 +1481,7 @@ class LazyLoader(salt.utils.lazy.LazyDict):
|
|||
virtual_funcs_to_process = [u'__virtual__'] + self.virtual_funcs
|
||||
for virtual_func in virtual_funcs_to_process:
|
||||
virtual_ret, module_name, virtual_err, virtual_aliases = \
|
||||
self.process_virtual(mod, module_name)
|
||||
self.process_virtual(mod, module_name, virtual_func)
|
||||
if virtual_err is not None:
|
||||
log.trace(
|
||||
u'Error loading %s.%s: %s',
|
||||
|
@ -1717,7 +1714,7 @@ class LazyLoader(salt.utils.lazy.LazyDict):
|
|||
log.trace(u'Loaded %s as virtual %s', module_name, virtual)
|
||||
|
||||
if not hasattr(mod, u'__virtualname__'):
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
u'Hydrogen',
|
||||
u'The \'{0}\' module is renaming itself in its '
|
||||
u'__virtual__() function ({1} => {2}). Please '
|
||||
|
|
|
@ -1453,13 +1453,21 @@ class Minion(MinionBase):
|
|||
function_name = data[u'fun']
|
||||
if function_name in minion_instance.functions:
|
||||
try:
|
||||
minion_blackout_violation = False
|
||||
if minion_instance.connected and minion_instance.opts[u'pillar'].get(u'minion_blackout', False):
|
||||
# this minion is blacked out. Only allow saltutil.refresh_pillar
|
||||
if function_name != u'saltutil.refresh_pillar' and \
|
||||
function_name not in minion_instance.opts[u'pillar'].get(u'minion_blackout_whitelist', []):
|
||||
raise SaltInvocationError(u'Minion in blackout mode. Set \'minion_blackout\' '
|
||||
u'to False in pillar to resume operations. Only '
|
||||
u'saltutil.refresh_pillar allowed in blackout mode.')
|
||||
whitelist = minion_instance.opts[u'pillar'].get(u'minion_blackout_whitelist', [])
|
||||
# this minion is blacked out. Only allow saltutil.refresh_pillar and the whitelist
|
||||
if function_name != u'saltutil.refresh_pillar' and function_name not in whitelist:
|
||||
minion_blackout_violation = True
|
||||
elif minion_instance.opts[u'grains'].get(u'minion_blackout', False):
|
||||
whitelist = minion_instance.opts[u'grains'].get(u'minion_blackout_whitelist', [])
|
||||
if function_name != u'saltutil.refresh_pillar' and function_name not in whitelist:
|
||||
minion_blackout_violation = True
|
||||
if minion_blackout_violation:
|
||||
raise SaltInvocationError(u'Minion in blackout mode. Set \'minion_blackout\' '
|
||||
u'to False in pillar or grains to resume operations. Only '
|
||||
u'saltutil.refresh_pillar allowed in blackout mode.')
|
||||
|
||||
func = minion_instance.functions[function_name]
|
||||
args, kwargs = load_args_and_kwargs(
|
||||
func,
|
||||
|
@ -1622,14 +1630,23 @@ class Minion(MinionBase):
|
|||
for ind in range(0, len(data[u'fun'])):
|
||||
ret[u'success'][data[u'fun'][ind]] = False
|
||||
try:
|
||||
minion_blackout_violation = False
|
||||
if minion_instance.connected and minion_instance.opts[u'pillar'].get(u'minion_blackout', False):
|
||||
# this minion is blacked out. Only allow saltutil.refresh_pillar
|
||||
if data[u'fun'][ind] != u'saltutil.refresh_pillar' and \
|
||||
data[u'fun'][ind] not in minion_instance.opts[u'pillar'].get(u'minion_blackout_whitelist', []):
|
||||
raise SaltInvocationError(u'Minion in blackout mode. Set \'minion_blackout\' '
|
||||
u'to False in pillar to resume operations. Only '
|
||||
u'saltutil.refresh_pillar allowed in blackout mode.')
|
||||
whitelist = minion_instance.opts[u'pillar'].get(u'minion_blackout_whitelist', [])
|
||||
# this minion is blacked out. Only allow saltutil.refresh_pillar and the whitelist
|
||||
if data[u'fun'][ind] != u'saltutil.refresh_pillar' and data[u'fun'][ind] not in whitelist:
|
||||
minion_blackout_violation = True
|
||||
elif minion_instance.opts[u'grains'].get(u'minion_blackout', False):
|
||||
whitelist = minion_instance.opts[u'grains'].get(u'minion_blackout_whitelist', [])
|
||||
if data[u'fun'][ind] != u'saltutil.refresh_pillar' and data[u'fun'][ind] not in whitelist:
|
||||
minion_blackout_violation = True
|
||||
if minion_blackout_violation:
|
||||
raise SaltInvocationError(u'Minion in blackout mode. Set \'minion_blackout\' '
|
||||
u'to False in pillar or grains to resume operations. Only '
|
||||
u'saltutil.refresh_pillar allowed in blackout mode.')
|
||||
|
||||
func = minion_instance.functions[data[u'fun'][ind]]
|
||||
|
||||
args, kwargs = load_args_and_kwargs(
|
||||
func,
|
||||
data[u'arg'][ind],
|
||||
|
|
|
@ -46,6 +46,7 @@ import salt.utils.path
|
|||
import salt.utils.pkg
|
||||
import salt.utils.pkg.deb
|
||||
import salt.utils.systemd
|
||||
import salt.utils.versions
|
||||
from salt.exceptions import (
|
||||
CommandExecutionError, MinionError, SaltInvocationError
|
||||
)
|
||||
|
@ -252,7 +253,7 @@ def latest_version(*names, **kwargs):
|
|||
|
||||
if 'repo' in kwargs:
|
||||
# Remember to kill _get_repo() too when removing this warning.
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Hydrogen',
|
||||
'The \'repo\' argument to apt.latest_version is deprecated, and '
|
||||
'will be removed in Salt {version}. Please use \'fromrepo\' '
|
||||
|
@ -319,7 +320,7 @@ def latest_version(*names, **kwargs):
|
|||
# to the install candidate, then the candidate is an upgrade, so
|
||||
# add it to the return dict
|
||||
if not any(
|
||||
(salt.utils.compare_versions(ver1=x,
|
||||
(salt.utils.versions.compare(ver1=x,
|
||||
oper='>=',
|
||||
ver2=candidate,
|
||||
cmp_func=version_cmp)
|
||||
|
@ -764,12 +765,12 @@ def install(name=None,
|
|||
|
||||
cver = old.get(pkgname, '')
|
||||
if reinstall and cver \
|
||||
and salt.utils.compare_versions(ver1=version_num,
|
||||
and salt.utils.versions.compare(ver1=version_num,
|
||||
oper='==',
|
||||
ver2=cver,
|
||||
cmp_func=version_cmp):
|
||||
to_reinstall[pkgname] = pkgstr
|
||||
elif not cver or salt.utils.compare_versions(ver1=version_num,
|
||||
elif not cver or salt.utils.versions.compare(ver1=version_num,
|
||||
oper='>=',
|
||||
ver2=cver,
|
||||
cmp_func=version_cmp):
|
||||
|
|
|
@ -81,7 +81,7 @@ def add(name, beacon_data, **kwargs):
|
|||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' beacons.add ps "{'salt-master': 'stopped', 'apache2': 'stopped'}"
|
||||
salt '*' beacons.add ps "[{'salt-master': 'stopped', 'apache2': 'stopped'}]"
|
||||
|
||||
'''
|
||||
ret = {'comment': 'Failed to add beacon {0}.'.format(name),
|
||||
|
@ -125,6 +125,7 @@ def add(name, beacon_data, **kwargs):
|
|||
res = __salt__['event.fire']({'name': name, 'beacon_data': beacon_data, 'func': 'add'}, 'manage_beacons')
|
||||
if res:
|
||||
event_ret = eventer.get_event(tag='/salt/minion/minion_beacon_add_complete', wait=30)
|
||||
log.debug('=== event_ret {} ==='.format(event_ret))
|
||||
if event_ret and event_ret['complete']:
|
||||
beacons = event_ret['beacons']
|
||||
if name in beacons and beacons[name] == beacon_data:
|
||||
|
@ -149,7 +150,7 @@ def modify(name, beacon_data, **kwargs):
|
|||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' beacons.modify ps "{'salt-master': 'stopped', 'apache2': 'stopped'}"
|
||||
salt '*' beacons.modify ps "[{'salt-master': 'stopped', 'apache2': 'stopped'}]"
|
||||
'''
|
||||
|
||||
ret = {'comment': '',
|
||||
|
@ -252,6 +253,7 @@ def delete(name, **kwargs):
|
|||
if res:
|
||||
event_ret = eventer.get_event(tag='/salt/minion/minion_beacon_delete_complete', wait=30)
|
||||
if event_ret and event_ret['complete']:
|
||||
log.debug('== event_ret {} =='.format(event_ret))
|
||||
beacons = event_ret['beacons']
|
||||
if name not in beacons:
|
||||
ret['result'] = True
|
||||
|
|
|
@ -1955,7 +1955,7 @@ def list_policy_versions(policy_name,
|
|||
return ret.get('list_policy_versions_response', {}).get('list_policy_versions_result', {}).get('versions')
|
||||
except boto.exception.BotoServerError as e:
|
||||
log.debug(e)
|
||||
msg = 'Failed to list {0} policy vesions.'
|
||||
msg = 'Failed to list {0} policy versions.'
|
||||
log.error(msg.format(policy_name))
|
||||
return []
|
||||
|
||||
|
|
|
@ -53,6 +53,7 @@ import time
|
|||
|
||||
# Import salt libs
|
||||
import salt.utils.compat
|
||||
import salt.utils.versions
|
||||
import salt.utils.odict as odict
|
||||
from salt.exceptions import SaltInvocationError
|
||||
from salt.utils.versions import LooseVersion as _LooseVersion
|
||||
|
@ -255,7 +256,7 @@ def zone_exists(zone, region=None, key=None, keyid=None, profile=None,
|
|||
conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile)
|
||||
|
||||
if retry_on_rate_limit or rate_limit_retries is not None:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Neon',
|
||||
'The \'retry_on_rate_limit\' and \'rate_limit_retries\' arguments '
|
||||
'have been deprecated in favor of \'retry_on_errors\' and '
|
||||
|
@ -387,7 +388,7 @@ def get_record(name, zone, record_type, fetch_all=False, region=None, key=None,
|
|||
conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile)
|
||||
|
||||
if retry_on_rate_limit or rate_limit_retries is not None:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Neon',
|
||||
'The \'retry_on_rate_limit\' and \'rate_limit_retries\' arguments '
|
||||
'have been deprecated in favor of \'retry_on_errors\' and '
|
||||
|
@ -468,7 +469,7 @@ def add_record(name, value, zone, record_type, identifier=None, ttl=None,
|
|||
conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile)
|
||||
|
||||
if retry_on_rate_limit or rate_limit_retries is not None:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Neon',
|
||||
'The \'retry_on_rate_limit\' and \'rate_limit_retries\' arguments '
|
||||
'have been deprecated in favor of \'retry_on_errors\' and '
|
||||
|
@ -555,7 +556,7 @@ def update_record(name, value, zone, record_type, identifier=None, ttl=None,
|
|||
_type = record_type.upper()
|
||||
|
||||
if retry_on_rate_limit or rate_limit_retries is not None:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Neon',
|
||||
'The \'retry_on_rate_limit\' and \'rate_limit_retries\' arguments '
|
||||
'have been deprecated in favor of \'retry_on_errors\' and '
|
||||
|
@ -617,7 +618,7 @@ def delete_record(name, zone, record_type, identifier=None, all_records=False,
|
|||
_type = record_type.upper()
|
||||
|
||||
if retry_on_rate_limit or rate_limit_retries is not None:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Neon',
|
||||
'The \'retry_on_rate_limit\' and \'rate_limit_retries\' arguments '
|
||||
'have been deprecated in favor of \'retry_on_errors\' and '
|
||||
|
|
|
@ -137,6 +137,7 @@ import random
|
|||
import salt.utils.boto
|
||||
import salt.utils.boto3
|
||||
import salt.utils.compat
|
||||
import salt.utils.versions
|
||||
from salt.exceptions import SaltInvocationError, CommandExecutionError
|
||||
from salt.utils.versions import LooseVersion as _LooseVersion
|
||||
|
||||
|
@ -2456,9 +2457,10 @@ def describe_route_table(route_table_id=None, route_table_name=None,
|
|||
|
||||
'''
|
||||
|
||||
salt.utils.warn_until('Oxygen',
|
||||
'The \'describe_route_table\' method has been deprecated and '
|
||||
'replaced by \'describe_route_tables\'.'
|
||||
salt.utils.versions.warn_until(
|
||||
'Oxygen',
|
||||
'The \'describe_route_table\' method has been deprecated and '
|
||||
'replaced by \'describe_route_tables\'.'
|
||||
)
|
||||
if not any((route_table_id, route_table_name, tags)):
|
||||
raise SaltInvocationError('At least one of the following must be specified: '
|
||||
|
|
|
@ -33,6 +33,7 @@ import salt.utils.powershell
|
|||
import salt.utils.stringutils
|
||||
import salt.utils.templates
|
||||
import salt.utils.timed_subprocess
|
||||
import salt.utils.versions
|
||||
import salt.utils.vt
|
||||
import salt.grains.extra
|
||||
from salt.ext import six
|
||||
|
@ -2119,7 +2120,7 @@ def script(source,
|
|||
)
|
||||
|
||||
if '__env__' in kwargs:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Oxygen',
|
||||
'Parameter \'__env__\' has been detected in the argument list. This '
|
||||
'parameter is no longer used and has been replaced by \'saltenv\' '
|
||||
|
@ -2335,7 +2336,7 @@ def script_retcode(source,
|
|||
salt '*' cmd.script_retcode salt://scripts/runme.sh stdin='one\\ntwo\\nthree\\nfour\\nfive\\n'
|
||||
'''
|
||||
if '__env__' in kwargs:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Oxygen',
|
||||
'Parameter \'__env__\' has been detected in the argument list. This '
|
||||
'parameter is no longer used and has been replaced by \'saltenv\' '
|
||||
|
|
|
@ -2,10 +2,7 @@
|
|||
'''
|
||||
An execution module that interacts with the Datadog API
|
||||
|
||||
Common parameters:
|
||||
|
||||
scope
|
||||
The scope of the request
|
||||
The following parameters are required for all functions.
|
||||
|
||||
api_key
|
||||
The datadog API key
|
||||
|
@ -45,6 +42,10 @@ def _initialize_connection(api_key, app_key):
|
|||
'''
|
||||
Initialize Datadog connection
|
||||
'''
|
||||
if api_key is None:
|
||||
raise SaltInvocationError('api_key must be specified')
|
||||
if app_key is None:
|
||||
raise SaltInvocationError('app_key must be specified')
|
||||
options = {
|
||||
'api_key': api_key,
|
||||
'app_key': app_key
|
||||
|
@ -52,31 +53,36 @@ def _initialize_connection(api_key, app_key):
|
|||
datadog.initialize(**options)
|
||||
|
||||
|
||||
def schedule_downtime(scope, api_key=None, app_key=None, monitor_id=None,
|
||||
start=None, end=None, message=None, recurrence=None,
|
||||
timezone=None, test=False):
|
||||
def schedule_downtime(scope,
|
||||
api_key=None,
|
||||
app_key=None,
|
||||
monitor_id=None,
|
||||
start=None,
|
||||
end=None,
|
||||
message=None,
|
||||
recurrence=None,
|
||||
timezone=None,
|
||||
test=False):
|
||||
'''
|
||||
Schedule downtime for a scope of monitors.
|
||||
|
||||
monitor_id
|
||||
The ID of the monitor
|
||||
start
|
||||
Start time in seconds since the epoch
|
||||
end
|
||||
End time in seconds since the epoch
|
||||
message
|
||||
A message to send in a notification for this downtime
|
||||
recurrence
|
||||
Repeat this downtime periodically
|
||||
timezone
|
||||
Specify the timezone
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-call datadog.schedule_downtime 'host:app2' stop=$(date --date='30
|
||||
minutes' +%s) app_key=<app_key> api_key=<api_key>
|
||||
salt-call datadog.schedule_downtime 'host:app2' \\
|
||||
stop=$(date --date='30 minutes' +%s) \\
|
||||
app_key='0123456789' \\
|
||||
api_key='9876543210'
|
||||
|
||||
Optional arguments
|
||||
|
||||
:param monitor_id: The ID of the monitor
|
||||
:param start: Start time in seconds since the epoch
|
||||
:param end: End time in seconds since the epoch
|
||||
:param message: A message to send in a notification for this downtime
|
||||
:param recurrence: Repeat this downtime periodically
|
||||
:param timezone: Specify the timezone
|
||||
'''
|
||||
ret = {'result': False,
|
||||
'response': None,
|
||||
|
@ -114,21 +120,25 @@ def schedule_downtime(scope, api_key=None, app_key=None, monitor_id=None,
|
|||
return ret
|
||||
|
||||
|
||||
def cancel_downtime(api_key=None, app_key=None, scope=None, id=None):
|
||||
def cancel_downtime(api_key=None,
|
||||
app_key=None,
|
||||
scope=None,
|
||||
id=None):
|
||||
'''
|
||||
Cancel a downtime by id or by scope.
|
||||
|
||||
Either scope or id is required.
|
||||
|
||||
id
|
||||
The ID of the downtime
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-call datadog.cancel_downtime scope='host:app01' api_key=<api_key>
|
||||
app_key=<app_key>`
|
||||
salt-call datadog.cancel_downtime scope='host:app01' \\
|
||||
api_key='0123456789' \\
|
||||
app_key='9876543210'`
|
||||
|
||||
Arguments - Either scope or id is required.
|
||||
|
||||
:param id: The downtime ID
|
||||
:param scope: The downtime scope
|
||||
'''
|
||||
if api_key is None:
|
||||
raise SaltInvocationError('api_key must be specified')
|
||||
|
@ -153,9 +163,9 @@ def cancel_downtime(api_key=None, app_key=None, scope=None, id=None):
|
|||
'scope': scope
|
||||
}
|
||||
response = requests.post(
|
||||
'https://app.datadoghq.com/api/v1/downtime/cancel/by_scope',
|
||||
params=params
|
||||
)
|
||||
'https://app.datadoghq.com/api/v1/downtime/cancel/by_scope',
|
||||
params=params
|
||||
)
|
||||
if response.status_code == 200:
|
||||
ret['result'] = True
|
||||
ret['response'] = response.json()
|
||||
|
@ -168,3 +178,87 @@ def cancel_downtime(api_key=None, app_key=None, scope=None, id=None):
|
|||
raise SaltInvocationError('One of id or scope must be specified')
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def post_event(api_key=None,
|
||||
app_key=None,
|
||||
title=None,
|
||||
text=None,
|
||||
date_happened=None,
|
||||
priority=None,
|
||||
host=None,
|
||||
tags=None,
|
||||
alert_type=None,
|
||||
aggregation_key=None,
|
||||
source_type_name=None):
|
||||
'''
|
||||
Post an event to the Datadog stream.
|
||||
|
||||
CLI Example
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-call datadog.post_event api_key='0123456789' \\
|
||||
app_key='9876543210' \\
|
||||
title='Salt Highstate' \\
|
||||
text="Salt highstate was run on $(salt-call grains.get id)" \\
|
||||
tags='["service:salt", "event:highstate"]'
|
||||
|
||||
Required arguments
|
||||
|
||||
:param title: The event title. Limited to 100 characters.
|
||||
:param text: The body of the event. Limited to 4000 characters. The text
|
||||
supports markdown.
|
||||
|
||||
Optional arguments
|
||||
|
||||
:param date_happened: POSIX timestamp of the event.
|
||||
:param priority: The priority of the event ('normal' or 'low').
|
||||
:param host: Host name to associate with the event.
|
||||
:param tags: A list of tags to apply to the event.
|
||||
:param alert_type: "error", "warning", "info" or "success".
|
||||
:param aggregation_key: An arbitrary string to use for aggregation,
|
||||
max length of 100 characters.
|
||||
:param source_type_name: The type of event being posted.
|
||||
'''
|
||||
_initialize_connection(api_key, app_key)
|
||||
if title is None:
|
||||
raise SaltInvocationError('title must be specified')
|
||||
if text is None:
|
||||
raise SaltInvocationError('text must be specified')
|
||||
if alert_type not in [None, 'error', 'warning', 'info', 'success']:
|
||||
# Datadog only supports these alert types but the API doesn't return an
|
||||
# error for an incorrect alert_type, so we can do it here for now.
|
||||
# https://github.com/DataDog/datadogpy/issues/215
|
||||
message = ('alert_type must be one of "error", "warning", "info", or '
|
||||
'"success"')
|
||||
raise SaltInvocationError(message)
|
||||
|
||||
ret = {'result': False,
|
||||
'response': None,
|
||||
'comment': ''}
|
||||
|
||||
try:
|
||||
response = datadog.api.Event.create(title=title,
|
||||
text=text,
|
||||
date_happened=date_happened,
|
||||
priority=priority,
|
||||
host=host,
|
||||
tags=tags,
|
||||
alert_type=alert_type,
|
||||
aggregation_key=aggregation_key,
|
||||
source_type_name=source_type_name
|
||||
)
|
||||
except ValueError:
|
||||
comment = ('Unexpected exception in Datadog Post Event API '
|
||||
'call. Are your keys correct?')
|
||||
ret['comment'] = comment
|
||||
return ret
|
||||
|
||||
ret['response'] = response
|
||||
if 'status' in response.keys():
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Successfully sent event'
|
||||
else:
|
||||
ret['comment'] = 'Error in posting event.'
|
||||
return ret
|
||||
|
|
|
@ -13,6 +13,7 @@ import re
|
|||
import salt.utils
|
||||
import salt.utils.path
|
||||
import salt.utils.files
|
||||
import salt.utils.versions
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -185,7 +186,7 @@ def set_file(path, saltenv='base', **kwargs):
|
|||
salt '*' debconf.set_file salt://pathto/pkg.selections
|
||||
'''
|
||||
if '__env__' in kwargs:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Oxygen',
|
||||
'Parameter \'__env__\' has been detected in the argument list. This '
|
||||
'parameter is no longer used and has been replaced by \'saltenv\' '
|
||||
|
|
|
@ -26,6 +26,7 @@ import salt.utils.args
|
|||
import salt.utils.path
|
||||
import salt.utils.pkg
|
||||
import salt.utils.systemd
|
||||
import salt.utils.versions
|
||||
from salt.exceptions import CommandExecutionError, MinionError
|
||||
from salt.ext import six
|
||||
|
||||
|
@ -236,7 +237,7 @@ def latest_version(*names, **kwargs):
|
|||
ret[name] = ''
|
||||
installed = _cpv_to_version(_vartree().dep_bestmatch(name))
|
||||
avail = _cpv_to_version(_porttree().dep_bestmatch(name))
|
||||
if avail and (not installed or salt.utils.compare_versions(ver1=installed, oper='<', ver2=avail, cmp_func=version_cmp)):
|
||||
if avail and (not installed or salt.utils.versions.compare(ver1=installed, oper='<', ver2=avail, cmp_func=version_cmp)):
|
||||
ret[name] = avail
|
||||
|
||||
# Return a string if only one package name passed
|
||||
|
|
|
@ -35,8 +35,6 @@ def useradd(pwfile, user, password, opts='', runas=None):
|
|||
Add a user to htpasswd file using the htpasswd command. If the htpasswd
|
||||
file does not exist, it will be created.
|
||||
|
||||
.. deprecated:: 2016.3.0
|
||||
|
||||
pwfile
|
||||
Path to htpasswd file
|
||||
|
||||
|
|
|
@ -56,6 +56,14 @@ try:
|
|||
except ImportError:
|
||||
HAS_LIBS = False
|
||||
|
||||
try:
|
||||
# There is an API change in Kubernetes >= 2.0.0.
|
||||
from kubernetes.client import V1beta1Deployment as AppsV1beta1Deployment
|
||||
from kubernetes.client import V1beta1DeploymentSpec as AppsV1beta1DeploymentSpec
|
||||
except ImportError:
|
||||
from kubernetes.client import AppsV1beta1Deployment
|
||||
from kubernetes.client import AppsV1beta1DeploymentSpec
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -876,7 +884,7 @@ def create_deployment(
|
|||
'''
|
||||
body = __create_object_body(
|
||||
kind='Deployment',
|
||||
obj_class=kubernetes.client.V1beta1Deployment,
|
||||
obj_class=AppsV1beta1Deployment,
|
||||
spec_creator=__dict_to_deployment_spec,
|
||||
name=name,
|
||||
namespace=namespace,
|
||||
|
@ -1139,7 +1147,7 @@ def replace_deployment(name,
|
|||
'''
|
||||
body = __create_object_body(
|
||||
kind='Deployment',
|
||||
obj_class=kubernetes.client.V1beta1Deployment,
|
||||
obj_class=AppsV1beta1Deployment,
|
||||
spec_creator=__dict_to_deployment_spec,
|
||||
name=name,
|
||||
namespace=namespace,
|
||||
|
@ -1410,9 +1418,9 @@ def __dict_to_object_meta(name, namespace, metadata):
|
|||
|
||||
def __dict_to_deployment_spec(spec):
|
||||
'''
|
||||
Converts a dictionary into kubernetes V1beta1DeploymentSpec instance.
|
||||
Converts a dictionary into kubernetes AppsV1beta1DeploymentSpec instance.
|
||||
'''
|
||||
spec_obj = kubernetes.client.V1beta1DeploymentSpec()
|
||||
spec_obj = AppsV1beta1DeploymentSpec()
|
||||
for key, value in iteritems(spec):
|
||||
if hasattr(spec_obj, key):
|
||||
setattr(spec_obj, key, value)
|
||||
|
|
|
@ -430,7 +430,7 @@ def lvcreate(lvname,
|
|||
cmd.extend(extra_arguments)
|
||||
|
||||
if force:
|
||||
cmd.append('-yes')
|
||||
cmd.append('--yes')
|
||||
|
||||
out = __salt__['cmd.run'](cmd, python_shell=False).splitlines()
|
||||
lvdev = '/dev/{0}/{1}'.format(vgname, lvname)
|
||||
|
|
|
@ -20,6 +20,7 @@ import logging
|
|||
import salt.utils
|
||||
import salt.utils.path
|
||||
import salt.utils.pkg
|
||||
import salt.utils.versions
|
||||
from salt.exceptions import CommandExecutionError, MinionError
|
||||
from salt.ext import six
|
||||
from salt.ext.six.moves import zip
|
||||
|
@ -128,7 +129,7 @@ def list_pkgs(versions_as_list=False, **kwargs):
|
|||
name_and_versions = line.split(' ')
|
||||
name = name_and_versions[0]
|
||||
installed_versions = name_and_versions[1:]
|
||||
key_func = functools.cmp_to_key(salt.utils.version_cmp)
|
||||
key_func = functools.cmp_to_key(salt.utils.versions.version_cmp)
|
||||
newest_version = sorted(installed_versions, key=key_func).pop()
|
||||
except ValueError:
|
||||
continue
|
||||
|
|
|
@ -42,6 +42,7 @@ import salt.utils.path
|
|||
import salt.utils.pkg
|
||||
import salt.utils.platform
|
||||
import salt.utils.mac_utils
|
||||
import salt.utils.versions
|
||||
from salt.exceptions import CommandExecutionError
|
||||
|
||||
# Import 3rd-party libs
|
||||
|
@ -170,7 +171,7 @@ def latest_version(*names, **kwargs):
|
|||
ret = {}
|
||||
|
||||
for key, val in six.iteritems(available):
|
||||
if key not in installed or salt.utils.compare_versions(ver1=installed[key], oper='<', ver2=val):
|
||||
if key not in installed or salt.utils.versions.compare(ver1=installed[key], oper='<', ver2=val):
|
||||
ret[key] = val
|
||||
else:
|
||||
ret[key] = '{0} (installed)'.format(version(key))
|
||||
|
|
|
@ -11,7 +11,7 @@ import sys
|
|||
|
||||
# Import salt libs
|
||||
import salt.minion
|
||||
import salt.utils
|
||||
import salt.utils.versions
|
||||
from salt.defaults import DEFAULT_TARGET_DELIM
|
||||
from salt.ext.six import string_types
|
||||
|
||||
|
@ -338,7 +338,7 @@ def filter_by(lookup,
|
|||
# remember to remove the expr_form argument from this function when
|
||||
# performing the cleanup on this deprecation.
|
||||
if expr_form is not None:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Fluorine',
|
||||
'the target type should be passed using the \'tgt_type\' '
|
||||
'argument instead of \'expr_form\'. Support for using '
|
||||
|
|
|
@ -15,8 +15,9 @@ import salt.crypt
|
|||
import salt.payload
|
||||
import salt.utils
|
||||
import salt.utils.args
|
||||
import salt.utils.network
|
||||
import salt.utils.event
|
||||
import salt.utils.network
|
||||
import salt.utils.versions
|
||||
from salt.exceptions import SaltClientError
|
||||
|
||||
# Import 3rd-party libs
|
||||
|
@ -292,7 +293,7 @@ def get(tgt,
|
|||
# remember to remove the expr_form argument from this function when
|
||||
# performing the cleanup on this deprecation.
|
||||
if expr_form is not None:
|
||||
salt.utils.warn_until(
|
||||
salt.utils.versions.warn_until(
|
||||
'Fluorine',
|
||||
'the target type should be passed using the \'tgt_type\' '
|
||||
'argument instead of \'expr_form\'. Support for using '
|
||||
|
|
|
@ -152,7 +152,8 @@ Optional small program to encrypt data without needing salt modules.
|
|||
from __future__ import absolute_import
|
||||
import base64
|
||||
import os
|
||||
import salt.utils
|
||||
import salt.utils.files
|
||||
import salt.utils.platform
|
||||
import salt.utils.win_functions
|
||||
import salt.utils.win_dacl
|
||||
import salt.syspaths
|
||||
|
@ -203,7 +204,7 @@ def _get_sk(**kwargs):
|
|||
key = config['sk']
|
||||
sk_file = config['sk_file']
|
||||
if not key and sk_file:
|
||||
with salt.utils.fopen(sk_file, 'rb') as keyf:
|
||||
with salt.utils.files.fopen(sk_file, 'rb') as keyf:
|
||||
key = str(keyf.read()).rstrip('\n')
|
||||
if key is None:
|
||||
raise Exception('no key or sk_file found')
|
||||
|
@ -218,7 +219,7 @@ def _get_pk(**kwargs):
|
|||
pubkey = config['pk']
|
||||
pk_file = config['pk_file']
|
||||
if not pubkey and pk_file:
|
||||
with salt.utils.fopen(pk_file, 'rb') as keyf:
|
||||
with salt.utils.files.fopen(pk_file, 'rb') as keyf:
|
||||
pubkey = str(keyf.read()).rstrip('\n')
|
||||
if pubkey is None:
|
||||
raise Exception('no pubkey or pk_file found')
|
||||
|
@ -256,9 +257,9 @@ def keygen(sk_file=None, pk_file=None):
|
|||
if sk_file and pk_file is None:
|
||||
if not os.path.isfile(sk_file):
|
||||
kp = libnacl.public.SecretKey()
|
||||
with salt.utils.fopen(sk_file, 'w') as keyf:
|
||||
with salt.utils.files.fopen(sk_file, 'w') as keyf:
|
||||
keyf.write(base64.b64encode(kp.sk))
|
||||
if salt.utils.is_windows():
|
||||
if salt.utils.platform.is_windows():
|
||||
cur_user = salt.utils.win_functions.get_current_user()
|
||||
salt.utils.win_dacl.set_owner(sk_file, cur_user)
|
||||
salt.utils.win_dacl.set_permissions(sk_file, cur_user, 'full_control', 'grant', reset_perms=True, protected=True)
|
||||
|
@ -277,25 +278,25 @@ def keygen(sk_file=None, pk_file=None):
|
|||
|
||||
if os.path.isfile(sk_file) and not os.path.isfile(pk_file):
|
||||
# generate pk using the sk
|
||||
with salt.utils.fopen(sk_file, 'rb') as keyf:
|
||||
with salt.utils.files.fopen(sk_file, 'rb') as keyf:
|
||||
sk = str(keyf.read()).rstrip('\n')
|
||||
sk = base64.b64decode(sk)
|
||||
kp = libnacl.public.SecretKey(sk)
|
||||
with salt.utils.fopen(pk_file, 'w') as keyf:
|
||||
with salt.utils.files.fopen(pk_file, 'w') as keyf:
|
||||
keyf.write(base64.b64encode(kp.pk))
|
||||
return 'saved pk_file: {0}'.format(pk_file)
|
||||
|
||||
kp = libnacl.public.SecretKey()
|
||||
with salt.utils.fopen(sk_file, 'w') as keyf:
|
||||
with salt.utils.files.fopen(sk_file, 'w') as keyf:
|
||||
keyf.write(base64.b64encode(kp.sk))
|
||||
if salt.utils.is_windows():
|
||||
if salt.utils.platform.is_windows():
|
||||
cur_user = salt.utils.win_functions.get_current_user()
|
||||
salt.utils.win_dacl.set_owner(sk_file, cur_user)
|
||||
salt.utils.win_dacl.set_permissions(sk_file, cur_user, 'full_control', 'grant', reset_perms=True, protected=True)
|
||||
else:
|
||||
# chmod 0600 file
|
||||
os.chmod(sk_file, 1536)
|
||||
with salt.utils.fopen(pk_file, 'w') as keyf:
|
||||
with salt.utils.files.fopen(pk_file, 'w') as keyf:
|
||||
keyf.write(base64.b64encode(kp.pk))
|
||||
return 'saved sk_file:{0} pk_file: {1}'.format(sk_file, pk_file)
|
||||
|
||||
|
@ -335,13 +336,13 @@ def enc_file(name, out=None, **kwargs):
|
|||
data = __salt__['cp.get_file_str'](name)
|
||||
except Exception as e:
|
||||
# likly using salt-run so fallback to local filesystem
|
||||
with salt.utils.fopen(name, 'rb') as f:
|
||||
with salt.utils.files.fopen(name, 'rb') as f:
|
||||
data = f.read()
|
||||
d = enc(data, **kwargs)
|
||||
if out:
|
||||
if os.path.isfile(out):
|
||||
raise Exception('file:{0} already exist.'.format(out))
|
||||
with salt.utils.fopen(out, 'wb') as f:
|
||||
with salt.utils.files.fopen(out, 'wb') as f:
|
||||
f.write(d)
|
||||
return 'Wrote: {0}'.format(out)
|
||||
return d
|
||||
|
@ -382,13 +383,13 @@ def dec_file(name, out=None, **kwargs):
|
|||
data = __salt__['cp.get_file_str'](name)
|
||||
except Exception as e:
|
||||
# likly using salt-run so fallback to local filesystem
|
||||
with salt.utils.fopen(name, 'rb') as f:
|
||||
with salt.utils.files.fopen(name, 'rb') as f:
|
||||
data = f.read()
|
||||
d = dec(data, **kwargs)
|
||||
if out:
|
||||
if os.path.isfile(out):
|
||||
raise Exception('file:{0} already exist.'.format(out))
|
||||
with salt.utils.fopen(out, 'wb') as f:
|
||||
with salt.utils.files.fopen(out, 'wb') as f:
|
||||
f.write(d)
|
||||
return 'Wrote: {0}'.format(out)
|
||||
return d
|
||||
|
|
|
@ -30,6 +30,7 @@ import logging
|
|||
|
||||
# Import Salt libs
|
||||
import salt.utils
|
||||
import salt.utils.versions
|
||||
from salt.exceptions import CommandExecutionError, MinionError
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
@ -123,7 +124,7 @@ def latest_version(*names, **kwargs):
|
|||
continue
|
||||
pkgname += '--{0}'.format(flavor) if flavor else ''
|
||||
cur = pkgs.get(pkgname, '')
|
||||
if not cur or salt.utils.compare_versions(ver1=cur,
|
||||
if not cur or salt.utils.versions.compare(ver1=cur,
|
||||
oper='<',
|
||||
ver2=pkgver):
|
||||
ret[pkgname] = pkgver
|
||||
|
|
|
@ -24,13 +24,13 @@ import re
|
|||
import logging
|
||||
|
||||
# Import salt libs
|
||||
import salt.utils # Can be removed when is_true, compare_versions, compare_dicts are moved
|
||||
import salt.utils # Can be removed when is_true, compare_dicts are moved
|
||||
import salt.utils.args
|
||||
import salt.utils.files
|
||||
import salt.utils.itertools
|
||||
import salt.utils.path
|
||||
import salt.utils.pkg
|
||||
from salt.utils.versions import LooseVersion as _LooseVersion
|
||||
import salt.utils.versions
|
||||
from salt.exceptions import (
|
||||
CommandExecutionError, MinionError, SaltInvocationError
|
||||
)
|
||||
|
@ -327,13 +327,13 @@ def install(name=None,
|
|||
else:
|
||||
pkgstr = '{0}={1}'.format(pkgname, version_num)
|
||||
cver = old.get(pkgname, '')
|
||||
if reinstall and cver and salt.utils.compare_versions(
|
||||
if reinstall and cver and salt.utils.versions.compare(
|
||||
ver1=version_num,
|
||||
oper='==',
|
||||
ver2=cver,
|
||||
cmp_func=version_cmp):
|
||||
to_reinstall.append(pkgstr)
|
||||
elif not cver or salt.utils.compare_versions(
|
||||
elif not cver or salt.utils.versions.compare(
|
||||
ver1=version_num,
|
||||
oper='>=',
|
||||
ver2=cver,
|
||||
|
@ -1014,7 +1014,8 @@ def version_cmp(pkg1, pkg2, ignore_epoch=False):
|
|||
output_loglevel='trace',
|
||||
python_shell=False)
|
||||
opkg_version = output.split(' ')[2].strip()
|
||||
if _LooseVersion(opkg_version) >= _LooseVersion('0.3.4'):
|
||||
if salt.utils.versions.LooseVersion(opkg_version) >= \
|
||||
salt.utils.versions.LooseVersion('0.3.4'):
|
||||
cmd_compare = ['opkg', 'compare-versions']
|
||||
elif salt.utils.path.which('opkg-compare-versions'):
|
||||
cmd_compare = ['opkg-compare-versions']
|
||||
|
|
|
@ -58,11 +58,11 @@ def _osquery(sql, format='json'):
|
|||
|
||||
cmd = 'osqueryi --json "{0}"'.format(sql)
|
||||
res = __salt__['cmd.run_all'](cmd)
|
||||
if res['retcode'] == 0:
|
||||
ret['data'] = json.loads(res['stdout'])
|
||||
else:
|
||||
if res['stderr']:
|
||||
ret['result'] = False
|
||||
ret['error'] = res['stderr']
|
||||
else:
|
||||
ret['data'] = json.loads(res['stdout'])
|
||||
return ret
|
||||
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue