mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Merge remote-tracking branch 'upstream/2015.2' into merge-forward-develop
Conflicts: salt/cli/salt.py salt/client/mixins.py salt/renderers/pyobjects.py
This commit is contained in:
commit
1a38b4b834
29 changed files with 3045 additions and 862 deletions
|
@ -67,7 +67,7 @@ help:
|
|||
|
||||
clean:
|
||||
rm -rf $(BUILDDIR)/*
|
||||
test -d 'locale' && find locale/ -name *.mo -exec rm {} \;
|
||||
test -d 'locale' && find locale/ -name *.mo -exec rm {} \; || true
|
||||
|
||||
html: translations
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SALT-API" "1" "March 09, 2015" "2015.2.0rc1-133-g24fa806" "Salt"
|
||||
.TH "SALT-API" "1" "April 21, 2015" "2015.2.0rc2-143-g32ef5ca" "Salt"
|
||||
.SH NAME
|
||||
salt-api \- salt-api Command
|
||||
.
|
||||
|
@ -49,13 +49,57 @@ The Salt API system manages network api connectors for the Salt Master
|
|||
.SH OPTIONS
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-h, \-\-help
|
||||
Print a usage message briefly summarizing these command\-line options.
|
||||
.B \-\-version
|
||||
Print the version of Salt that is running.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-C CONFIG, \-\-config=CONFIG
|
||||
Specify an alternative location for the salt master configuration file.
|
||||
.B \-\-versions\-report
|
||||
Show program\(aqs dependencies and version number, and then exit
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-h, \-\-help
|
||||
Show the help message and exit
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-c CONFIG_DIR, \-\-config\-dir=CONFIG_dir
|
||||
The location of the Salt configuration directory. This directory contains
|
||||
the configuration files for Salt master and minions. The default location
|
||||
on most systems is \fB/etc/salt\fP\&.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-d, \-\-daemon
|
||||
Run the salt\-api as a daemon
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-pid\-file=PIDFILE
|
||||
Specify the location of the pidfile. Default: /var/run/salt\-api.pid
|
||||
.UNINDENT
|
||||
.SS Logging Options
|
||||
.sp
|
||||
Logging options which override any settings defined on the configuration files.
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-l LOG_LEVEL, \-\-log\-level=LOG_LEVEL
|
||||
Console logging log level. One of \fBall\fP, \fBgarbage\fP, \fBtrace\fP,
|
||||
\fBdebug\fP, \fBinfo\fP, \fBwarning\fP, \fBerror\fP, \fBquiet\fP\&. Default:
|
||||
\fBwarning\fP\&.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-log\-file=LOG_FILE
|
||||
Log file path. Default: /var/log/salt/api\&.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-log\-file\-level=LOG_LEVEL_LOGFILE
|
||||
Logfile logging log level. One of \fBall\fP, \fBgarbage\fP, \fBtrace\fP,
|
||||
\fBdebug\fP, \fBinfo\fP, \fBwarning\fP, \fBerror\fP, \fBquiet\fP\&. Default:
|
||||
\fBwarning\fP\&.
|
||||
.UNINDENT
|
||||
.SH SEE ALSO
|
||||
.sp
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SALT-CLOUD" "1" "March 09, 2015" "2015.2.0rc1-133-g24fa806" "Salt"
|
||||
.TH "SALT-CLOUD" "1" "April 21, 2015" "2015.2.0rc2-143-g32ef5ca" "Salt"
|
||||
.SH NAME
|
||||
salt-cloud \- Salt Cloud Command
|
||||
.
|
||||
|
@ -58,22 +58,57 @@ clouds via a cleanly controlled profile and mapping system.
|
|||
.SH OPTIONS
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-version
|
||||
Print the version of Salt that is running.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-versions\-report
|
||||
Show program\(aqs dependencies and version number, and then exit
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-h, \-\-help
|
||||
Print a usage message briefly summarizing these command\-line options.
|
||||
Show the help message and exit
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-c CONFIG_DIR, \-\-config\-dir=CONFIG_dir
|
||||
The location of the Salt configuration directory. This directory contains
|
||||
the configuration files for Salt master and minions. The default location
|
||||
on most systems is \fB/etc/salt\fP\&.
|
||||
.UNINDENT
|
||||
.SS Execution Options
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-L LOCATION, \-\-location=LOCATION
|
||||
Specify which region to connect to.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-a ACTION, \-\-action=ACTION
|
||||
Perform an action that may be specific to this cloud provider. This
|
||||
argument requires one or more instance names to be specified.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-f <FUNC\-NAME> <PROVIDER>, \-\-function=<FUNC\-NAME> <PROVIDER>
|
||||
Perform an function that may be specific to this cloud provider, that does
|
||||
not apply to an instance. This argument requires a provider to be specified
|
||||
(i.e.: nova).
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-p PROFILE, \-\-profile=PROFILE
|
||||
Select a single profile to build the named cloud VMs from. The profile
|
||||
must be defined in the specified profiles file.
|
||||
Select a single profile to build the named cloud VMs from. The profile must
|
||||
be defined in the specified profiles file.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-m MAP, \-\-map=MAP
|
||||
Specify a map file to use. If used without any other options, this option
|
||||
will ensure that all of the mapped VMs are created. If VM names are
|
||||
also passed as arguments, they will be used to filter the map file.
|
||||
If the named VM already exists then it will be skipped.
|
||||
will ensure that all of the mapped VMs are created. If the named VM already
|
||||
exists then it will be skipped.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
|
@ -112,6 +147,38 @@ in conjunction with \-m to display only information about the specified map.
|
|||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-u, \-\-update\-bootstrap
|
||||
Update salt\-bootstrap to the latest develop version on GitHub.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-y, \-\-assume\-yes
|
||||
Default yes in answer to all confirmation questions.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-k, \-\-keep\-tmp
|
||||
Do not remove files from /tmp/ after deploy.sh finishes.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-show\-deploy\-args
|
||||
Include the options used to deploy the minion in the data returned.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-script\-args=SCRIPT_ARGS
|
||||
Script arguments to be fed to the bootstrap script when deploying the VM.
|
||||
.UNINDENT
|
||||
.SS Query Options
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-Q, \-\-query
|
||||
Execute a query and return some information about the nodes running on
|
||||
configured cloud providers
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-F, \-\-full\-query
|
||||
Execute a query and print out all available information about all cloud VMs.
|
||||
Can be used in conjunction with \-m to display only information about the
|
||||
|
@ -139,58 +206,98 @@ Display a list of configured profiles. Pass in a cloud provider to view
|
|||
the provider\(aqs associated profiles, such as \fBdigital_ocean\fP, or pass in
|
||||
\fBall\fP to list all the configured profiles.
|
||||
.UNINDENT
|
||||
.SS Cloud Providers Listings
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-list\-images
|
||||
Display a list of images available in configured cloud providers.
|
||||
Pass the cloud provider that available images are desired on, aka
|
||||
"linode", or pass "all" to list images for all configured cloud providers.
|
||||
.B \-\-list\-locations=LIST_LOCATIONS
|
||||
Display a list of locations available in configured cloud providers. Pass
|
||||
the cloud provider that available locations are desired on, aka "linode",
|
||||
or pass "all" to list locations for all configured cloud providers
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-list\-sizes
|
||||
.B \-\-list\-images=LIST_IMAGES
|
||||
Display a list of images available in configured cloud providers. Pass the
|
||||
cloud provider that available images are desired on, aka "linode", or pass
|
||||
"all" to list images for all configured cloud providers
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-list\-sizes=LIST_SIZES
|
||||
Display a list of sizes available in configured cloud providers. Pass the
|
||||
cloud provider that available sizes are desired on, aka "aws", or pass
|
||||
cloud provider that available sizes are desired on, aka "AWS", or pass
|
||||
"all" to list sizes for all configured cloud providers
|
||||
.UNINDENT
|
||||
.SS Cloud Credentials
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-C CLOUD_CONFIG, \-\-cloud\-config=CLOUD_CONFIG
|
||||
Specify an alternative location for the salt cloud configuration file.
|
||||
Default location is /etc/salt/cloud.
|
||||
.B \-\-set\-password=<USERNAME> <PROVIDER>
|
||||
Configure password for a cloud provider and save it to the keyring.
|
||||
PROVIDER can be specified with or without a driver, for example:
|
||||
"\-\-set\-password bob rackspace" or more specific "\-\-set\-password bob
|
||||
rackspace:openstack" DEPRECATED!
|
||||
.UNINDENT
|
||||
.SS Output Options
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-out
|
||||
Pass in an alternative outputter to display the return of data. This
|
||||
outputter can be any of the available outputters:
|
||||
.INDENT 7.0
|
||||
.INDENT 3.5
|
||||
\fBgrains\fP, \fBhighstate\fP, \fBjson\fP, \fBkey\fP, \fBoverstatestage\fP, \fBpprint\fP, \fBraw\fP, \fBtxt\fP, \fByaml\fP
|
||||
.UNINDENT
|
||||
.UNINDENT
|
||||
.sp
|
||||
Some outputters are formatted only for data returned from specific
|
||||
functions; for instance, the \fBgrains\fP outputter will not work for non\-grains
|
||||
data.
|
||||
.sp
|
||||
If an outputter is used that does not support the data passed into it, then
|
||||
Salt will fall back on the \fBpprint\fP outputter and display the return data
|
||||
using the Python \fBpprint\fP standard library module.
|
||||
.sp
|
||||
\fBNOTE:\fP
|
||||
.INDENT 7.0
|
||||
.INDENT 3.5
|
||||
If using \fB\-\-out=json\fP, you will probably want \fB\-\-static\fP as well.
|
||||
Without the static option, you will get a JSON string for each minion.
|
||||
This is due to using an iterative outputter. So if you want to feed it
|
||||
to a JSON parser, use \fB\-\-static\fP as well.
|
||||
.UNINDENT
|
||||
.UNINDENT
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-M MASTER_CONFIG, \-\-master\-config=MASTER_CONFIG
|
||||
Specify an alternative location for the salt master configuration file.
|
||||
The salt master configuration file is used to determine how to handle the
|
||||
minion RSA keys. Default location is /etc/salt/master.
|
||||
.B \-\-out\-indent OUTPUT_INDENT, \-\-output\-indent OUTPUT_INDENT
|
||||
Print the output indented by the provided value in spaces. Negative values
|
||||
disable indentation. Only applicable in outputters that support
|
||||
indentation.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-V VM_CONFIG, \-\-profiles=VM_CONFIG, \-\-vm_config=VM_CONFIG
|
||||
Specify an alternative location for the salt cloud profiles file.
|
||||
Default location is /etc/salt/cloud.profiles.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-raw\-out
|
||||
Print the output from the salt command in raw python
|
||||
form, this is suitable for re\-reading the output into
|
||||
an executing python script with eval.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-out=OUTPUT, \-\-output=OUTPUT
|
||||
Print the output from the salt\-cloud command using the specified outputter. The
|
||||
builtins are \(aqraw\(aq, \(aqcompact\(aq, \(aqno_return\(aq, \(aqgrains\(aq, \(aqoverstatestage\(aq, \(aqpprint\(aq,
|
||||
\(aqjson\(aq, \(aqnested\(aq, \(aqyaml\(aq, \(aqhighstate\(aq, \(aqquiet\(aq, \(aqkey\(aq, \(aqtxt\(aq, \(aqnewline_values_only\(aq,
|
||||
\(aqvirt_query\(aq.
|
||||
.B \-\-out\-file=OUTPUT_FILE, \-\-output\-file=OUTPUT_FILE
|
||||
Write the output to the specified file.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-no\-color
|
||||
Disable all colored output.
|
||||
Disable all colored output
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-force\-color
|
||||
Force colored output
|
||||
.sp
|
||||
\fBNOTE:\fP
|
||||
.INDENT 7.0
|
||||
.INDENT 3.5
|
||||
When using colored output the color codes are as follows:
|
||||
.sp
|
||||
\fBgreen\fP denotes success, \fBred\fP denotes failure, \fBblue\fP denotes
|
||||
changes and success and \fByellow\fP denotes a expected future change in configuration.
|
||||
.UNINDENT
|
||||
.UNINDENT
|
||||
.UNINDENT
|
||||
.SH EXAMPLES
|
||||
.sp
|
||||
|
|
2954
doc/man/salt.7
2954
doc/man/salt.7
File diff suppressed because it is too large
Load diff
|
@ -69,7 +69,7 @@ If Exist "%BinDir%\README.txt" del /q "%BinDir%\README.txt"
|
|||
|
||||
@ echo Building the installer...
|
||||
@ echo -------------------------
|
||||
makensis.exe /DSaltVersion="%Version%" "%InsDir%\Salt-Minion-Setup.nsi"
|
||||
makensis.exe /DSaltVersion=%Version% "%InsDir%\Salt-Minion-Setup.nsi"
|
||||
@ echo.
|
||||
|
||||
@ echo.
|
||||
|
|
|
@ -17,7 +17,7 @@ ${StrLoc}
|
|||
${StrStrAdv}
|
||||
|
||||
!ifdef SaltVersion
|
||||
!define PRODUCT_VERSION ${SaltVersion}
|
||||
!define PRODUCT_VERSION "${SaltVersion}"
|
||||
!else
|
||||
!define PRODUCT_VERSION "Undefined Version"
|
||||
!endif
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 140 KiB |
|
@ -6,6 +6,7 @@ import os
|
|||
import sys
|
||||
|
||||
# Import Salt libs
|
||||
import salt.utils.job
|
||||
from salt.ext.six import string_types
|
||||
from salt.utils import parsers, print_cli
|
||||
from salt.exceptions import (
|
||||
|
@ -85,7 +86,11 @@ class SaltCMD(parsers.SaltCMDOptionParser):
|
|||
batch = salt.cli.batch.Batch(self.config, eauth=eauth)
|
||||
# Printing the output is already taken care of in run() itself
|
||||
for res in batch.run():
|
||||
pass
|
||||
if self.options.failhard:
|
||||
for ret in res.itervalues():
|
||||
retcode = salt.utils.job.get_retcode(ret)
|
||||
if retcode != 0:
|
||||
sys.exit(retcode)
|
||||
|
||||
else:
|
||||
if self.options.timeout <= 0:
|
||||
|
@ -295,8 +300,9 @@ class SaltCMD(parsers.SaltCMDOptionParser):
|
|||
ret[key] = data['ret']
|
||||
if 'out' in data:
|
||||
out = data['out']
|
||||
if 'retcode' in data:
|
||||
retcode = data['retcode']
|
||||
ret_retcode = salt.utils.job.get_retcode(data)
|
||||
if ret_retcode > retcode:
|
||||
retcode = ret_retcode
|
||||
return ret, out, retcode
|
||||
|
||||
def _format_error(self, minion_error):
|
||||
|
|
|
@ -939,6 +939,18 @@ class RemoteClient(Client):
|
|||
dest is omitted, then the downloaded file will be placed in the minion
|
||||
cache
|
||||
'''
|
||||
|
||||
# Check if file exists on server, before creating files and
|
||||
# directories
|
||||
hash_server = self.hash_file(path, saltenv)
|
||||
if hash_server == '':
|
||||
log.debug(
|
||||
'Could not find file from saltenv {0!r}, {1!r}'.format(
|
||||
saltenv, path
|
||||
)
|
||||
)
|
||||
return False
|
||||
|
||||
if env is not None:
|
||||
salt.utils.warn_until(
|
||||
'Boron',
|
||||
|
@ -971,7 +983,6 @@ class RemoteClient(Client):
|
|||
|
||||
if dest2check and os.path.isfile(dest2check):
|
||||
hash_local = self.hash_file(dest2check, saltenv)
|
||||
hash_server = self.hash_file(path, saltenv)
|
||||
if hash_local == hash_server:
|
||||
log.info(
|
||||
'Fetching file from saltenv {0!r}, ** skipped ** '
|
||||
|
|
|
@ -524,8 +524,6 @@ def grains(opts, force_refresh=False):
|
|||
|
||||
# Run the rest of the grains
|
||||
for key, fun in six.iteritems(funcs):
|
||||
if '.' not in key:
|
||||
continue
|
||||
if key.startswith('core.') or key == '_errors':
|
||||
continue
|
||||
try:
|
||||
|
@ -719,7 +717,6 @@ class LazyLoader(salt.utils.lazy.LazyDict):
|
|||
whitelist=None,
|
||||
virtual_enable=True,
|
||||
): # pylint: disable=W0231
|
||||
super(LazyLoader, self).__init__() # init the lazy loader
|
||||
self.opts = self.__prep_mod_opts(opts)
|
||||
|
||||
self.module_dirs = module_dirs
|
||||
|
@ -739,19 +736,37 @@ class LazyLoader(salt.utils.lazy.LazyDict):
|
|||
|
||||
# names of modules that we don't have (errors, __virtual__, etc.)
|
||||
self.missing_modules = {} # mapping of name -> error
|
||||
self.loaded_modules = set() # list of all modules that we have loaded
|
||||
self.loaded_modules = {} # mapping of module_name -> dict_of_functions
|
||||
self.loaded_files = set() # TODO: just remove them from file_mapping?
|
||||
|
||||
self.disabled = set(self.opts.get('disable_{0}s'.format(self.tag), []))
|
||||
|
||||
self.refresh_file_mapping()
|
||||
|
||||
super(LazyLoader, self).__init__() # late init the lazy loader
|
||||
# create all of the import namespaces
|
||||
_generate_module('{0}.int'.format(self.loaded_base_name))
|
||||
_generate_module('{0}.int.{1}'.format(self.loaded_base_name, tag))
|
||||
_generate_module('{0}.ext'.format(self.loaded_base_name))
|
||||
_generate_module('{0}.ext.{1}'.format(self.loaded_base_name, tag))
|
||||
|
||||
def __getattr__(self, mod_name):
|
||||
'''
|
||||
Allow for "direct" attribute access-- this allows jinja templates to
|
||||
access things like `salt.test.ping()`
|
||||
'''
|
||||
if mod_name not in self.loaded_modules and not self.loaded:
|
||||
for name in self._iter_files(mod_name):
|
||||
if name in self.loaded_files:
|
||||
continue
|
||||
# if we got what we wanted, we are done
|
||||
if self._load_module(name) and mod_name in self.loaded_modules:
|
||||
break
|
||||
if mod_name in self.loaded_modules:
|
||||
return self.loaded_modules[mod_name]
|
||||
else:
|
||||
raise AttributeError(mod_name)
|
||||
|
||||
def missing_fun_string(self, function_name):
|
||||
'''
|
||||
Return the error string for a missing function.
|
||||
|
@ -848,7 +863,7 @@ class LazyLoader(salt.utils.lazy.LazyDict):
|
|||
super(LazyLoader, self).clear() # clear the lazy loader
|
||||
self.loaded_files = set()
|
||||
self.missing_modules = {}
|
||||
self.loaded_modules = set()
|
||||
self.loaded_modules = {}
|
||||
# if we have been loaded before, lets clear the file mapping since
|
||||
# we obviously want a re-do
|
||||
if hasattr(self, 'opts'):
|
||||
|
@ -1029,7 +1044,7 @@ class LazyLoader(salt.utils.lazy.LazyDict):
|
|||
module_name
|
||||
)
|
||||
)
|
||||
self._dict[module_name] = salt.utils.odict.OrderedDict()
|
||||
mod_dict = salt.utils.odict.OrderedDict()
|
||||
for attr in getattr(mod, '__load__', dir(mod)):
|
||||
if attr.startswith('_'):
|
||||
# private functions are skipped
|
||||
|
@ -1048,13 +1063,13 @@ class LazyLoader(salt.utils.lazy.LazyDict):
|
|||
funcname = getattr(mod, '__func_alias__', {}).get(attr, attr)
|
||||
# Save many references for lookups
|
||||
self._dict['{0}.{1}'.format(module_name, funcname)] = func
|
||||
setattr(self._dict[module_name], funcname, func)
|
||||
self._dict[module_name][funcname] = func
|
||||
setattr(mod_dict, funcname, func)
|
||||
mod_dict[funcname] = func
|
||||
self._apply_outputter(func, mod)
|
||||
|
||||
# enforce depends
|
||||
Depends.enforce_dependencies(self._dict, self.tag)
|
||||
self.loaded_modules.add(module_name)
|
||||
self.loaded_modules[module_name] = mod_dict
|
||||
return True
|
||||
|
||||
def _load(self, key):
|
||||
|
@ -1062,12 +1077,9 @@ class LazyLoader(salt.utils.lazy.LazyDict):
|
|||
Load a single item if you have it
|
||||
'''
|
||||
# if the key doesn't have a '.' then it isn't valid for this mod dict
|
||||
if not isinstance(key, six.string_types):
|
||||
if not isinstance(key, six.string_types) or '.' not in key:
|
||||
raise KeyError
|
||||
if '.' not in key:
|
||||
mod_name = key
|
||||
else:
|
||||
mod_name, _ = key.split('.', 1)
|
||||
mod_name, _ = key.split('.', 1)
|
||||
if mod_name in self.missing_modules:
|
||||
return True
|
||||
# if the modulename isn't in the whitelist, don't bother
|
||||
|
|
|
@ -1196,6 +1196,9 @@ class AESFuncs(object):
|
|||
# Register the syndic
|
||||
syndic_cache_path = os.path.join(self.opts['cachedir'], 'syndics', load['id'])
|
||||
if not os.path.exists(syndic_cache_path):
|
||||
path_name = os.path.split(syndic_cache_path)[0]
|
||||
if not os.path.exists(path_name):
|
||||
os.makedirs(path_name)
|
||||
with salt.utils.fopen(syndic_cache_path, 'w') as f:
|
||||
f.write('')
|
||||
|
||||
|
|
|
@ -514,7 +514,7 @@ def install(name=None,
|
|||
if pkgs is None and kwargs.get('version') and len(pkg_params) == 1:
|
||||
# Only use the 'version' param if 'name' was not specified as a
|
||||
# comma-separated list
|
||||
pkg_params = {name: kwargs.get('version')}
|
||||
pkg_params = {name: str(kwargs.get('version'))}
|
||||
targets = []
|
||||
for param, version_num in six.iteritems(pkg_params):
|
||||
if version_num is None:
|
||||
|
|
|
@ -336,9 +336,8 @@ def cache_file(path, saltenv='base', env=None):
|
|||
if '?env=' in path:
|
||||
salt.utils.warn_until(
|
||||
'Boron',
|
||||
'Passing a salt environment should be done using '
|
||||
'\'saltenv\' not \'env\'. This functionality will be '
|
||||
'removed in Salt Boron.'
|
||||
'Passing a salt environment should be done using \'saltenv\' '
|
||||
'not \'env\'. This functionality will be removed in Salt Boron.'
|
||||
)
|
||||
env_splitter = '?env='
|
||||
try:
|
||||
|
|
|
@ -1,6 +1,77 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
r'''
|
||||
Install Python packages with pip to either the system or a virtualenv
|
||||
|
||||
Windows Support
|
||||
===============
|
||||
|
||||
.. versionadded:: 2014.7.4
|
||||
|
||||
Salt now uses a portable python. As a result the entire pip module is now
|
||||
functional on the salt installation itself. You can pip install dependencies
|
||||
for your custom modules. You can even upgrade salt itself using pip. For this
|
||||
to work properly, you must specify the Current Working Directory (``cwd``) and
|
||||
the Pip Binary (``bin_env``) salt should use.
|
||||
|
||||
For example, the following command will list all software installed using pip
|
||||
to your current salt environment:
|
||||
|
||||
.. code-block:: bat
|
||||
|
||||
salt <minion> pip.list cwd='C:\salt\bin\Scripts' bin_env='C:\salt\bin\Scripts\pip.exe'
|
||||
|
||||
Specifying the ``cwd`` and ``bin_env`` options ensures you're modifying the
|
||||
salt environment. If these are omitted, it will default to the local
|
||||
installation of python. If python is not installed locally it will fail saying
|
||||
it couldn't find pip.
|
||||
|
||||
State File Support
|
||||
------------------
|
||||
|
||||
This functionality works in states as well. If you need to pip install colorama
|
||||
with a state, for example, the following will work:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
install_colorama:
|
||||
pip.installed:
|
||||
- name: colorama
|
||||
- cwd: 'C:\salt\bin\scripts'
|
||||
- bin_env: 'C:\salt\bin\scripts\pip.exe'
|
||||
- upgrade: True
|
||||
|
||||
Upgrading Salt using Pip
|
||||
------------------------
|
||||
|
||||
You can now update salt using pip to any version from the 2014.7 branch
|
||||
forward. Previous version require recompiling some of the dependencies which is
|
||||
painful in windows.
|
||||
|
||||
To do this you just use pip with git to update to the version you want and then
|
||||
restart the service. Here is a sample state file that upgrades salt to the head
|
||||
of the 2015.2 branch:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
install_salt:
|
||||
pip.installed:
|
||||
- cwd: 'C:\salt\bin\scripts'
|
||||
- bin_env: 'C:\salt\bin\scripts\pip.exe'
|
||||
- editable: git+https://github.com/saltstack/salt@2015.2#egg=salt
|
||||
- upgrade: True
|
||||
|
||||
restart_service:
|
||||
service.running:
|
||||
- name: salt-minion
|
||||
- enable: True
|
||||
- watch:
|
||||
- pip: install_salt
|
||||
|
||||
.. note::
|
||||
If you're having problems, you might try doubling the back slashes. For
|
||||
example, cwd: 'C:\\salt\\bin\\scripts'. Sometimes python thinks the single
|
||||
back slash is an escape character.
|
||||
|
||||
'''
|
||||
from __future__ import absolute_import
|
||||
|
||||
|
@ -55,8 +126,39 @@ def _get_pip_bin(bin_env):
|
|||
return bin_env
|
||||
|
||||
|
||||
def _process_salt_url(path, saltenv):
|
||||
'''
|
||||
Process 'salt://' and '?saltenv=' out of `path` and return the stripped
|
||||
path and the saltenv.
|
||||
'''
|
||||
path = path.split('salt://', 1)[-1]
|
||||
|
||||
env_splitter = '?saltenv='
|
||||
if '?env=' in path:
|
||||
salt.utils.warn_until(
|
||||
'Boron',
|
||||
'Passing a salt environment should be done using \'saltenv\' '
|
||||
'not \'env\'. This functionality will be removed in Salt Boron.'
|
||||
)
|
||||
env_splitter = '?env='
|
||||
try:
|
||||
path, saltenv = path.split(env_splitter)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
return path, saltenv
|
||||
|
||||
|
||||
def _get_cached_requirements(requirements, saltenv):
|
||||
'''Get the location of a cached requirements file; caching if necessary.'''
|
||||
'''
|
||||
Get the location of a cached requirements file; caching if necessary.
|
||||
'''
|
||||
|
||||
requirements_file, saltenv = _process_salt_url(requirements, saltenv)
|
||||
if requirements_file not in __salt__['cp.list_master'](saltenv):
|
||||
# Requirements file does not exist in the given saltenv.
|
||||
return False
|
||||
|
||||
cached_requirements = __salt__['cp.is_cached'](
|
||||
requirements, saltenv
|
||||
)
|
||||
|
|
|
@ -850,7 +850,7 @@ def wheel(fun, **kwargs):
|
|||
salt '*' saltutil.wheel key.accept match=jerry
|
||||
'''
|
||||
wclient = salt.wheel.WheelClient(__opts__)
|
||||
return wclient.cmd(fun, **kwargs)
|
||||
return wclient.cmd(fun, kwarg=kwargs)
|
||||
|
||||
|
||||
# this is the only way I could figure out how to get the REAL file_roots
|
||||
|
|
|
@ -89,4 +89,18 @@ def store_job(opts, load, event=None, mminion=None):
|
|||
log.error(emsg)
|
||||
raise KeyError(emsg)
|
||||
|
||||
|
||||
def get_retcode(ret):
|
||||
'''
|
||||
Determine a retcode for a given return
|
||||
'''
|
||||
retcode = 0
|
||||
# if there is a dict with retcode, use that
|
||||
if isinstance(ret, dict) and ret.get('retcode', 0) != 0:
|
||||
return ret['retcode']
|
||||
# if its a boolean, False means 1
|
||||
elif isinstance(ret, bool) and not ret:
|
||||
return 1
|
||||
return retcode
|
||||
|
||||
# vim:set et sts=4 ts=4 tw=80:
|
||||
|
|
|
@ -94,22 +94,6 @@ class LazyDict(collections.MutableMapping):
|
|||
else:
|
||||
return self._dict[key]
|
||||
|
||||
def __getattr__(self, name):
|
||||
'''
|
||||
Check if the name is in the dict and return it if it is
|
||||
'''
|
||||
if name not in self._dict and not self.loaded:
|
||||
# load the item
|
||||
if self._load(name):
|
||||
log.debug('LazyLoaded {0}'.format(name))
|
||||
return self._dict[name]
|
||||
else:
|
||||
log.debug('Could not LazyLoad {0}'.format(name))
|
||||
raise KeyError(name)
|
||||
elif name in self:
|
||||
return self[name]
|
||||
raise AttributeError(name)
|
||||
|
||||
def __len__(self):
|
||||
# if not loaded,
|
||||
if not self.loaded:
|
||||
|
|
|
@ -1479,6 +1479,12 @@ class SaltCMDOptionParser(six.with_metaclass(OptionParserMeta,
|
|||
action='store_true',
|
||||
help=('Display a progress graph')
|
||||
)
|
||||
self.add_option(
|
||||
'--failhard',
|
||||
default=False,
|
||||
action='store_true',
|
||||
help=('Stop batch execution upon first "bad" return')
|
||||
)
|
||||
self.add_option(
|
||||
'--async',
|
||||
default=False,
|
||||
|
|
|
@ -842,6 +842,21 @@ class Schedule(object):
|
|||
self.loop_interval = seconds
|
||||
run = False
|
||||
|
||||
if 'splay' in data:
|
||||
if 'when' in data:
|
||||
log.error('Unable to use "splay" with "when" option at this time. Ignoring.')
|
||||
elif 'cron' in data:
|
||||
log.error('Unable to use "splay" with "cron" option at this time. Ignoring.')
|
||||
else:
|
||||
if '_seconds' not in data:
|
||||
log.debug('The _seconds parameter is missing, '
|
||||
'most likely the first run or the schedule '
|
||||
'has been refreshed refresh.')
|
||||
if 'seconds' in data:
|
||||
data['_seconds'] = data['seconds']
|
||||
else:
|
||||
data['_seconds'] = 0
|
||||
|
||||
if job in self.intervals:
|
||||
if 'when' in data:
|
||||
if seconds == 0:
|
||||
|
@ -855,17 +870,6 @@ class Schedule(object):
|
|||
if now - self.intervals[job] >= seconds:
|
||||
run = True
|
||||
else:
|
||||
if 'splay' in data:
|
||||
if 'when' in data:
|
||||
log.error('Unable to use "splay" with "when" option at this time. Ignoring.')
|
||||
elif 'cron' in data:
|
||||
log.error('Unable to use "splay" with "cron" option at this time. Ignoring.')
|
||||
else:
|
||||
if 'seconds' in data:
|
||||
data['_seconds'] = data['seconds']
|
||||
else:
|
||||
data['_seconds'] = 0
|
||||
|
||||
if 'when' in data:
|
||||
if seconds == 0:
|
||||
if data['_when_run']:
|
||||
|
|
|
@ -109,8 +109,7 @@ class LazyLoaderVirtualEnabledTest(TestCase):
|
|||
self.assertEqual(func_globals['__pillar__'], self.opts.get('pillar', {}))
|
||||
# the opts passed into modules is at least a subset of the whole opts
|
||||
for key, val in six.iteritems(func_globals['__opts__']):
|
||||
if key in self.opts:
|
||||
self.assertEqual(self.opts[key], val)
|
||||
self.assertEqual(self.opts[key], val)
|
||||
|
||||
def test_pack(self):
|
||||
self.loader.pack['__foo__'] = 'bar'
|
||||
|
|
|
@ -182,10 +182,11 @@ class PipModuleTest(integration.ModuleCase):
|
|||
)
|
||||
try:
|
||||
self.assertEqual(ret['retcode'], 0)
|
||||
self.assertIn(
|
||||
'Successfully installed pep8 Blinker SaltTesting',
|
||||
ret['stdout']
|
||||
)
|
||||
for package in ('Blinker', 'SaltTesting', 'pep8'):
|
||||
self.assertRegexpMatches(
|
||||
ret['stdout'],
|
||||
r'(?:.*)(Successfully installed)(?:.*)({0})(?:.*)'.format(package)
|
||||
)
|
||||
except AssertionError:
|
||||
import pprint
|
||||
pprint.pprint(ret)
|
||||
|
|
|
@ -74,8 +74,6 @@ class SysModuleTest(integration.ModuleCase):
|
|||
)
|
||||
|
||||
for fun in docs:
|
||||
if '.' not in fun:
|
||||
continue
|
||||
if fun.startswith('runtests_helpers'):
|
||||
continue
|
||||
if fun in allow_failure:
|
||||
|
|
86
tests/unit/modules/powerpath_test.py
Normal file
86
tests/unit/modules/powerpath_test.py
Normal file
|
@ -0,0 +1,86 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
:codeauthor: :email:`Jayesh Kariya <jayeshk@saltstack.com>`
|
||||
'''
|
||||
# Import Python libs
|
||||
from __future__ import absolute_import
|
||||
|
||||
# Import Salt Testing Libs
|
||||
from salttesting import skipIf, TestCase
|
||||
from salttesting.mock import (
|
||||
NO_MOCK,
|
||||
NO_MOCK_REASON,
|
||||
MagicMock,
|
||||
patch)
|
||||
|
||||
from salttesting.helpers import ensure_in_syspath
|
||||
|
||||
ensure_in_syspath('../../')
|
||||
|
||||
# Import Salt Libs
|
||||
from salt.modules import powerpath
|
||||
|
||||
# Globals
|
||||
powerpath.__salt__ = {}
|
||||
|
||||
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
class PowerpathTestCase(TestCase):
|
||||
'''
|
||||
Test cases for salt.modules.powerpath
|
||||
'''
|
||||
@patch('os.path.exists')
|
||||
def test_has_powerpath(self, mock_exists):
|
||||
'''
|
||||
Test for powerpath
|
||||
'''
|
||||
mock_exists.return_value = True
|
||||
self.assertTrue(powerpath.has_powerpath())
|
||||
|
||||
mock_exists.return_value = False
|
||||
self.assertFalse(powerpath.has_powerpath())
|
||||
|
||||
def test_list_licenses(self):
|
||||
'''
|
||||
Test to returns a list of applied powerpath license keys
|
||||
'''
|
||||
with patch.dict(powerpath.__salt__,
|
||||
{'cmd.run': MagicMock(return_value='A\nB')}):
|
||||
self.assertListEqual(powerpath.list_licenses(), [])
|
||||
|
||||
def test_add_license(self):
|
||||
'''
|
||||
Test to add a license
|
||||
'''
|
||||
with patch.object(powerpath, 'has_powerpath', return_value=False):
|
||||
self.assertDictEqual(powerpath.add_license('key'),
|
||||
{'output': 'PowerPath is not installed',
|
||||
'result': False, 'retcode': -1})
|
||||
|
||||
mock = MagicMock(return_value={'retcode': 1, 'stderr': 'stderr'})
|
||||
with patch.object(powerpath, 'has_powerpath', return_value=True):
|
||||
with patch.dict(powerpath.__salt__, {'cmd.run_all': mock}):
|
||||
self.assertDictEqual(powerpath.add_license('key'),
|
||||
{'output': 'stderr', 'result': False,
|
||||
'retcode': 1})
|
||||
|
||||
def test_remove_license(self):
|
||||
'''
|
||||
Test to remove a license
|
||||
'''
|
||||
with patch.object(powerpath, 'has_powerpath', return_value=False):
|
||||
self.assertDictEqual(powerpath.remove_license('key'),
|
||||
{'output': 'PowerPath is not installed',
|
||||
'result': False, 'retcode': -1})
|
||||
|
||||
mock = MagicMock(return_value={'retcode': 1, 'stderr': 'stderr'})
|
||||
with patch.object(powerpath, 'has_powerpath', return_value=True):
|
||||
with patch.dict(powerpath.__salt__, {'cmd.run_all': mock}):
|
||||
self.assertDictEqual(powerpath.remove_license('key'),
|
||||
{'output': 'stderr', 'result': False,
|
||||
'retcode': 1})
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from integration import run_tests
|
||||
run_tests(PowerpathTestCase, needs_daemon=False)
|
|
@ -8,7 +8,6 @@ from __future__ import absolute_import
|
|||
import os
|
||||
|
||||
# Import Salt Testing Libs
|
||||
import salt.utils
|
||||
from salttesting import TestCase, skipIf
|
||||
from salt.exceptions import SaltInvocationError
|
||||
from salttesting.helpers import ensure_in_syspath
|
||||
|
@ -23,6 +22,7 @@ from salttesting.mock import (
|
|||
ensure_in_syspath('../../')
|
||||
|
||||
# Import Salt Libs
|
||||
import salt.utils
|
||||
from salt.modules import state
|
||||
|
||||
# Globals
|
||||
|
@ -548,9 +548,9 @@ class StateTestCase(TestCase):
|
|||
'''
|
||||
Test to retrieve the highstate data from the salt master
|
||||
'''
|
||||
mock = MagicMock(side_effect=[{"A"}, None, None])
|
||||
mock = MagicMock(side_effect=["A", None, None])
|
||||
with patch.object(state, '_check_queue', mock):
|
||||
self.assertEqual(state.show_highstate(), {"A"})
|
||||
self.assertEqual(state.show_highstate(), "A")
|
||||
|
||||
self.assertRaises(SaltInvocationError,
|
||||
state.show_highstate,
|
||||
|
@ -562,7 +562,7 @@ class StateTestCase(TestCase):
|
|||
'''
|
||||
Test to list out the low data that will be applied to this minion
|
||||
'''
|
||||
mock = MagicMock(side_effect=[{"A"}, None])
|
||||
mock = MagicMock(side_effect=["A", None])
|
||||
with patch.object(state, '_check_queue', mock):
|
||||
self.assertRaises(AssertionError, state.show_lowstate)
|
||||
|
||||
|
@ -573,9 +573,9 @@ class StateTestCase(TestCase):
|
|||
Test to call a single ID from the
|
||||
named module(s) and handle all requisites
|
||||
'''
|
||||
mock = MagicMock(side_effect=[{"A"}, None, None, None])
|
||||
mock = MagicMock(side_effect=["A", None, None, None])
|
||||
with patch.object(state, '_check_queue', mock):
|
||||
self.assertEqual(state.sls_id("apache", "http"), {"A"})
|
||||
self.assertEqual(state.sls_id("apache", "http"), "A")
|
||||
|
||||
with patch.dict(state.__opts__, {"test": "A"}):
|
||||
mock = MagicMock(return_value={'test': True})
|
||||
|
@ -597,9 +597,9 @@ class StateTestCase(TestCase):
|
|||
'''
|
||||
Test to display the low data from a specific sls
|
||||
'''
|
||||
mock = MagicMock(side_effect=[{"A"}, None, None])
|
||||
mock = MagicMock(side_effect=["A", None, None])
|
||||
with patch.object(state, '_check_queue', mock):
|
||||
self.assertEqual(state.show_low_sls("foo"), {"A"})
|
||||
self.assertEqual(state.show_low_sls("foo"), "A")
|
||||
|
||||
with patch.dict(state.__opts__, {"test": "A"}):
|
||||
mock = MagicMock(return_value={'test': True})
|
||||
|
@ -616,9 +616,9 @@ class StateTestCase(TestCase):
|
|||
'''
|
||||
Test to display the state data from a specific sls
|
||||
'''
|
||||
mock = MagicMock(side_effect=[{"A"}, None, None, None])
|
||||
mock = MagicMock(side_effect=["A", None, None, None])
|
||||
with patch.object(state, '_check_queue', mock):
|
||||
self.assertEqual(state.show_sls("foo"), {"A"})
|
||||
self.assertEqual(state.show_sls("foo"), "A")
|
||||
|
||||
with patch.dict(state.__opts__, {"test": "A"}):
|
||||
mock = MagicMock(return_value={'test': True})
|
||||
|
@ -642,9 +642,9 @@ class StateTestCase(TestCase):
|
|||
Test to execute a specific top file
|
||||
'''
|
||||
ret = ['Pillar failed to render with the following messages:', 'E']
|
||||
mock = MagicMock(side_effect=[{"A"}, None, None, None])
|
||||
mock = MagicMock(side_effect=["A", None, None, None])
|
||||
with patch.object(state, '_check_queue', mock):
|
||||
self.assertEqual(state.top("reverse_top.sls"), {"A"})
|
||||
self.assertEqual(state.top("reverse_top.sls"), "A")
|
||||
|
||||
mock = MagicMock(side_effect=[False, True, True])
|
||||
with patch.object(state, '_check_pillar', mock):
|
||||
|
@ -688,9 +688,9 @@ class StateTestCase(TestCase):
|
|||
'To re-enable, run state.enable highstate',
|
||||
'result': 'False'})
|
||||
|
||||
mock = MagicMock(side_effect=[{"A"}, None, None])
|
||||
mock = MagicMock(side_effect=["A", None, None])
|
||||
with patch.object(state, '_check_queue', mock):
|
||||
self.assertEqual(state.highstate("whitelist=sls1.sls"), {"A"})
|
||||
self.assertEqual(state.highstate("whitelist=sls1.sls"), "A")
|
||||
|
||||
with patch.dict(state.__opts__, {"test": "A"}):
|
||||
mock = MagicMock(return_value={'test': True})
|
||||
|
@ -703,7 +703,7 @@ class StateTestCase(TestCase):
|
|||
mock = MagicMock(return_value=True)
|
||||
with patch.dict(state.__salt__,
|
||||
{'config.option': mock}):
|
||||
mock = MagicMock(return_value={"A"})
|
||||
mock = MagicMock(return_value="A")
|
||||
with patch.object(state, '_filter_running',
|
||||
mock):
|
||||
mock = MagicMock(return_value=True)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
:codeauthor: :email:`Jayesh Kariya <jayeshk@saltstack.com>`
|
||||
:codeauthor: :email:`Jayesh Kariya <jayeshk@saltstack.com>`
|
||||
'''
|
||||
|
||||
# Import Python Libs
|
||||
|
@ -99,12 +99,6 @@ class SysmodTestCase(TestCase):
|
|||
'''
|
||||
self.assertDictEqual(sysmod.doc(), {})
|
||||
|
||||
ret = ("str(object='') -> string\n\nReturn a nice string"
|
||||
" representation of the object.\nIf the argument is a string,"
|
||||
" the return value is the same object.")
|
||||
with patch.dict(sysmod.__salt__, {'sys.doc': ''}):
|
||||
self.assertDictEqual(sysmod.doc('sys.doc'), {'sys.doc': ret})
|
||||
|
||||
# 'state_doc' function tests: 1
|
||||
|
||||
def test_state_doc(self):
|
||||
|
|
101
tests/unit/states/boto_asg_test.py
Normal file
101
tests/unit/states/boto_asg_test.py
Normal file
|
@ -0,0 +1,101 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
:codeauthor: :email:`Jayesh Kariya <jayeshk@saltstack.com>`
|
||||
'''
|
||||
# Import Python libs
|
||||
from __future__ import absolute_import
|
||||
|
||||
# Import Salt Testing Libs
|
||||
from salttesting import skipIf, TestCase
|
||||
from salttesting.mock import (
|
||||
NO_MOCK,
|
||||
NO_MOCK_REASON,
|
||||
MagicMock,
|
||||
patch)
|
||||
|
||||
from salttesting.helpers import ensure_in_syspath
|
||||
|
||||
ensure_in_syspath('../../')
|
||||
|
||||
# Import Salt Libs
|
||||
from salt.states import boto_asg
|
||||
|
||||
boto_asg.__salt__ = {}
|
||||
boto_asg.__opts__ = {}
|
||||
|
||||
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
class BotoAsgTestCase(TestCase):
|
||||
'''
|
||||
Test cases for salt.states.boto_asg
|
||||
'''
|
||||
# 'present' function tests: 1
|
||||
|
||||
def test_present(self):
|
||||
'''
|
||||
Test to ensure the autoscale group exists.
|
||||
'''
|
||||
name = 'myasg'
|
||||
launch_config_name = 'mylc'
|
||||
availability_zones = ['us-east-1a', 'us-east-1b']
|
||||
min_size = 1
|
||||
max_size = 1
|
||||
|
||||
ret = {'name': name,
|
||||
'result': None,
|
||||
'changes': {},
|
||||
'comment': ''}
|
||||
|
||||
mock = MagicMock(side_effect=[False, {'min_size': 2}, ['']])
|
||||
with patch.dict(boto_asg.__salt__, {'boto_asg.get_config': mock}):
|
||||
with patch.dict(boto_asg.__opts__, {'test': True}):
|
||||
comt = ('Autoscale group set to be created.')
|
||||
ret.update({'comment': comt})
|
||||
self.assertDictEqual(boto_asg.present(name, launch_config_name,
|
||||
availability_zones,
|
||||
min_size, max_size), ret)
|
||||
|
||||
comt = ('Autoscale group set to be updated.')
|
||||
ret.update({'comment': comt, 'result': None})
|
||||
self.assertDictEqual(boto_asg.present(name, launch_config_name,
|
||||
availability_zones,
|
||||
min_size, max_size), ret)
|
||||
|
||||
with patch.dict(boto_asg.__salt__,
|
||||
{'config.option': MagicMock(return_value={})}):
|
||||
comt = ('Autoscale group present. ')
|
||||
ret.update({'comment': comt, 'result': True})
|
||||
self.assertDictEqual(boto_asg.present(name,
|
||||
launch_config_name,
|
||||
availability_zones,
|
||||
min_size, max_size),
|
||||
ret)
|
||||
|
||||
# 'absent' function tests: 1
|
||||
|
||||
def test_absent(self):
|
||||
'''
|
||||
Test to ensure the named autoscale group is deleted.
|
||||
'''
|
||||
name = 'myasg'
|
||||
|
||||
ret = {'name': name,
|
||||
'result': None,
|
||||
'changes': {},
|
||||
'comment': ''}
|
||||
|
||||
mock = MagicMock(side_effect=[True, False])
|
||||
with patch.dict(boto_asg.__salt__, {'boto_asg.get_config': mock}):
|
||||
with patch.dict(boto_asg.__opts__, {'test': True}):
|
||||
comt = ('Autoscale group set to be deleted.')
|
||||
ret.update({'comment': comt})
|
||||
self.assertDictEqual(boto_asg.absent(name), ret)
|
||||
|
||||
comt = ('Autoscale group does not exist.')
|
||||
ret.update({'comment': comt, 'result': True})
|
||||
self.assertDictEqual(boto_asg.absent(name), ret)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from integration import run_tests
|
||||
run_tests(BotoAsgTestCase, needs_daemon=False)
|
103
tests/unit/states/boto_cloudwatch_alarm_test.py
Normal file
103
tests/unit/states/boto_cloudwatch_alarm_test.py
Normal file
|
@ -0,0 +1,103 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
:codeauthor: :email:`Jayesh Kariya <jayeshk@saltstack.com>`
|
||||
'''
|
||||
# Import Python libs
|
||||
from __future__ import absolute_import
|
||||
|
||||
# Import Salt Testing Libs
|
||||
from salttesting import skipIf, TestCase
|
||||
from salttesting.mock import (
|
||||
NO_MOCK,
|
||||
NO_MOCK_REASON,
|
||||
MagicMock,
|
||||
patch)
|
||||
|
||||
from salttesting.helpers import ensure_in_syspath
|
||||
|
||||
ensure_in_syspath('../../')
|
||||
|
||||
# Import Salt Libs
|
||||
from salt.states import boto_cloudwatch_alarm
|
||||
|
||||
boto_cloudwatch_alarm.__salt__ = {}
|
||||
boto_cloudwatch_alarm.__opts__ = {}
|
||||
|
||||
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
class BotoCloudwatchAlarmTestCase(TestCase):
|
||||
'''
|
||||
Test cases for salt.states.boto_cloudwatch_alarm
|
||||
'''
|
||||
# 'present' function tests: 1
|
||||
|
||||
def test_present(self):
|
||||
'''
|
||||
Test to ensure the cloudwatch alarm exists.
|
||||
'''
|
||||
name = 'my test alarm'
|
||||
attributes = {'metric': 'ApproximateNumberOfMessagesVisible',
|
||||
'namespace': 'AWS/SQS'}
|
||||
|
||||
ret = {'name': name,
|
||||
'result': None,
|
||||
'changes': {},
|
||||
'comment': ''}
|
||||
|
||||
mock = MagicMock(side_effect=[['ok_actions'], [], []])
|
||||
mock_bool = MagicMock(return_value=True)
|
||||
with patch.dict(boto_cloudwatch_alarm.__salt__,
|
||||
{'boto_cloudwatch.get_alarm': mock,
|
||||
'boto_cloudwatch.create_or_update_alarm': mock_bool}):
|
||||
with patch.dict(boto_cloudwatch_alarm.__opts__, {'test': True}):
|
||||
comt = ('alarm my test alarm is to be created/updated.')
|
||||
ret.update({'comment': comt})
|
||||
self.assertDictEqual(boto_cloudwatch_alarm.present(name,
|
||||
attributes),
|
||||
ret)
|
||||
|
||||
comt = ('alarm my test alarm is to be created/updated.')
|
||||
ret.update({'comment': comt})
|
||||
self.assertDictEqual(boto_cloudwatch_alarm.present(name,
|
||||
attributes),
|
||||
ret)
|
||||
|
||||
with patch.dict(boto_cloudwatch_alarm.__opts__, {'test': False}):
|
||||
changes = {'new':
|
||||
{'metric': 'ApproximateNumberOfMessagesVisible',
|
||||
'namespace': 'AWS/SQS'}}
|
||||
comt = ('alarm my test alarm is to be created/updated.')
|
||||
ret.update({'changes': changes, 'comment': '', 'result': True})
|
||||
self.assertDictEqual(boto_cloudwatch_alarm.present(name,
|
||||
attributes),
|
||||
ret)
|
||||
|
||||
# 'absent' function tests: 1
|
||||
|
||||
def test_absent(self):
|
||||
'''
|
||||
Test to ensure the named cloudwatch alarm is deleted.
|
||||
'''
|
||||
name = 'my test alarm'
|
||||
|
||||
ret = {'name': name,
|
||||
'result': None,
|
||||
'changes': {},
|
||||
'comment': ''}
|
||||
|
||||
mock = MagicMock(side_effect=[True, False])
|
||||
with patch.dict(boto_cloudwatch_alarm.__salt__,
|
||||
{'boto_cloudwatch.get_alarm': mock}):
|
||||
with patch.dict(boto_cloudwatch_alarm.__opts__, {'test': True}):
|
||||
comt = ('alarm {0} is set to be removed.'.format(name))
|
||||
ret.update({'comment': comt})
|
||||
self.assertDictEqual(boto_cloudwatch_alarm.absent(name), ret)
|
||||
|
||||
comt = ('my test alarm does not exist in None.')
|
||||
ret.update({'comment': comt, 'result': True})
|
||||
self.assertDictEqual(boto_cloudwatch_alarm.absent(name), ret)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from integration import run_tests
|
||||
run_tests(BotoCloudwatchAlarmTestCase, needs_daemon=False)
|
119
tests/unit/states/boto_dynamodb_test.py
Normal file
119
tests/unit/states/boto_dynamodb_test.py
Normal file
|
@ -0,0 +1,119 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
:codeauthor: :email:`Jayesh Kariya <jayeshk@saltstack.com>`
|
||||
'''
|
||||
# Import Python libs
|
||||
from __future__ import absolute_import
|
||||
|
||||
# Import Salt Testing Libs
|
||||
from salttesting import skipIf, TestCase
|
||||
from salttesting.mock import (
|
||||
NO_MOCK,
|
||||
NO_MOCK_REASON,
|
||||
MagicMock,
|
||||
patch)
|
||||
|
||||
from salttesting.helpers import ensure_in_syspath
|
||||
|
||||
ensure_in_syspath('../../')
|
||||
|
||||
# Import Salt Libs
|
||||
from salt.states import boto_dynamodb
|
||||
|
||||
boto_dynamodb.__salt__ = {}
|
||||
boto_dynamodb.__opts__ = {}
|
||||
|
||||
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
class BotoDynamodbTestCase(TestCase):
|
||||
'''
|
||||
Test cases for salt.states.boto_dynamodb
|
||||
'''
|
||||
# 'present' function tests: 1
|
||||
|
||||
def test_present(self):
|
||||
'''
|
||||
Test to ensure the DynamoDB table exists.
|
||||
'''
|
||||
name = 'new_table'
|
||||
|
||||
ret = {'name': name,
|
||||
'result': True,
|
||||
'changes': {},
|
||||
'comment': ''}
|
||||
|
||||
mock = MagicMock(side_effect=[True, False, False])
|
||||
mock_bool = MagicMock(return_value=True)
|
||||
with patch.dict(boto_dynamodb.__salt__,
|
||||
{'boto_dynamodb.exists': mock,
|
||||
'boto_dynamodb.create_table': mock_bool}):
|
||||
comt = ('DynamoDB table {0} already exists. \
|
||||
Nothing to change.'.format(name))
|
||||
ret.update({'comment': comt})
|
||||
self.assertDictEqual(boto_dynamodb.present(name), ret)
|
||||
|
||||
with patch.dict(boto_dynamodb.__opts__, {'test': True}):
|
||||
comt = ('DynamoDB table {0} is set to be created \
|
||||
'.format(name))
|
||||
ret.update({'comment': comt, 'result': None})
|
||||
self.assertDictEqual(boto_dynamodb.present(name), ret)
|
||||
|
||||
changes = {'new': {'global_indexes': None,
|
||||
'hash_key': (None,),
|
||||
'hash_key_data_type': None,
|
||||
'local_indexes': (None,),
|
||||
'range_key': (None,),
|
||||
'range_key_data_type': (None,),
|
||||
'read_capacity_units': (None,),
|
||||
'table': 'new_table',
|
||||
'write_capacity_units': (None,)},
|
||||
'old': None}
|
||||
|
||||
with patch.dict(boto_dynamodb.__opts__, {'test': False}):
|
||||
comt = ('DynamoDB table {0} created successfully \
|
||||
'.format(name))
|
||||
ret.update({'comment': comt, 'result': True,
|
||||
'changes': changes})
|
||||
self.assertDictEqual(boto_dynamodb.present(name), ret)
|
||||
|
||||
# 'absent' function tests: 1
|
||||
|
||||
def test_absent(self):
|
||||
'''
|
||||
Test to ensure the DynamoDB table does not exist.
|
||||
'''
|
||||
name = 'new_table'
|
||||
|
||||
ret = {'name': name,
|
||||
'result': True,
|
||||
'changes': {},
|
||||
'comment': ''}
|
||||
|
||||
mock = MagicMock(side_effect=[False, True, True])
|
||||
mock_bool = MagicMock(return_value=True)
|
||||
with patch.dict(boto_dynamodb.__salt__,
|
||||
{'boto_dynamodb.exists': mock,
|
||||
'boto_dynamodb.delete': mock_bool}):
|
||||
comt = ('DynamoDB table {0} does not exist'.format(name))
|
||||
ret.update({'comment': comt})
|
||||
self.assertDictEqual(boto_dynamodb.absent(name), ret)
|
||||
|
||||
with patch.dict(boto_dynamodb.__opts__, {'test': True}):
|
||||
comt = ('DynamoDB table {0} is set to be deleted \
|
||||
'.format(name))
|
||||
ret.update({'comment': comt, 'result': None})
|
||||
self.assertDictEqual(boto_dynamodb.absent(name), ret)
|
||||
|
||||
changes = {'new': 'Table new_table deleted',
|
||||
'old': 'Table new_table exists'}
|
||||
|
||||
with patch.dict(boto_dynamodb.__opts__, {'test': False}):
|
||||
comt = ('Deleted DynamoDB table {0}'.format(name))
|
||||
ret.update({'comment': comt, 'result': True,
|
||||
'changes': changes})
|
||||
self.assertDictEqual(boto_dynamodb.absent(name), ret)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from integration import run_tests
|
||||
run_tests(BotoDynamodbTestCase, needs_daemon=False)
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
# Import Python Libs
|
||||
from __future__ import absolute_import
|
||||
import sys
|
||||
|
||||
# Import Salt Testing Libs
|
||||
from salttesting import TestCase, skipIf
|
||||
|
@ -57,6 +58,7 @@ class MockGrains(object):
|
|||
return {'A': 'B'}
|
||||
|
||||
|
||||
@skipIf(sys.version_info < (2, 7), 'This needs to be refactored to work with Python 2.6')
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
class NetworkTestCase(TestCase):
|
||||
'''
|
||||
|
|
Loading…
Add table
Reference in a new issue