... | ... |
@@ -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) |