Browse code

Tracebacks are now catchable with ignore_errors and have streamlined output. Also removes 'baby-JSON' for bash modules.

Michael DeHaan authored on 2014/09/12 01:26:54
Showing 9 changed files
... ...
@@ -3,7 +3,7 @@ Ansible Changes By Release
3 3
 
4 4
 ## 1.8 "You Really Got Me" - Active Development
5 5
 
6
-New core features:
6
+Major changes:
7 7
 
8 8
 * fact caching support, pluggable, initially supports Redis (DOCS pending)
9 9
 * 'serial' size in a rolling update can be specified as a percentage
... ...
@@ -15,6 +15,7 @@ New core features:
15 15
 * ansible-galaxy install -f requirements.yml allows advanced options and installs from non-galaxy SCM sources and tarballs.
16 16
 * command_warnings feature will warn about when usage of the shell/command module can be simplified to use core modules - this can be enabled in ansible.cfg
17 17
 * new omit value can be used to leave off a parameter when not set, like so module_name: a=1 b={{ c | default(omit) }}, would not pass value for b (not even an empty value) if c was not set.
18
+* developers: 'baby JSON' in module responses, originally intended for writing modules in bash, is removed as a feature to simplify logic, script module remains available for running bash scripts.
18 19
 
19 20
 New Modules:
20 21
 
... ...
@@ -30,6 +31,7 @@ New Modules:
30 30
 
31 31
 Some other notable changes:
32 32
 
33
+* if a module should ever traceback, it will return a standard error, catchable by ignore_errors, versus an 'unreachable'
33 34
 * ec2_lc: added support for multiple new parameters like kernel_id, ramdisk_id and ebs_optimized.
34 35
 * ec2_elb_lb: added support for the connection_draining_timeout and cross_az_load_balancing options.
35 36
 * support for symbolic representations (ie. u+rw) for file permission modes (file/copy/template modules etc.).
... ...
@@ -493,7 +493,7 @@ class PlaybookRunnerCallbacks(DefaultRunnerCallbacks):
493 493
         if returned_msg:
494 494
             display("msg: %s" % returned_msg, color='red', runner=self.runner)
495 495
         if not parsed and module_msg:
496
-            display("invalid output was: %s" % module_msg, color='red', runner=self.runner)
496
+            display(module_msg, color='red', runner=self.runner)
497 497
         if ignore_errors:
498 498
             display("...ignoring", color='cyan', runner=self.runner)
499 499
         super(PlaybookRunnerCallbacks, self).on_failed(host, results, ignore_errors=ignore_errors)
... ...
@@ -138,4 +138,10 @@ class InventoryScript(object):
138 138
         except OSError, e:
139 139
             raise errors.AnsibleError("problem running %s (%s)" % (' '.join(cmd), e))
140 140
         (out, err) = sp.communicate()
141
-        return utils.parse_json(out)
141
+        if out.strip() == '':
142
+            return dict()
143
+        try:
144
+            return utils.parse_json(out)
145
+        except ValueError:
146
+            raise errors.AnsibleError("could not parse post variable response: %s, %s" % (cmd, out))
147
+
... ...
@@ -539,7 +539,7 @@ class Runner(object):
539 539
                 cmd2 = conn.shell.remove(tmp, recurse=True)
540 540
                 self._low_level_exec_command(conn, cmd2, tmp, sudoable=False)
541 541
 
542
-        data = utils.parse_json(res['stdout'], from_remote=True)
542
+        data = utils.parse_json(res['stdout'], from_remote=True, no_exceptions=True)
543 543
         if 'parsed' in data and data['parsed'] == False:
544 544
             data['msg'] += res['stderr']
545 545
         return ReturnData(conn=conn, result=data)
... ...
@@ -43,7 +43,7 @@ class ReturnData(object):
43 43
         self.diff = diff
44 44
 
45 45
         if type(self.result) in [ str, unicode ]:
46
-            self.result = utils.parse_json(self.result, from_remote=True)
46
+            self.result = utils.parse_json(self.result, from_remote=True, exceptions=False)
47 47
 
48 48
         if self.host is None:
49 49
             raise Exception("host not set")
... ...
@@ -506,7 +506,7 @@ def _clean_data_struct(orig_data, from_remote=False, from_inventory=False):
506 506
         data = orig_data
507 507
     return data
508 508
 
509
-def parse_json(raw_data, from_remote=False, from_inventory=False):
509
+def parse_json(raw_data, from_remote=False, from_inventory=False, no_exceptions=False):
510 510
     ''' this version for module return data only '''
511 511
 
512 512
     orig_data = raw_data
... ...
@@ -517,28 +517,10 @@ def parse_json(raw_data, from_remote=False, from_inventory=False):
517 517
     try:
518 518
         results = json.loads(data)
519 519
     except:
520
-        # not JSON, but try "Baby JSON" which allows many of our modules to not
521
-        # require JSON and makes writing modules in bash much simpler
522
-        results = {}
523
-        try:
524
-            tokens = shlex.split(data)
525
-        except:
526
-            print "failed to parse json: "+ data
520
+        if no_exceptions:
521
+            return dict(failed=True, parsed=False, msg=raw_data)
522
+        else:
527 523
             raise
528
-        for t in tokens:
529
-            if "=" not in t:
530
-                raise errors.AnsibleError("failed to parse: %s" % orig_data)
531
-            (key,value) = t.split("=", 1)
532
-            if key == 'changed' or 'failed':
533
-                if value.lower() in [ 'true', '1' ]:
534
-                    value = True
535
-                elif value.lower() in [ 'false', '0' ]:
536
-                    value = False
537
-            if key == 'rc':
538
-                value = int(value)
539
-            results[key] = value
540
-        if len(results.keys()) == 0:
541
-            return { "failed" : True, "parsed" : False, "msg" : orig_data }
542 524
 
543 525
     if from_remote:
544 526
         results = _clean_data_struct(results, from_remote, from_inventory)
... ...
@@ -37,6 +37,8 @@ EXAMPLES = '''
37 37
 ansible webservers -m ping
38 38
 '''
39 39
 
40
+import exceptions
41
+
40 42
 def main():
41 43
     module = AnsibleModule(
42 44
         argument_spec = dict(
... ...
@@ -46,6 +48,8 @@ def main():
46 46
     )
47 47
     result = dict(ping='pong')
48 48
     if module.params['data']:
49
+        if module.params['data'] == 'crash':
50
+            raise exceptions.Exception("boom")
49 51
         result['ping'] = module.params['data']
50 52
     module.exit_json(**result)
51 53
 
... ...
@@ -1,5 +1,6 @@
1 1
 # -*- coding: utf-8 -*-
2 2
 
3
+import traceback
3 4
 import unittest
4 5
 import os
5 6
 import os.path
... ...
@@ -217,36 +218,23 @@ class TestUtils(unittest.TestCase):
217 217
         # leading junk
218 218
         self.assertEqual(ansible.utils.parse_json('ansible\n{"foo": "bar"}'), dict(foo="bar"))
219 219
 
220
-        # "baby" json
221
-        self.assertEqual(ansible.utils.parse_json('foo=bar baz=qux'), dict(foo='bar', baz='qux'))
222
-
223 220
         # No closing quotation
224 221
         try:
225
-            ansible.utils.parse_json('foo=bar "')
222
+            rc = ansible.utils.parse_json('foo=bar "')
223
+            print rc
226 224
         except ValueError:
227 225
             pass
228 226
         else:
227
+            traceback.print_exc()
229 228
             raise AssertionError('Incorrect exception, expected ValueError')
230 229
 
231 230
         # Failed to parse
232 231
         try:
233 232
             ansible.utils.parse_json('{')
234
-        except ansible.errors.AnsibleError:
233
+        except ValueError:
235 234
             pass
236 235
         else:
237
-            raise AssertionError('Incorrect exception, expected ansible.errors.AnsibleError')
238
-
239
-        # boolean changed/failed
240
-        self.assertEqual(ansible.utils.parse_json('changed=true'), dict(changed=True))
241
-        self.assertEqual(ansible.utils.parse_json('changed=false'), dict(changed=False))
242
-        self.assertEqual(ansible.utils.parse_json('failed=true'), dict(failed=True))
243
-        self.assertEqual(ansible.utils.parse_json('failed=false'), dict(failed=False))
244
-
245
-        # rc
246
-        self.assertEqual(ansible.utils.parse_json('rc=0'), dict(rc=0))
247
-
248
-        # Just a string
249
-        self.assertEqual(ansible.utils.parse_json('foo'), dict(failed=True, parsed=False, msg='foo'))
236
+            raise AssertionError('Incorrect exception, expected ValueError')
250 237
 
251 238
     def test_parse_yaml(self):
252 239
         #json
... ...
@@ -34,8 +34,11 @@ if options.host is not None:
34 34
     if options.extra:
35 35
         k,v = options.extra.split("=")
36 36
         variables[options.host][k] = v
37
-    print json.dumps(variables[options.host])
37
+    if options.host in variables:
38
+        print json.dumps(variables[options.host])
39
+    else:
40
+        print "{}"
38 41
     sys.exit(0)
39 42
 
40 43
 parser.print_help()
41
-sys.exit(1)
42 44
\ No newline at end of file
45
+sys.exit(1)