diff --git a/doc/topics/cloud/vmware.rst b/doc/topics/cloud/vmware.rst index d943222ae2..6e7aff55b2 100644 --- a/doc/topics/cloud/vmware.rst +++ b/doc/topics/cloud/vmware.rst @@ -38,6 +38,14 @@ This package can be installed using `pip` or `easy_install`: .. _Issue #29537: https://github.com/saltstack/salt/issues/29537 +.. note:: + + pyVmomi doesn't expose the ability to specify the locale when connecting to + VMware. This causes parsing issues when connecting to an instance of VMware + running under a non-English locale. Until this feature is added upstream + `Issue #38402`_ contains a workaround. + +.. _Issue #38402: https://github.com/saltstack/salt/issues/38402 Configuration ============= diff --git a/doc/topics/installation/arch.rst b/doc/topics/installation/arch.rst index 62c4e63d3f..0b146e3a26 100644 --- a/doc/topics/installation/arch.rst +++ b/doc/topics/installation/arch.rst @@ -16,18 +16,7 @@ Install Salt stable releases from the Arch Linux Official repositories as follow .. code-block:: bash - pacman -S salt-zmq - -To install Salt stable releases using the :ref:`RAET protocol`, -use the following: - -.. code-block:: bash - - pacman -S salt-raet - -.. note:: transports - - Unlike other linux distributions, please be aware that Arch Linux's package manager pacman defaults to RAET as the Salt transport. If you want to use ZeroMQ instead, make sure to enter the associated number for the salt-zmq repository when prompted. + pacman -S salt Tracking develop ---------------- diff --git a/salt/beacons/memusage.py b/salt/beacons/memusage.py index 786f883f33..d779f9c58e 100644 --- a/salt/beacons/memusage.py +++ b/salt/beacons/memusage.py @@ -12,9 +12,6 @@ from __future__ import absolute_import import logging import re -# Import Salt libs -import salt.utils - # Import Third Party Libs try: import psutil @@ -28,9 +25,7 @@ __virtualname__ = 'memusage' def __virtual__(): - if salt.utils.is_windows(): - return False - elif HAS_PSUTIL is False: + if HAS_PSUTIL is False: return False else: return __virtualname__ diff --git a/salt/config/__init__.py b/salt/config/__init__.py index a32e6802bc..603cd6c9e0 100644 --- a/salt/config/__init__.py +++ b/salt/config/__init__.py @@ -2958,6 +2958,8 @@ def is_profile_configured(opts, provider, profile_name, vm_=None): if driver == 'linode' and profile_key.get('clonefrom', False): non_image_drivers.append('linode') non_size_drivers.append('linode') + elif driver == 'gce' and 'sourceImage' in str(vm_.get('ex_disks_gce_struct')): + non_image_drivers.append('gce') # If cloning on VMware, specifying image is not necessary. if driver == 'vmware' and 'image' not in list(profile_key.keys()): @@ -3456,10 +3458,15 @@ def api_config(path): Read in the Salt Master config file and add additional configs that need to be stubbed out for salt-api ''' - # Let's grab a copy of salt-api's required defaults - opts = DEFAULT_API_OPTS - # Let's override them with salt's master opts - opts.update(client_config(path, defaults=DEFAULT_MASTER_OPTS)) + # Let's grab a copy of salt's master opts + opts = client_config(path, defaults=DEFAULT_MASTER_OPTS) + # Let's override them with salt-api's required defaults + api_opts = DEFAULT_API_OPTS + api_opts.update({ + 'pidfile': opts.get('api_pidfile', DEFAULT_API_OPTS['api_pidfile']), + 'log_file': opts.get('api_logfile', DEFAULT_API_OPTS['api_logfile']), + }) + opts.update(api_opts) prepend_root_dir(opts, [ 'api_pidfile', 'api_logfile', diff --git a/salt/engines/slack.py b/salt/engines/slack.py index 071e691197..b3e212c590 100644 --- a/salt/engines/slack.py +++ b/salt/engines/slack.py @@ -34,10 +34,10 @@ prefaced with a ``!``. # Import python libraries from __future__ import absolute_import import datetime +import json import logging import time import re -import json import yaml try: @@ -231,5 +231,6 @@ def start(token, else: # Fire event to event bus fire('{0}/{1}'.format(tag, _m['type']), _m) + time.sleep(1) else: raise UserWarning("Could not connect to slack") diff --git a/salt/fileclient.py b/salt/fileclient.py index afb521ca35..cb33e3f8e9 100644 --- a/salt/fileclient.py +++ b/salt/fileclient.py @@ -713,6 +713,9 @@ class Client(object): else: netloc = url_data.netloc + # Strip user:pass from URLs + netloc = netloc.split('@')[-1] + if cachedir is None: cachedir = self.opts['cachedir'] elif not os.path.isabs(cachedir): diff --git a/salt/modules/napalm_network.py b/salt/modules/napalm_network.py index 52854b2aba..81746a268c 100644 --- a/salt/modules/napalm_network.py +++ b/salt/modules/napalm_network.py @@ -370,7 +370,7 @@ def cli(*commands): # in case of errors, they'll be catched in the proxy -def traceroute(destination, source='', ttl=0, timeout=0): +def traceroute(destination, source=None, ttl=None, timeout=None): ''' Calls the method traceroute from the NAPALM driver object and returns a dictionary with the result of the traceroute @@ -400,7 +400,7 @@ def traceroute(destination, source='', ttl=0, timeout=0): ) -def ping(destination, source='', ttl=0, timeout=0, size=0, count=0): +def ping(destination, source=None, ttl=None, timeout=None, size=None, count=None): ''' Executes a ping on the network device and returns a dictionary as a result. diff --git a/salt/modules/napalm_route.py b/salt/modules/napalm_route.py index 644e65f134..ad2a72bd28 100644 --- a/salt/modules/napalm_route.py +++ b/salt/modules/napalm_route.py @@ -68,7 +68,7 @@ def __virtual__(): # ---------------------------------------------------------------------------------------------------------------------- -def show(destination, protocol): +def show(destination, protocol=None): ''' Displays all details for a certain route learned via a specific protocol. diff --git a/salt/states/archive.py b/salt/states/archive.py index 747be42761..cc37291396 100644 --- a/salt/states/archive.py +++ b/salt/states/archive.py @@ -869,6 +869,7 @@ def extracted(name, else: source_sum = {} + concurrent = bool(__opts__.get('sudo_user')) if not source_is_local and not os.path.isfile(cached_source): if __opts__['test']: ret['result'] = None @@ -885,9 +886,19 @@ def extracted(name, source_hash_name=source_hash_name, makedirs=True, skip_verify=skip_verify, - saltenv=__env__) + saltenv=__env__, + concurrent=concurrent) log.debug('file.managed: {0}'.format(file_result)) + # Prevent a traceback if errors prevented the above state from getting + # off the ground. + if isinstance(file_result, list): + try: + ret['comment'] = '\n'.join(file_result) + except TypeError: + ret['comment'] = '\n'.join([str(x) for x in file_result]) + return ret + # Get actual state result. The state.single return is a single-element # dictionary with the state's unique ID at the top level, and its value # being the state's return dictionary. next(iter(dict_name)) will give @@ -1324,7 +1335,8 @@ def extracted(name, user=user, group=group, recurse=recurse, - test=__opts__['test']) + test=__opts__['test'], + concurrent=concurrent) try: dir_result = dir_result[next(iter(dir_result))] except AttributeError: diff --git a/salt/utils/jinja.py b/salt/utils/jinja.py index 10037fb77c..902000b56d 100644 --- a/salt/utils/jinja.py +++ b/salt/utils/jinja.py @@ -378,7 +378,6 @@ class SerializerExtension(Extension, object): super(SerializerExtension, self).__init__(environment) self.environment.filters.update({ 'yaml': self.format_yaml, - 'yaml_safe': self.format_yaml_safe, 'json': self.format_json, 'python': self.format_python, 'load_yaml': self.load_yaml, @@ -422,13 +421,6 @@ class SerializerExtension(Extension, object): yaml_txt = yaml_txt[:len(yaml_txt)-4] return Markup(yaml_txt) - def format_yaml_safe(self, value, flow_style=True): - yaml_txt = yaml.safe_dump(value, default_flow_style=flow_style, - Dumper=OrderedDictDumper).strip() - if yaml_txt.endswith('\n...\n'): - yaml_txt = yaml_txt[:len(yaml_txt-5)] - return Markup(yaml_txt) - def format_python(self, value): return Markup(pprint.pformat(value).strip()) diff --git a/salt/utils/yamlloader.py b/salt/utils/yamlloader.py index b33acd0c83..a51d1f1f25 100644 --- a/salt/utils/yamlloader.py +++ b/salt/utils/yamlloader.py @@ -45,6 +45,9 @@ class SaltYamlSafeLoader(yaml.SafeLoader, object): self.add_constructor( u'tag:yaml.org,2002:omap', type(self).construct_yaml_map) + self.add_constructor( + u'tag:yaml.org,2002:python/unicode', + type(self).construct_unicode) self.dictclass = dictclass def construct_yaml_map(self, node): @@ -53,6 +56,9 @@ class SaltYamlSafeLoader(yaml.SafeLoader, object): value = self.construct_mapping(node) data.update(value) + def construct_unicode(self, node): + return node.value + def construct_mapping(self, node, deep=False): ''' Build the mapping for YAML diff --git a/tests/integration/cloud/providers/gce.py b/tests/integration/cloud/providers/gce.py index e8fef5238b..65e03c1efd 100644 --- a/tests/integration/cloud/providers/gce.py +++ b/tests/integration/cloud/providers/gce.py @@ -22,6 +22,8 @@ ensure_in_syspath('../../../') # Import Third-Party Libs from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin +TIMEOUT = 500 + def _random_name(size=6): ''' @@ -92,18 +94,18 @@ class GCETest(integration.ShellCase): ''' # create the instance - instance = self.run_cloud('-p gce-test {0}'.format(self.INSTANCE_NAME), timeout=500) + instance = self.run_cloud('-p gce-test {0}'.format(self.INSTANCE_NAME), timeout=TIMEOUT) ret_str = '{0}:'.format(self.INSTANCE_NAME) # check if instance returned with salt installed try: self.assertIn(ret_str, instance) except AssertionError: - self.run_cloud('-d {0} --assume-yes'.format(self.INSTANCE_NAME), timeout=500) + self.run_cloud('-d {0} --assume-yes'.format(self.INSTANCE_NAME), timeout=TIMEOUT) raise # delete the instance - delete = self.run_cloud('-d {0} --assume-yes'.format(self.INSTANCE_NAME), timeout=500) + delete = self.run_cloud('-d {0} --assume-yes'.format(self.INSTANCE_NAME), timeout=TIMEOUT) # example response: ['gce-config:', '----------', ' gce:', '----------', 'cloud-test-dq4e6c:', 'True', ''] delete_str = ''.join(delete) @@ -120,18 +122,20 @@ class GCETest(integration.ShellCase): ''' # create the instance - instance = self.run_cloud('-p gce-test-extra {0}'.format(self.INSTANCE_NAME)) + instance = self.run_cloud('-p gce-test-extra \ + {0}'.format(self.INSTANCE_NAME), + timeout=TIMEOUT) ret_str = '{0}:'.format(self.INSTANCE_NAME) # check if instance returned with salt installed try: self.assertIn(ret_str, instance) except AssertionError: - self.run_cloud('-d {0} --assume-yes'.format(self.INSTANCE_NAME), timeout=500) + self.run_cloud('-d {0} --assume-yes'.format(self.INSTANCE_NAME), timeout=TIMEOUT) raise # delete the instance - delete = self.run_cloud('-d {0} --assume-yes'.format(self.INSTANCE_NAME), timeout=500) + delete = self.run_cloud('-d {0} --assume-yes'.format(self.INSTANCE_NAME), timeout=TIMEOUT) # example response: ['gce-config:', '----------', ' gce:', '----------', 'cloud-test-dq4e6c:', 'True', ''] delete_str = ''.join(delete) @@ -152,7 +156,7 @@ class GCETest(integration.ShellCase): # if test instance is still present, delete it if ret_str in query: - self.run_cloud('-d {0} --assume-yes'.format(self.INSTANCE_NAME), timeout=500) + self.run_cloud('-d {0} --assume-yes'.format(self.INSTANCE_NAME), timeout=TIMEOUT) if __name__ == '__main__': diff --git a/tests/unit/renderers/yaml_test.py b/tests/unit/renderers/yaml_test.py new file mode 100644 index 0000000000..45af476ab7 --- /dev/null +++ b/tests/unit/renderers/yaml_test.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- + +# Import Python Libs +from __future__ import absolute_import + +# Import Salt Testing libs +from salttesting import TestCase +from salttesting.helpers import ensure_in_syspath + +ensure_in_syspath('../../') + +# Import Salt libs +from salt.renderers import yaml + +yaml.__salt__ = {} +yaml.__opts__ = {} + + +class YAMLRendererTestCase(TestCase): + def test_yaml_render_string(self): + data = "string" + result = yaml.render(data) + + self.assertEqual(result, data) + + def test_yaml_render_unicode(self): + data = "!!python/unicode python unicode string" + result = yaml.render(data) + + self.assertEqual(result, u"python unicode string") diff --git a/tests/unit/templates/jinja_test.py b/tests/unit/templates/jinja_test.py index 61960a128f..d5badee466 100644 --- a/tests/unit/templates/jinja_test.py +++ b/tests/unit/templates/jinja_test.py @@ -504,7 +504,14 @@ class TestCustomExtensions(TestCase): "foo": True, "bar": 42, "baz": [1, 2, 3], - "qux": 2.0 + "qux": 2.0, + "spam": OrderedDict([ + ('foo', OrderedDict([ + ('bar', 'baz'), + ('qux', 42) + ]) + ) + ]) } env = Environment(extensions=[SerializerExtension]) rendered = env.from_string('{{ dataset|yaml }}').render(dataset=dataset) @@ -516,6 +523,12 @@ class TestCustomExtensions(TestCase): rendered = env.from_string('{{ dataset|yaml }}').render(dataset=dataset) self.assertEqual(dataset, rendered) + def test_serialize_yaml_unicode(self): + dataset = u"str value" + env = Environment(extensions=[SerializerExtension]) + rendered = env.from_string('{{ dataset|yaml }}').render(dataset=dataset) + self.assertEqual("!!python/unicode str value", rendered) + def test_serialize_python(self): dataset = { "foo": True,