refurbish network and rest api from dowse development

This commit is contained in:
Jaromil 2016-06-08 12:09:24 +02:00
parent 9df91767f8
commit b7a0dca0ec
1 changed files with 214 additions and 79 deletions

293
zuper
View File

@ -33,6 +33,9 @@ arrs=(req freq)
vars+=(zuper_version)
zuper_version=0.2
zmodload zsh/system
zmodload zsh/net/tcp
# {{{ Messaging
# Messaging function with pretty coloring
@ -209,7 +212,6 @@ catch() { function TRAPZERR() { } }
# Endgame handling
arrs+=(destruens)
destruens=()
# Trap functions for the endgame event
TRAPINT() { endgame INT; return $? }
@ -292,21 +294,133 @@ strtok() {
for c in {1..${#_string}}; do
if [[ "${_string[(e)$c]}" == "$_delim" ]]; then
# check if not empty
t=${_string[(e)$(($f + 1)),$(($c - 1))]}
[[ "$t" == "" ]] || tok+=($t)
t="${_string[(e)$(($f + 1)),$(($c - 1))]}"
if [[ "$t" == "" ]]; then
tok+=("null")
else
tok+=("$t")
fi
# save last found
f=$c
fi
done
# add last token
t=${_string[(e)$(($f + 1)),$c]}
[[ "$t" == "" ]] || tok+=($t)
if [[ "$t" == "" ]]; then
tok+=("null")
else
tok+=("$t")
fi
}
# TODO: move in here some helpers
# }}} Strings
# {{{ Networking
# This is only tested on GNU/Linux and makes use of sysfs
# index of all network devices
arrs+=(net_devices)
# map of ipv4 assigned addresses: [dev addr]
maps+=(net_ip4_addr)
# map of ipv6 assigned addresses: [dev addr]
maps+=(net_ip6_addr)
# map of dhcp served ipv4
maps+=(ip4dhcps)
# map of dhcp served ipv6
maps+=(ip6dhcps)
# map of external ipv4 addresses
maps+=(net_ip4_exit)
# map of internal ipv6 addresses
# maps+=(ip6exits)
net.scan_devices() {
for i in "${(f)$(find /sys/devices/ -name net)}"; do
dev=`ls --indicator-style=none $i`
# skip the loopback device
[[ "$dev" =~ "^lo" ]] && continue
func "$dev"
net_devices+=($dev)
done
# return error if no device found
if [[ ${#net_devices} = 0 ]]; then return 1
else return 0; fi
}
net.scan_addresses() {
[[ ${#net_devices} = 0 ]] && {
error "No network device found."
func "Have you ran net.scan_devices() first?"
return 1
}
for dev in ${net_devices}; do
# check ipv4 connections
conn=`ip addr show $dev | awk '/inet / {print $2}'`
[[ "$conn" = "" ]] || {
net_ip4_addr+=($dev $conn) }
# check ipv6 connections
conn=`ip addr show $dev | awk '/inet6/ {print $2}'`
[[ "$conn" = "" ]] || {
net_ip6_addr+=($dev $conn) }
done
# list ipv4
notice "${#net_ip4_addr} ipv4 connected devices found"
for c in ${(k)net_ip4_addr}; do
act " $c ${net_ip4_addr[$c]}"
done
# list ipv6
notice "${#net_ip6_addr} ipv6 connected devices found"
for c in ${(k)net_ip6_addr}; do
act " $c ${net_ip6_addr[$c]}"
done
# find out network addresses
return 0
}
net.scan_exits() {
# just ipv4 for now, also we use curl to drive the call over the
# specific interface, but if that wouldn't matter then rest.get is
# better to avoid this dependency
for dev in ${(k)net_ip4_addr}; do
addr=`curl --silent --interface $dev https://api.ipify.org`
if [[ "$?" != "0" ]]; then
error "curl returns $?: $addr"
else
[[ "$addr" = "" ]] || {
notice "$dev external ip: $addr"
net_ip4_exit+=($dev $addr)
}
fi
done
for dev in ${(k)net_ip6_addr}; do
addr=`curl --silent --ipv6 --interface $dev https://api.ipify.org`
if [[ $? != 0 ]]; then
error "curl returns $?: $addr"
else
[[ "$addr" = "" ]] || {
notice "$dev external ip: $addr"
net_ip4_exit+=($dev $addr)
}
fi
done
}
# }}} Networking
# {{{ Key/Value filesave
# optional: define zkv=1 on source
@ -316,7 +430,6 @@ strtok() {
##########################
# Key/Value file storage using ZSh associative maps
zmodload zsh/system
# load a map from a file
# map must be already instantiated with typeset -A by called
@ -382,44 +495,40 @@ EOF
# {{{ Get/Set REST API
# optional: define restful=1 on source
########
# Restful API client
# there is a clear zsh optimization here in get/set kv
# using zsh/tcp instead of spawning curl
# and perhaps querying with one call using ?recursive
[[ "$restful" = "" ]] || {
vars+=(rest_reply_body rest_reply_header)
maps+=(rest_header)
########
# Restful API client
# there is a clear zsh optimization here in get/set kv
# using zsh/tcp instead of spawning curl
# and perhaps querying with one call using ?recursive
function rest.put() {
fn "rest.put $*"
zmodload zsh/net/tcp
# $1 = hostname
# $2 = port
# $3 = path
# value from stdin |
# to check if the http service is running is up to the caller
function restful.put() {
fn "restful.put $*"
_host=${1} # ip address
_port=${2}
_path=${3}
sysread _v
# $1 = hostname
# $2 = port
# $3 = path
# value from stdin |
req=(_host)
ckreq || return $?
# to check if the http service is running is up to the caller
if ztcp $_host $_port; then
_host=${1} # ip address
_port=${2}
_path=${3}
sysread _v
# TODO: work out various parsers, this one works with consul.io
req=(_host)
ckreq || return $?
if ztcp $_host $_port; then
# TODO: work out various parsers, this one works with consul.io
_fd=$REPLY
# func "tcp open on fd $fd"
cat <<EOF >& $_fd
_fd=$REPLY
# func "tcp open on fd $fd"
cat <<EOF >& $_fd
PUT ${_path} HTTP/1.1
User-Agent: Zuper/$zuper_version
Host: ${_host}:${_port}
@ -429,72 +538,98 @@ Content-Type: application/x-www-form-urlencoded
EOF
print -n "$_v" >& $_fd
print -n "$_v" >& $_fd
sysread -i $_fd _res
sysread -i $_fd _res
# close connection
ztcp -c $_fd
# close connection
ztcp -c $_fd
[[ "$_res" =~ "true" ]] || {
warn "failed PUT on restful key/value"
warn "host: ${_host}"
warn "port: ${_port}"
warn "path: ${_path}"
warn "value: $_v"
print - "$_res"
zerr
return 1
}
else
error "cannot connect to restful service: $_host:$_port"
zerr
return 1
fi
return 0
}
function restful.get() {
fn "restful.get $*"
_host=${1}
_port=${2}
_path=${3}
req=(_host _port)
ckreq || return $?
ztcp $_host $_port || {
[[ "$_res" =~ "true" ]] || {
warn "failed PUT on restful key/value"
warn "host: ${_host}"
warn "port: ${_port}"
warn "path: ${_path}"
warn "value: $_v"
print - "$_res"
zerr
return 1
}
_fd=$REPLY
else
error "cannot connect to restful service: $_host:$_port"
zerr
return 1
fi
# TODO: work out various parsers, this one works with consul.io
return 0
cat <<EOF >& $_fd
}
function rest.get() {
fn "rest.get $*"
_host=${1}
_port=${2}
_path=${3}
req=(_host _port)
ckreq || return $?
ztcp $_host $_port || {
zerr
return 1
}
_fd=$REPLY
# TODO: work out various parsers, this one works with consul.io
cat <<EOF >& $_fd
GET ${_path} HTTP/1.1
User-Agent: Zuper/$zuper_version
Host: $_host:$_port
Accept: */*
EOF
sysread -i $_fd -o 1 | awk -F: '
/"Value":/ { gsub(/"|}]/,"",$7) ; print $7 }' | base64 -d
# close connection
ztcp -c $_fd
# read header response
rest_reply=`sysread -i $_fd -o 1`
return 0
for i in "${(f)rest_reply}"; do
print $i | hexdump -C
# first line is the response code
}
[[ "$i" -regex-match "\x0d\x0a$" ]] && {
func BLANK
break }
# # save other lines in map for fast retrieval
# _field=${i[(ws@:@)1]}
# func "$_field - header field parsed"
# rest_header[$_field]="${i[(ws@:@)2]}"
# c=$(( $c + 1 ))
done
# rest_reply_header="${(f)$(cat <&$_fd)}"
func "${#rest_reply_header} bytes response header stored in rest_reply_header"
# | awk -F: '
#/"Value":/ { gsub(/"|}]/,"",$7) ; print $7 }' | base64 -d
# TODO: read content-length and use it here
rest_reply_body="${(f)$(sysread -i $_fd -o 1)}"
func "${#rest_reply_body} bytes response body stored in rest_reply_body"
# close connection
ztcp -c $_fd
return 0
}
# }}} Get/Set REST API
# {{{ Helpers