subnet.command.sh
· 10 KiB · Bash
Brut
#!/usr/bin/env bash
# subnet.command.sh — manage the subnet map (subnets.json)
#
# Subcommands:
# wgctl subnet list
# wgctl subnet show --name <name>
# wgctl subnet add --name <name> --subnet <cidr> [--type <type>]
# [--tunnel-mode split|full] [--desc <desc>]
# [--group <parent>]
# wgctl subnet rm --name <name>
# wgctl subnet rename --name <old> --new-name <new>
# ============================================
# Lifecycle
# ============================================
function cmd::subnet::on_load() {
flag::register --name
flag::register --subnet
flag::register --type
flag::register --tunnel-mode
flag::register --desc
flag::register --group
flag::register --new-name
command::mixin json_output
}
# ============================================
# Help
# ============================================
function cmd::subnet::help() {
cat <<EOF
Usage: wgctl subnet <subcommand> [options]
Manage the subnet map.
Subcommands:
list List all configured subnets
show --name <name> Show details for a subnet
add --name <name> Add a new subnet entry
--subnet <cidr>
[--type <type>]
[--tunnel-mode split|full]
[--desc <desc>]
[--group <parent>]
rm --name <name> Remove a subnet (refused if in use)
rename --name <old> Rename a subnet (refused if in use)
--new-name <new>
Examples:
wgctl subnet list
wgctl subnet show --name guests
wgctl subnet add --name iot-cctv --subnet 10.1.211.0/24 --type iot
wgctl subnet add --name desktop --subnet 10.1.101.0/24 --group guests
wgctl subnet rm --name iot-cctv
wgctl subnet rename --name iot-cctv --new-name cctv
EOF
}
# ============================================
# Run
# ============================================
function cmd::subnet::run() {
local subcmd="${1:-list}"
shift || true
if command::json; then
cmd::subnet::_output_json
return 0
fi
case "$subcmd" in
list) cmd::subnet::_list "$@" ;;
show) cmd::subnet::_show "$@" ;;
add) cmd::subnet::_add "$@" ;;
rm) cmd::subnet::_rm "$@" ;;
rename) cmd::subnet::_rename "$@" ;;
--help) cmd::subnet::help ;;
*)
log::error "Unknown subcommand '${subcmd}'. Available: list, show, add, rm, rename"
return 1
;;
esac
}
# ============================================
# Subcommands
# ============================================
function cmd::subnet::_list() {
local data
data=$(subnet::list_data | ui::sort_rows 1)
if [[ -z "$data" ]]; then
log::info "No subnets defined."
return 0
fi
if display::is_table "subnet_list"; then
cmd::subnet::_render_table "$data"
return 0
fi
echo ""
local prev_group=""
while IFS='|' read -r display_name subnet type_key tunnel_mode desc is_group group_parent; do
if [[ "$is_group" == "true" ]]; then
# Print group parent header when we encounter first child
if [[ "$group_parent" != "$prev_group" ]]; then
[[ -n "$prev_group" ]] && ui::subnet::group_separator
ui::subnet::row_group_parent "$group_parent"
prev_group="$group_parent"
fi
ui::subnet::row_group_child "$type_key" "$subnet" "$tunnel_mode"
else
# Scalar entry
[[ -n "$prev_group" ]] && ui::subnet::group_separator
prev_group=""
ui::subnet::row_scalar "$display_name" "$subnet" "$tunnel_mode"
fi
done <<< "$data"
echo ""
}
function cmd::subnet::_render_table() {
local data="${1:-}"
[[ -z "$data" ]] && return 0
ui::subnet::list_header_table
while IFS='|' read -r type cidr display_name tunnel desc is_group group_parent; do
[[ -z "$type" ]] && continue
ui::subnet::list_row_table "$type" "$cidr" "$tunnel" "$desc"
done <<< "$data"
printf "\n"
}
function cmd::subnet::_maybe_group_separator() {
local is_group="${1:-}" group_parent="${2:-}" prev_group="${3:-}"
if [[ "$is_group" == "true" && "$group_parent" != "$prev_group" && -n "$prev_group" ]]; then
ui::subnet::group_separator
elif [[ "$is_group" == "false" && -n "$prev_group" ]]; then
ui::subnet::group_separator
fi
}
function cmd::subnet::_update_prev_group() {
local is_group="${1:-}" group_parent="${2:-}" prev_group="${3:-}"
if [[ "$is_group" == "true" ]]; then
echo "$group_parent"
else
echo ""
fi
}
function cmd::subnet::_show() {
local name=""
while [[ $# -gt 0 ]]; do
case "$1" in
--name) name="$2"; shift 2 ;;
--help) cmd::subnet::help; return ;;
*) log::error "Unknown flag: $1"; return 1 ;;
esac
done
[[ -z "$name" ]] && { log::error "Missing required flag: --name"; return 1; }
subnet::require_exists "$name" || return 1
local data
data=$(subnet::show_data "$name")
local is_group="false"
local show_name="" show_subnet="" show_tunnel="" show_desc=""
while IFS='|' read -r key val rest; do
case "$key" in
name) show_name="$val" ;;
is_group) is_group="$val" ;;
subnet) show_subnet="$val" ;;
tunnel_mode) show_tunnel="$val" ;;
desc) show_desc="$val" ;;
esac
done <<< "$data"
if [[ "$is_group" == "true" ]]; then
# Group display
ui::subnet::show_group "$show_name"
while IFS='|' read -r key val rest; do
[[ "$key" != "child" ]] && continue
local c_type="$val"
local c_subnet c_tunnel c_desc
c_subnet=$(echo "$rest" | cut -d'|' -f1)
c_tunnel=$(echo "$rest" | cut -d'|' -f2)
c_desc=$(echo "$rest" | cut -d'|' -f3)
ui::subnet::show_child_row "$c_type" "$c_subnet" "$c_tunnel" "$c_desc"
done <<< "$data"
local peers_using
peers_using=$(subnet::peers_using "$name")
ui::subnet::show_peers_annotated "$peers_using" "$(ctx::subnets)"
else
# Scalar display
ui::subnet::show_scalar "$show_name" "$show_subnet" "$show_tunnel" "$show_desc"
local peers_using
peers_using=$(subnet::peers_using "$name")
ui::subnet::show_peers "$peers_using"
fi
echo ""
}
function cmd::subnet::_add() {
local name="" cidr="" type_key="" tunnel_mode="split" desc="" group_parent=""
while [[ $# -gt 0 ]]; do
case "$1" in
--name) name="$2"; shift 2 ;;
--subnet) cidr="$2"; shift 2 ;;
--type) type_key="$2"; shift 2 ;;
--tunnel-mode) tunnel_mode="$2"; shift 2 ;;
--desc) desc="$2"; shift 2 ;;
--group) group_parent="$2"; shift 2 ;;
--help) cmd::subnet::help; return ;;
*) log::error "Unknown flag: $1"; return 1 ;;
esac
done
[[ -z "$name" ]] && { log::error "Missing required flag: --name"; return 1; }
[[ -z "$cidr" ]] && { log::error "Missing required flag: --subnet"; return 1; }
cmd::subnet::_validate_tunnel_mode "$tunnel_mode" || return 1
cmd::subnet::_validate_cidr "$cidr" || return 1
json::subnet_add "$(ctx::subnets)" "$name" "$cidr" \
"${type_key:-$name}" "$tunnel_mode" "$desc" "$group_parent"
log::ok "Subnet '${name}' added (${cidr})"
}
function cmd::subnet::_rm() {
local name=""
while [[ $# -gt 0 ]]; do
case "$1" in
--name) name="$2"; shift 2 ;;
--help) cmd::subnet::help; return ;;
*) log::error "Unknown flag: $1"; return 1 ;;
esac
done
[[ -z "$name" ]] && { log::error "Missing required flag: --name"; return 1; }
subnet::require_exists "$name" || return 1
local peers_using
peers_using=$(subnet::peers_using "$name")
if [[ -n "$peers_using" ]]; then
log::error "Cannot remove subnet '${name}' — in use by: ${peers_using//,/, }"
log::error "Migrate or remove those peers first."
return 1
fi
json::subnet_remove "$(ctx::subnets)" "$name" ""
log::ok "Subnet '${name}' removed"
}
function cmd::subnet::_rename() {
local name="" new_name=""
while [[ $# -gt 0 ]]; do
case "$1" in
--name) name="$2"; shift 2 ;;
--new-name) new_name="$2"; shift 2 ;;
--help) cmd::subnet::help; return ;;
*) log::error "Unknown flag: $1"; return 1 ;;
esac
done
[[ -z "$name" ]] && { log::error "Missing required flag: --name"; return 1; }
[[ -z "$new_name" ]] && { log::error "Missing required flag: --new-name"; return 1; }
subnet::require_exists "$name" || return 1
local peers_using
peers_using=$(subnet::peers_using "$name")
if [[ -n "$peers_using" ]]; then
log::error "Cannot rename subnet '${name}' — configs already distributed to: ${peers_using//,/, }"
log::error "Client configs reference the subnet CIDR which cannot change after distribution."
return 1
fi
json::subnet_rename "$(ctx::subnets)" "$name" "$new_name" ""
log::ok "Subnet '${name}' renamed to '${new_name}'"
}
# ============================================
# Validation Helpers
# ============================================
function cmd::subnet::_validate_tunnel_mode() {
local mode="${1:-}"
case "$mode" in
split|full) return 0 ;;
*)
log::error "Invalid --tunnel-mode '${mode}'. Use: split, full"
return 1
;;
esac
}
function cmd::subnet::_validate_cidr() {
local cidr="${1:-}"
if ! echo "$cidr" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/[0-9]+$'; then
log::error "Invalid CIDR format: '${cidr}'"
return 1
fi
}
function cmd::subnet::_output_json() {
local data
data=$(subnet::list_data 2>/dev/null)
local -a subnets=()
while IFS='|' read -r type cidr display_name tunnel_mode desc is_group group_parent; do
[[ -z "$type" ]] && continue
local is_group_json="false"
[[ "$is_group" == "true" ]] && is_group_json="true"
subnets+=("$(printf '{"type":"%s","cidr":"%s","display_name":"%s","tunnel_mode":"%s","desc":"%s","is_group":%s,"group_parent":"%s"}' \
"$type" "$cidr" "$display_name" "$tunnel_mode" \
"$desc" "$is_group_json" "${group_parent:-}")")
done <<< "$data"
local count=${#subnets[@]}
local array
array=$(printf '%s\n' "${subnets[@]:-}" | paste -sd ',' -)
printf '{"subnets":[%s]}' "${array:-}" | json::envelope "subnet list" "$count"
}
| 1 | #!/usr/bin/env bash |
| 2 | # subnet.command.sh — manage the subnet map (subnets.json) |
| 3 | # |
| 4 | # Subcommands: |
| 5 | # wgctl subnet list |
| 6 | # wgctl subnet show --name <name> |
| 7 | # wgctl subnet add --name <name> --subnet <cidr> [--type <type>] |
| 8 | # [--tunnel-mode split|full] [--desc <desc>] |
| 9 | # [--group <parent>] |
| 10 | # wgctl subnet rm --name <name> |
| 11 | # wgctl subnet rename --name <old> --new-name <new> |
| 12 | |
| 13 | # ============================================ |
| 14 | # Lifecycle |
| 15 | # ============================================ |
| 16 | |
| 17 | function cmd::subnet::on_load() { |
| 18 | flag::register --name |
| 19 | flag::register --subnet |
| 20 | flag::register --type |
| 21 | flag::register --tunnel-mode |
| 22 | flag::register --desc |
| 23 | flag::register --group |
| 24 | flag::register --new-name |
| 25 | |
| 26 | command::mixin json_output |
| 27 | } |
| 28 | |
| 29 | # ============================================ |
| 30 | # Help |
| 31 | # ============================================ |
| 32 | |
| 33 | function cmd::subnet::help() { |
| 34 | cat <<EOF |
| 35 | Usage: wgctl subnet <subcommand> [options] |
| 36 | |
| 37 | Manage the subnet map. |
| 38 | |
| 39 | Subcommands: |
| 40 | list List all configured subnets |
| 41 | show --name <name> Show details for a subnet |
| 42 | add --name <name> Add a new subnet entry |
| 43 | --subnet <cidr> |
| 44 | [--type <type>] |
| 45 | [--tunnel-mode split|full] |
| 46 | [--desc <desc>] |
| 47 | [--group <parent>] |
| 48 | rm --name <name> Remove a subnet (refused if in use) |
| 49 | rename --name <old> Rename a subnet (refused if in use) |
| 50 | --new-name <new> |
| 51 | |
| 52 | Examples: |
| 53 | wgctl subnet list |
| 54 | wgctl subnet show --name guests |
| 55 | wgctl subnet add --name iot-cctv --subnet 10.1.211.0/24 --type iot |
| 56 | wgctl subnet add --name desktop --subnet 10.1.101.0/24 --group guests |
| 57 | wgctl subnet rm --name iot-cctv |
| 58 | wgctl subnet rename --name iot-cctv --new-name cctv |
| 59 | EOF |
| 60 | } |
| 61 | |
| 62 | # ============================================ |
| 63 | # Run |
| 64 | # ============================================ |
| 65 | |
| 66 | function cmd::subnet::run() { |
| 67 | local subcmd="${1:-list}" |
| 68 | shift || true |
| 69 | |
| 70 | if command::json; then |
| 71 | cmd::subnet::_output_json |
| 72 | return 0 |
| 73 | fi |
| 74 | |
| 75 | case "$subcmd" in |
| 76 | list) cmd::subnet::_list "$@" ;; |
| 77 | show) cmd::subnet::_show "$@" ;; |
| 78 | add) cmd::subnet::_add "$@" ;; |
| 79 | rm) cmd::subnet::_rm "$@" ;; |
| 80 | rename) cmd::subnet::_rename "$@" ;; |
| 81 | --help) cmd::subnet::help ;; |
| 82 | *) |
| 83 | log::error "Unknown subcommand '${subcmd}'. Available: list, show, add, rm, rename" |
| 84 | return 1 |
| 85 | ;; |
| 86 | esac |
| 87 | } |
| 88 | |
| 89 | # ============================================ |
| 90 | # Subcommands |
| 91 | # ============================================ |
| 92 | |
| 93 | function cmd::subnet::_list() { |
| 94 | local data |
| 95 | data=$(subnet::list_data | ui::sort_rows 1) |
| 96 | |
| 97 | if [[ -z "$data" ]]; then |
| 98 | log::info "No subnets defined." |
| 99 | return 0 |
| 100 | fi |
| 101 | |
| 102 | if display::is_table "subnet_list"; then |
| 103 | cmd::subnet::_render_table "$data" |
| 104 | return 0 |
| 105 | fi |
| 106 | |
| 107 | echo "" |
| 108 | local prev_group="" |
| 109 | while IFS='|' read -r display_name subnet type_key tunnel_mode desc is_group group_parent; do |
| 110 | if [[ "$is_group" == "true" ]]; then |
| 111 | # Print group parent header when we encounter first child |
| 112 | if [[ "$group_parent" != "$prev_group" ]]; then |
| 113 | [[ -n "$prev_group" ]] && ui::subnet::group_separator |
| 114 | ui::subnet::row_group_parent "$group_parent" |
| 115 | prev_group="$group_parent" |
| 116 | fi |
| 117 | ui::subnet::row_group_child "$type_key" "$subnet" "$tunnel_mode" |
| 118 | else |
| 119 | # Scalar entry |
| 120 | [[ -n "$prev_group" ]] && ui::subnet::group_separator |
| 121 | prev_group="" |
| 122 | ui::subnet::row_scalar "$display_name" "$subnet" "$tunnel_mode" |
| 123 | fi |
| 124 | done <<< "$data" |
| 125 | echo "" |
| 126 | } |
| 127 | |
| 128 | function cmd::subnet::_render_table() { |
| 129 | local data="${1:-}" |
| 130 | [[ -z "$data" ]] && return 0 |
| 131 | |
| 132 | ui::subnet::list_header_table |
| 133 | while IFS='|' read -r type cidr display_name tunnel desc is_group group_parent; do |
| 134 | [[ -z "$type" ]] && continue |
| 135 | ui::subnet::list_row_table "$type" "$cidr" "$tunnel" "$desc" |
| 136 | done <<< "$data" |
| 137 | printf "\n" |
| 138 | } |
| 139 | |
| 140 | function cmd::subnet::_maybe_group_separator() { |
| 141 | local is_group="${1:-}" group_parent="${2:-}" prev_group="${3:-}" |
| 142 | if [[ "$is_group" == "true" && "$group_parent" != "$prev_group" && -n "$prev_group" ]]; then |
| 143 | ui::subnet::group_separator |
| 144 | elif [[ "$is_group" == "false" && -n "$prev_group" ]]; then |
| 145 | ui::subnet::group_separator |
| 146 | fi |
| 147 | } |
| 148 | |
| 149 | function cmd::subnet::_update_prev_group() { |
| 150 | local is_group="${1:-}" group_parent="${2:-}" prev_group="${3:-}" |
| 151 | if [[ "$is_group" == "true" ]]; then |
| 152 | echo "$group_parent" |
| 153 | else |
| 154 | echo "" |
| 155 | fi |
| 156 | } |
| 157 | |
| 158 | function cmd::subnet::_show() { |
| 159 | local name="" |
| 160 | while [[ $# -gt 0 ]]; do |
| 161 | case "$1" in |
| 162 | --name) name="$2"; shift 2 ;; |
| 163 | --help) cmd::subnet::help; return ;; |
| 164 | *) log::error "Unknown flag: $1"; return 1 ;; |
| 165 | esac |
| 166 | done |
| 167 | |
| 168 | [[ -z "$name" ]] && { log::error "Missing required flag: --name"; return 1; } |
| 169 | subnet::require_exists "$name" || return 1 |
| 170 | |
| 171 | local data |
| 172 | data=$(subnet::show_data "$name") |
| 173 | |
| 174 | local is_group="false" |
| 175 | local show_name="" show_subnet="" show_tunnel="" show_desc="" |
| 176 | |
| 177 | while IFS='|' read -r key val rest; do |
| 178 | case "$key" in |
| 179 | name) show_name="$val" ;; |
| 180 | is_group) is_group="$val" ;; |
| 181 | subnet) show_subnet="$val" ;; |
| 182 | tunnel_mode) show_tunnel="$val" ;; |
| 183 | desc) show_desc="$val" ;; |
| 184 | esac |
| 185 | done <<< "$data" |
| 186 | |
| 187 | if [[ "$is_group" == "true" ]]; then |
| 188 | # Group display |
| 189 | ui::subnet::show_group "$show_name" |
| 190 | |
| 191 | while IFS='|' read -r key val rest; do |
| 192 | [[ "$key" != "child" ]] && continue |
| 193 | local c_type="$val" |
| 194 | local c_subnet c_tunnel c_desc |
| 195 | c_subnet=$(echo "$rest" | cut -d'|' -f1) |
| 196 | c_tunnel=$(echo "$rest" | cut -d'|' -f2) |
| 197 | c_desc=$(echo "$rest" | cut -d'|' -f3) |
| 198 | ui::subnet::show_child_row "$c_type" "$c_subnet" "$c_tunnel" "$c_desc" |
| 199 | done <<< "$data" |
| 200 | |
| 201 | local peers_using |
| 202 | peers_using=$(subnet::peers_using "$name") |
| 203 | ui::subnet::show_peers_annotated "$peers_using" "$(ctx::subnets)" |
| 204 | else |
| 205 | # Scalar display |
| 206 | ui::subnet::show_scalar "$show_name" "$show_subnet" "$show_tunnel" "$show_desc" |
| 207 | |
| 208 | local peers_using |
| 209 | peers_using=$(subnet::peers_using "$name") |
| 210 | ui::subnet::show_peers "$peers_using" |
| 211 | fi |
| 212 | |
| 213 | echo "" |
| 214 | } |
| 215 | |
| 216 | function cmd::subnet::_add() { |
| 217 | local name="" cidr="" type_key="" tunnel_mode="split" desc="" group_parent="" |
| 218 | |
| 219 | while [[ $# -gt 0 ]]; do |
| 220 | case "$1" in |
| 221 | --name) name="$2"; shift 2 ;; |
| 222 | --subnet) cidr="$2"; shift 2 ;; |
| 223 | --type) type_key="$2"; shift 2 ;; |
| 224 | --tunnel-mode) tunnel_mode="$2"; shift 2 ;; |
| 225 | --desc) desc="$2"; shift 2 ;; |
| 226 | --group) group_parent="$2"; shift 2 ;; |
| 227 | --help) cmd::subnet::help; return ;; |
| 228 | *) log::error "Unknown flag: $1"; return 1 ;; |
| 229 | esac |
| 230 | done |
| 231 | |
| 232 | [[ -z "$name" ]] && { log::error "Missing required flag: --name"; return 1; } |
| 233 | [[ -z "$cidr" ]] && { log::error "Missing required flag: --subnet"; return 1; } |
| 234 | |
| 235 | cmd::subnet::_validate_tunnel_mode "$tunnel_mode" || return 1 |
| 236 | cmd::subnet::_validate_cidr "$cidr" || return 1 |
| 237 | |
| 238 | json::subnet_add "$(ctx::subnets)" "$name" "$cidr" \ |
| 239 | "${type_key:-$name}" "$tunnel_mode" "$desc" "$group_parent" |
| 240 | |
| 241 | log::ok "Subnet '${name}' added (${cidr})" |
| 242 | } |
| 243 | |
| 244 | function cmd::subnet::_rm() { |
| 245 | local name="" |
| 246 | while [[ $# -gt 0 ]]; do |
| 247 | case "$1" in |
| 248 | --name) name="$2"; shift 2 ;; |
| 249 | --help) cmd::subnet::help; return ;; |
| 250 | *) log::error "Unknown flag: $1"; return 1 ;; |
| 251 | esac |
| 252 | done |
| 253 | |
| 254 | [[ -z "$name" ]] && { log::error "Missing required flag: --name"; return 1; } |
| 255 | subnet::require_exists "$name" || return 1 |
| 256 | |
| 257 | local peers_using |
| 258 | peers_using=$(subnet::peers_using "$name") |
| 259 | |
| 260 | if [[ -n "$peers_using" ]]; then |
| 261 | log::error "Cannot remove subnet '${name}' — in use by: ${peers_using//,/, }" |
| 262 | log::error "Migrate or remove those peers first." |
| 263 | return 1 |
| 264 | fi |
| 265 | |
| 266 | json::subnet_remove "$(ctx::subnets)" "$name" "" |
| 267 | log::ok "Subnet '${name}' removed" |
| 268 | } |
| 269 | |
| 270 | function cmd::subnet::_rename() { |
| 271 | local name="" new_name="" |
| 272 | while [[ $# -gt 0 ]]; do |
| 273 | case "$1" in |
| 274 | --name) name="$2"; shift 2 ;; |
| 275 | --new-name) new_name="$2"; shift 2 ;; |
| 276 | --help) cmd::subnet::help; return ;; |
| 277 | *) log::error "Unknown flag: $1"; return 1 ;; |
| 278 | esac |
| 279 | done |
| 280 | |
| 281 | [[ -z "$name" ]] && { log::error "Missing required flag: --name"; return 1; } |
| 282 | [[ -z "$new_name" ]] && { log::error "Missing required flag: --new-name"; return 1; } |
| 283 | subnet::require_exists "$name" || return 1 |
| 284 | |
| 285 | local peers_using |
| 286 | peers_using=$(subnet::peers_using "$name") |
| 287 | if [[ -n "$peers_using" ]]; then |
| 288 | log::error "Cannot rename subnet '${name}' — configs already distributed to: ${peers_using//,/, }" |
| 289 | log::error "Client configs reference the subnet CIDR which cannot change after distribution." |
| 290 | return 1 |
| 291 | fi |
| 292 | |
| 293 | json::subnet_rename "$(ctx::subnets)" "$name" "$new_name" "" |
| 294 | log::ok "Subnet '${name}' renamed to '${new_name}'" |
| 295 | } |
| 296 | |
| 297 | # ============================================ |
| 298 | # Validation Helpers |
| 299 | # ============================================ |
| 300 | |
| 301 | function cmd::subnet::_validate_tunnel_mode() { |
| 302 | local mode="${1:-}" |
| 303 | case "$mode" in |
| 304 | split|full) return 0 ;; |
| 305 | *) |
| 306 | log::error "Invalid --tunnel-mode '${mode}'. Use: split, full" |
| 307 | return 1 |
| 308 | ;; |
| 309 | esac |
| 310 | } |
| 311 | |
| 312 | function cmd::subnet::_validate_cidr() { |
| 313 | local cidr="${1:-}" |
| 314 | if ! echo "$cidr" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/[0-9]+$'; then |
| 315 | log::error "Invalid CIDR format: '${cidr}'" |
| 316 | return 1 |
| 317 | fi |
| 318 | } |
| 319 | |
| 320 | function cmd::subnet::_output_json() { |
| 321 | local data |
| 322 | data=$(subnet::list_data 2>/dev/null) |
| 323 | |
| 324 | local -a subnets=() |
| 325 | while IFS='|' read -r type cidr display_name tunnel_mode desc is_group group_parent; do |
| 326 | [[ -z "$type" ]] && continue |
| 327 | |
| 328 | local is_group_json="false" |
| 329 | [[ "$is_group" == "true" ]] && is_group_json="true" |
| 330 | |
| 331 | subnets+=("$(printf '{"type":"%s","cidr":"%s","display_name":"%s","tunnel_mode":"%s","desc":"%s","is_group":%s,"group_parent":"%s"}' \ |
| 332 | "$type" "$cidr" "$display_name" "$tunnel_mode" \ |
| 333 | "$desc" "$is_group_json" "${group_parent:-}")") |
| 334 | done <<< "$data" |
| 335 | |
| 336 | local count=${#subnets[@]} |
| 337 | local array |
| 338 | array=$(printf '%s\n' "${subnets[@]:-}" | paste -sd ',' -) |
| 339 | printf '{"subnets":[%s]}' "${array:-}" | json::envelope "subnet list" "$count" |
| 340 | } |