1#!/bin/ksh -p
2#
3# CDDL HEADER START
4#
5# This file and its contents are supplied under the terms of the
6# Common Development and Distribution License ("CDDL"), version 1.0.
7# You may only use this file in accordance with the terms of version
8# 1.0 of the CDDL.
9#
10# A full copy of the text of the CDDL should have accompanied this
11# source.  A copy is of the CDDL is also available via the Internet
12# at http://www.illumos.org/license/CDDL.
13#
14# CDDL HEADER END
15#
16
17#
18# Copyright (c) 2018 Datto Inc.
19#
20
21. $STF_SUITE/include/libtest.shlib
22
23#
24# DESCRIPTION:
25#
26# STRATEGY:
27#	1. Ensure empty JSON is printed when there is no channel program output
28#	2. Compare JSON output formatting for a channel program to template
29#	3. Using bad command line option (-Z) gives correct error output
30#
31
32verify_runnable "both"
33
34function cleanup
35{
36	log_must zfs destroy -r $TESTDS
37	return 0
38}
39log_onexit cleanup
40
41log_assert "Channel programs output valid JSON"
42
43TESTDS="$TESTPOOL/zcp-json"
44TESTSNAP="$TESTDS@snap0"
45log_must zfs create $TESTDS
46
47# 1. Ensure empty JSON is printed when there is no channel program output
48TESTZCP="/$TESTDS/zfs_destroy.zcp"
49cat > "$TESTZCP" << EOF
50       args = ...
51       argv = args["argv"]
52       zfs.sync.destroy(argv[1])
53EOF
54
55EMPTY_OUTPUT=("{}")
56log_must zfs snap $TESTSNAP 2>&1
57log_must zfs list $TESTSNAP 2>&1
58log_must zfs program $TESTPOOL $TESTZCP $TESTSNAP 2>&1
59log_mustnot zfs list $TESTSNAP 2>&1
60log_must zfs snap $TESTSNAP 2>&1
61log_must zfs list $TESTSNAP 2>&1
62log_must zfs program -j $TESTPOOL $TESTZCP $TESTSNAP 2>&1
63log_mustnot zfs list $TESTSNAP 2>&1
64log_must zfs snap $TESTSNAP 2>&1
65log_must zfs list $TESTSNAP 2>&1
66OUTPUT=$(zfs program -j $TESTPOOL $TESTZCP $TESTSNAP 2>&1)
67if [ "$OUTPUT" != "$EMPTY_OUTPUT" ]; then
68       log_note "Got     :$OUTPUT"
69       log_note "Expected:$EMPTY_OUTPUT"
70       log_fail "Channel program output not empty";
71fi
72log_mustnot zfs list $TESTSNAP 2>&1
73
74# 2. Compare JSON output formatting for a channel program to template
75TESTZCP="/$TESTDS/zfs_rlist.zcp"
76cat > "$TESTZCP" << EOF
77	succeeded = {}
78	failed = {}
79
80	function list_recursive(root, prop)
81		for child in zfs.list.children(root) do
82			list_recursive(child, prop)
83		end
84		val, src  = zfs.get_prop(root, prop)
85		if (val == nil) then
86			failed[root] = val
87		else
88			succeeded[root] = val
89		end
90	end
91
92	args = ...
93
94	argv = args["argv"]
95
96	list_recursive(argv[1], argv[2])
97
98	results = {}
99	results["succeeded"] = succeeded
100	results["failed"] = failed
101	return results
102EOF
103
104typeset -a pos_cmds=("recordsize" "type")
105typeset -a pos_cmds_out=(
106"{
107    \"return\": {
108        \"failed\": {},
109        \"succeeded\": {
110            \"$TESTDS\": 131072
111        }
112    }
113}"
114"{
115    \"return\": {
116        \"failed\": {},
117        \"succeeded\": {
118            \"$TESTDS\": \"filesystem\"
119        }
120    }
121}")
122
123#
124# N.B. json.tool is needed to guarantee consistent ordering of fields,
125# sed is needed to trim trailing space in CentOS 6's json.tool output
126#
127# As of Python 3.5 the behavior of json.tool changed to keep the order
128# the same as the input and the --sort-keys option was added.  Detect when
129# --sort-keys is supported and apply the option to ensure the expected order.
130#
131if python -m json.tool --sort-keys <<< "{}"; then
132	JSON_TOOL_CMD="python -m json.tool --sort-keys"
133else
134	JSON_TOOL_CMD="python -m json.tool"
135fi
136
137typeset -i cnt=0
138typeset cmd
139for cmd in ${pos_cmds[@]}; do
140	log_must zfs program $TESTPOOL $TESTZCP $TESTDS $cmd 2>&1
141	log_must zfs program -j $TESTPOOL $TESTZCP $TESTDS $cmd 2>&1
142	OUTPUT=$(zfs program -j $TESTPOOL $TESTZCP $TESTDS $cmd 2>&1 |
143	    $JSON_TOOL_CMD | sed 's/[[:space:]]*$//')
144	if [ "$OUTPUT" != "${pos_cmds_out[$cnt]}" ]; then
145		log_note "Got     :$OUTPUT"
146		log_note "Expected:${pos_cmds_out[$cnt]}"
147		log_fail "Unexpected channel program output";
148	fi
149	cnt=$((cnt + 1))
150done
151
152# 3. Using bad command line option (-Z) gives correct error output
153typeset -a neg_cmds=("-Z")
154typeset -a neg_cmds_out=(
155"invalid option 'Z'
156usage:
157	program [-jn] [-t <instruction limit>] [-m <memory limit (b)>] <pool> <program file> [lua args...]
158
159For the property list, run: zfs set|get
160
161For the delegated permission list, run: zfs allow|unallow")
162cnt=0
163for cmd in ${neg_cmds[@]}; do
164	log_mustnot zfs program $cmd $TESTPOOL $TESTZCP $TESTDS 2>&1
165	log_mustnot zfs program -j $cmd $TESTPOOL $TESTZCP $TESTDS 2>&1
166	OUTPUT=$(zfs program -j $cmd $TESTPOOL $TESTZCP $TESTDS 2>&1)
167	if [ "$OUTPUT" != "${neg_cmds_out[$cnt]}" ]; then
168		log_note "Got     :$OUTPUT"
169		log_note "Expected:${neg_cmds_out[$cnt]}"
170		log_fail "Unexpected channel program error output";
171	fi
172	cnt=$((cnt + 1))
173done
174
175log_pass "Channel programs output valid JSON"
176