Merge branch '2014.7' into develop

Conflicts:
	salt/modules/virtualenv_mod.py
	salt/states/network.py
	salt/utils/parsers.py
This commit is contained in:
Thomas S Hatch 2014-12-09 10:59:18 -07:00
commit 3d73816507
14 changed files with 129 additions and 70 deletions

View File

@ -29,13 +29,13 @@ for :doc:`Cobbler <../../ref/tops/all/salt.tops.cobbler>` or:
for :doc:`Reclass <../../ref/tops/all/salt.tops.reclass_adapter>`.
It's also possible to create custom master_tops modules. These modules must go
It's also possible to create custom master_tops modules. These modules must go
in a subdirectory called `tops` in the `extension_modules` directory.
The `extension_modules` directory is not defined by default (the
default `/srv/salt/_modules` will NOT work as of this release)
Custom tops modules are written like any other execution module, see the source
for the two modules above for examples of fully functional ones. Below is
for the two modules above for examples of fully functional ones. Below is
a degenerate example:
/etc/salt/master:
@ -48,7 +48,7 @@ a degenerate example:
/srv/salt/modules/tops/customtop.py:
.. code-block: python
.. code-block:: python
import logging
import sys
@ -67,7 +67,7 @@ a degenerate example:
`salt minion state.show_top` should then display something like:
.. code-block: bash
.. code-block:: bash
$ salt minion state.show_top
@ -75,5 +75,3 @@ a degenerate example:
----------
base:
- test

View File

@ -9,5 +9,7 @@ Version 2014.7.1 is a bugfix release for :doc:`2014.7.0
- Fixed gitfs serving symlinks in :mod:`file.recurse
<salt.states.file.recurse>` states (:issue:`17700`)
- Fix holding of multiple packages (YUM) when combined with version pinning
- Fixed holding of multiple packages (YUM) when combined with version pinning
(:issue:`18468`)
- Fixed use of Jinja templates in masterless mode with non-roots fileserver
backend (:issue:`17963`)

View File

@ -138,6 +138,23 @@ def clean_expired_tokens(opts):
pass
def clean_pub_auth(opts):
try:
auth_cache = os.path.join(opts['cachedir'], 'publish_auth')
if not os.path.exists(auth_cache):
return
else:
for (dirpath, dirnames, filenames) in os.walkpath(auth_cache):
for auth_file in filenames:
auth_file_path = os.path.join(dirpath, auth_file)
if not os.path.isfile(auth_file_path):
continue
if os.path.getmtime(auth_file_path) - time.time() > opts['keep_jobs']:
os.remove(auth_file_path)
except (IOError, OSError):
log.error('Unable to delete pub auth file')
def clean_old_jobs(opts):
'''
Clean out the old jobs from the job cache

View File

@ -190,6 +190,8 @@ class Maintenance(multiprocessing.Process):
last = int(time.time())
# Clean out the fileserver backend cache
salt.daemons.masterapi.clean_fsbackend(self.opts)
# Clean out pub auth
salt.daemons.masterapi.clean_pub_auth(self.opts)
old_present = set()
while True:

View File

@ -958,8 +958,8 @@ def list_pkgs(versions_as_list=False,
return ret
ret = {'installed': {}, 'removed': {}, 'purge_desired': {}}
cmd = ('dpkg-query', '--showformat',
'${Status} ${Package} ${Version} ${Architecture}\n', '-W')
cmd = ['dpkg-query', '--showformat',
'${Status} ${Package} ${Version} ${Architecture}\n', '-W']
out = __salt__['cmd.run_stdout'](
cmd,

View File

@ -1089,6 +1089,8 @@ def _parse_bridge_opts(opts, iface):
config = {}
if 'ports' in opts:
if isinstance(opts['ports'], list):
opts['ports'] = ','.join(opts['ports'])
config.update({'ports': opts['ports']})
for opt in ['ageing', 'fd', 'gcint', 'hello', 'maxage']:
@ -1539,7 +1541,7 @@ def build_interface(iface, iface_type, enabled, **settings):
elif iface_type == 'bridge':
if 'ports' not in settings:
msg = 'ports is a required setting for bridge interfaces'
msg = 'ports is a required setting for bridge interfaces on Debian or Ubuntu based systems'
log.error(msg)
raise AttributeError(msg)
__salt__['pkg.install']('bridge-utils')

View File

@ -52,8 +52,8 @@ def _publish(
salt system.example.com publish.publish '*' cmd.run 'ls -la /tmp'
'''
if fun == 'publish.publish':
log.info('Function name is \'publish.publish\'. Returning {}')
if fun.startswith('publish.'):
log.info('Cannot publish publish calls. Returning {}')
return {}
if not isinstance(arg, list):
@ -223,7 +223,8 @@ def full_data(tgt, fun, arg=None, expr_form='glob', returner='', timeout=5):
expr_form=expr_form,
returner=returner,
timeout=timeout,
form='full')
form='full',
wait=True)
def runner(fun, arg=None, timeout=5):

View File

@ -10,10 +10,6 @@ import shutil
import logging
import os
import os.path
try:
from shlex import quote as _cmd_quote # pylint: disable=E0611
except ImportError:
from pipes import quote as _cmd_quote
# Import salt libs
import salt.utils
@ -130,7 +126,7 @@ def create(path,
elif runas is not None and not user:
user = str(runas)
cmd = [_cmd_quote(venv_bin)]
cmd = [venv_bin]
if 'pyvenv' not in venv_bin:
# ----- Stop the user if pyvenv only options are used --------------->
@ -158,8 +154,10 @@ def create(path,
)
except ImportError:
# Unable to import?? Let's parse the version from the console
version_cmd = '{0} --version'.format(_cmd_quote(venv_bin))
ret = __salt__['cmd.run_all'](version_cmd, runas=user)
version_cmd = '{0} --version'.format(venv_bin)
ret = __salt__['cmd.run_all'](
version_cmd, runas=user, python_shell=False
)
if ret['retcode'] > 0 or not ret['stdout'].strip():
raise salt.exceptions.CommandExecutionError(
'Unable to get the virtualenv version output using {0!r}. '
@ -187,7 +185,7 @@ def create(path,
'Requested python ({0}) does not appear '
'executable.'.format(python)
)
cmd.append('--python={0}'.format(_cmd_quote(python)))
cmd.append('--python={0}'.format(python))
if extra_search_dir is not None:
if isinstance(extra_search_dir, string_types) and \
extra_search_dir.strip() != '':
@ -195,7 +193,7 @@ def create(path,
e.strip() for e in extra_search_dir.split(',')
]
for entry in extra_search_dir:
cmd.append('--extra-search-dir={0}'.format(_cmd_quote(entry)))
cmd.append('--extra-search-dir={0}'.format(entry))
if never_download is True:
if virtualenv_version_info >= (1, 10):
log.info(
@ -207,7 +205,7 @@ def create(path,
else:
cmd.append('--never-download')
if prompt is not None and prompt.strip() != '':
cmd.append('--prompt={0!r}'.format(_cmd_quote(prompt)))
cmd.append('--prompt={0!r}'.format(prompt))
else:
# venv module from the Python >= 3.3 standard library
@ -248,10 +246,10 @@ def create(path,
cmd.append('--system-site-packages')
# Finally the virtualenv path
cmd.append(_cmd_quote(path))
cmd.append(path)
# Let's create the virtualenv
ret = __salt__['cmd.run_all'](' '.join(cmd), runas=user)
ret = __salt__['cmd.run_all'](cmd, runas=user, python_shell=False)
if ret['retcode'] > 0:
# Something went wrong. Let's bail out now!
return ret
@ -314,7 +312,7 @@ def get_site_packages(venv):
raise salt.exceptions.CommandExecutionError(
"Path does not appear to be a virtualenv: '{0}'".format(bin_path))
return __salt__['cmd.exec_code'](_cmd_quote(bin_path),
return __salt__['cmd.exec_code'](bin_path,
'from distutils import sysconfig; print sysconfig.get_python_lib()')
@ -331,11 +329,12 @@ def _install_script(source, cwd, python, user, saltenv='base', use_vt=False):
os.chown(tmppath, __salt__['file.user_to_uid'](user), -1)
try:
return __salt__['cmd.run_all'](
'{0} {1}'.format(_cmd_quote(python), _cmd_quote(tmppath)),
'{0} {1}'.format(python, tmppath),
runas=user,
cwd=cwd,
env={'VIRTUAL_ENV': cwd},
use_vt=use_vt
use_vt=use_vt,
python_shell=False,
)
finally:
os.remove(tmppath)

View File

@ -2,6 +2,9 @@
'''
Management of Mongodb users
===========================
.. note::
This module requires PyMongo to be installed.
'''
# Define the module's virtual name

View File

@ -171,7 +171,6 @@ def mounted(name,
+ "options changed"
remount_result = __salt__['mount.remount'](real_name, device, mkmnt=mkmnt, fstype=fstype, opts=opts, user=user)
ret['result'] = remount_result
return ret
if real_device not in device_list:
# name matches but device doesn't - need to umount
if __opts__['test']:

View File

@ -148,6 +148,7 @@ all interfaces are ignored unless specified.
- proto: dhcp
- bridge: br0
- delay: 0
- ports: eth4
- bypassfirewall: True
- use:
- network: eth4
@ -170,6 +171,10 @@ all interfaces are ignored unless specified.
.. versionadded:: Lithium
.. note::
When managing bridged interfaces on a Debian or Ubuntu based system, the
ports argument is required. Red Hat systems will ignore the argument.
'''
from __future__ import absolute_import

View File

@ -42,6 +42,9 @@ try:
BASE_MASTER_ROOTS_DIR,
LOGS_DIR,
PIDFILE_DIR,
CLOUD_DIR,
INSTALL_DIR,
BOOTSTRAP,
)
except ImportError:
# The installation time was not generated, let's define the default values

View File

@ -57,7 +57,8 @@ def build_pillar_data(options):
Build a YAML formatted string to properly pass pillar data
'''
pillar = {'test_transport': options.test_transport,
'cloud_only': options.cloud_only}
'cloud_only': options.cloud_only,
'with_coverage': options.test_without_coverage is False}
if options.test_git_commit is not None:
pillar['test_git_commit'] = options.test_git_commit
if options.test_git_url is not None:
@ -371,7 +372,8 @@ def run(opts):
)
if opts.download_remote_reports:
opts.download_coverage_report = vm_name
if opts.test_without_coverage is False:
opts.download_coverage_report = vm_name
opts.download_unittest_reports = vm_name
opts.download_packages = vm_name
@ -773,7 +775,8 @@ def run(opts):
# Download unittest reports
download_unittest_reports(opts)
# Download coverage report
download_coverage_report(opts)
if opts.test_without_coverage is False:
download_coverage_report(opts)
if opts.clean and 'JENKINS_SALTCLOUD_VM_NAME' not in os.environ:
delete_vm(opts)
@ -829,6 +832,12 @@ def parse():
default='zeromq',
choices=('zeromq', 'raet'),
help='Set to raet to run integration tests with raet transport. Default: %default')
parser.add_option(
'--test-without-coverage',
default=False,
action='store_true',
help='Do not generate coverage reports'
)
parser.add_option(
'--prep-sls',
default='git.salt',
@ -943,9 +952,10 @@ def parse():
download_unittest_reports(options)
parser.exit(0)
if options.download_coverage_report is not None and not options.test_git_commit:
download_coverage_report(options)
parser.exit(0)
if options.test_without_coverage is False:
if options.download_coverage_report is not None and not options.test_git_commit:
download_coverage_report(options)
parser.exit(0)
if options.download_remote_logs is not None and not options.test_git_commit:
download_remote_logs(options)

View File

@ -50,8 +50,9 @@ class VirtualenvTestCase(TestCase):
'/tmp/foo', system_site_packages=True, distribute=True
)
mock.assert_called_once_with(
'virtualenv --distribute --system-site-packages /tmp/foo',
runas=None
['virtualenv', '--distribute', '--system-site-packages', '/tmp/foo'],
runas=None,
python_shell=False
)
with TestsLoggingHandler() as handler:
@ -66,8 +67,9 @@ class VirtualenvTestCase(TestCase):
'/tmp/foo', system_site_packages=True, distribute=True
)
mock.assert_called_once_with(
'virtualenv --system-site-packages /tmp/foo',
runas=None
['virtualenv', '--system-site-packages', '/tmp/foo'],
runas=None,
python_shell=False
)
# Are we logging the deprecation information?
@ -87,8 +89,9 @@ class VirtualenvTestCase(TestCase):
'/tmp/foo', never_download=True
)
mock.assert_called_once_with(
'virtualenv --never-download /tmp/foo',
runas=None
['virtualenv', '--never-download', '/tmp/foo'],
runas=None,
python_shell=False
)
with TestsLoggingHandler() as handler:
@ -102,8 +105,9 @@ class VirtualenvTestCase(TestCase):
virtualenv_mod.create(
'/tmp/foo', never_download=True
)
mock.assert_called_once_with('virtualenv /tmp/foo',
runas=None)
mock.assert_called_once_with(['virtualenv', '/tmp/foo'],
runas=None,
python_shell=False)
# Are we logging the deprecation information?
self.assertIn(
@ -128,12 +132,13 @@ class VirtualenvTestCase(TestCase):
'/tmp/foo', extra_search_dir=extra_search_dirs
)
mock.assert_called_once_with(
'virtualenv '
'--extra-search-dir=/tmp/bar-1 '
'--extra-search-dir=/tmp/bar-2 '
'--extra-search-dir=/tmp/bar-3 '
'/tmp/foo',
runas=None
['virtualenv',
'--extra-search-dir=/tmp/bar-1',
'--extra-search-dir=/tmp/bar-2',
'--extra-search-dir=/tmp/bar-3',
'/tmp/foo'],
runas=None,
python_shell=False
)
# Passing extra_search_dirs as comma separated list
@ -143,12 +148,13 @@ class VirtualenvTestCase(TestCase):
'/tmp/foo', extra_search_dir=','.join(extra_search_dirs)
)
mock.assert_called_once_with(
'virtualenv '
'--extra-search-dir=/tmp/bar-1 '
'--extra-search-dir=/tmp/bar-2 '
'--extra-search-dir=/tmp/bar-3 '
'/tmp/foo',
runas=None
['virtualenv',
'--extra-search-dir=/tmp/bar-1',
'--extra-search-dir=/tmp/bar-2',
'--extra-search-dir=/tmp/bar-3',
'/tmp/foo'],
runas=None,
python_shell=False
)
def test_unapplicable_options(self):
@ -253,8 +259,9 @@ class VirtualenvTestCase(TestCase):
'/tmp/foo', never_download=True
)
mock.assert_called_with(
'virtualenv --never-download /tmp/foo',
runas=None
['virtualenv', '--never-download', '/tmp/foo'],
runas=None,
python_shell=False
)
# <---- virtualenv binary returns 1.9.1 as its version ----------
@ -268,8 +275,9 @@ class VirtualenvTestCase(TestCase):
'/tmp/foo', never_download=True
)
mock.assert_called_with(
'virtualenv /tmp/foo',
runas=None
['virtualenv', '/tmp/foo'],
runas=None,
python_shell=False
)
# <---- virtualenv binary returns 1.10rc1 as its version --------
@ -281,8 +289,9 @@ class VirtualenvTestCase(TestCase):
'/tmp/foo', python=sys.executable,
)
mock.assert_called_once_with(
'virtualenv --python={0} /tmp/foo'.format(sys.executable),
runas=None
['virtualenv', '--python={0}'.format(sys.executable), '/tmp/foo'],
runas=None,
python_shell=False
)
def test_prompt_argument(self):
@ -290,8 +299,9 @@ class VirtualenvTestCase(TestCase):
with patch.dict(virtualenv_mod.__salt__, {'cmd.run_all': mock}):
virtualenv_mod.create('/tmp/foo', prompt='PY Prompt')
mock.assert_called_once_with(
'virtualenv --prompt=\'PY Prompt\' /tmp/foo',
runas=None
['virtualenv', '--prompt=\'PY Prompt\'', '/tmp/foo'],
runas=None,
python_shell=False
)
# Now with some quotes on the mix
@ -299,16 +309,18 @@ class VirtualenvTestCase(TestCase):
with patch.dict(virtualenv_mod.__salt__, {'cmd.run_all': mock}):
virtualenv_mod.create('/tmp/foo', prompt='\'PY\' Prompt')
mock.assert_called_once_with(
'virtualenv --prompt="\'PY\' Prompt" /tmp/foo',
runas=None
['virtualenv', '--prompt="\'PY\' Prompt"', '/tmp/foo'],
runas=None,
python_shell=False
)
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(virtualenv_mod.__salt__, {'cmd.run_all': mock}):
virtualenv_mod.create('/tmp/foo', prompt='"PY" Prompt')
mock.assert_called_once_with(
'virtualenv --prompt=\'"PY" Prompt\' /tmp/foo',
runas=None
['virtualenv', '--prompt=\'"PY" Prompt\'', '/tmp/foo'],
runas=None,
python_shell=False
)
def test_clear_argument(self):
@ -316,7 +328,9 @@ class VirtualenvTestCase(TestCase):
with patch.dict(virtualenv_mod.__salt__, {'cmd.run_all': mock}):
virtualenv_mod.create('/tmp/foo', clear=True)
mock.assert_called_once_with(
'virtualenv --clear /tmp/foo', runas=None
['virtualenv', '--clear', '/tmp/foo'],
runas=None,
python_shell=False
)
def test_upgrade_argument(self):
@ -326,7 +340,9 @@ class VirtualenvTestCase(TestCase):
with patch.dict(virtualenv_mod.__salt__, {'cmd.run_all': mock}):
virtualenv_mod.create('/tmp/foo', venv_bin='pyvenv', upgrade=True)
mock.assert_called_once_with(
'pyvenv --upgrade /tmp/foo', runas=None
['pyvenv', '--upgrade', '/tmp/foo'],
runas=None,
python_shell=False
)
def test_symlinks_argument(self):
@ -336,7 +352,9 @@ class VirtualenvTestCase(TestCase):
with patch.dict(virtualenv_mod.__salt__, {'cmd.run_all': mock}):
virtualenv_mod.create('/tmp/foo', venv_bin='pyvenv', symlinks=True)
mock.assert_called_once_with(
'pyvenv --symlinks /tmp/foo', runas=None
['pyvenv', '--symlinks', '/tmp/foo'],
runas=None,
python_shell=False
)