Browse Source

refurbish network and rest api from dowse development

Jaromil 3 years ago
parent
commit
b7a0dca0ec
1 changed files with 209 additions and 74 deletions
  1. 209 74
      zuper

+ 209 - 74
zuper

@@ -33,6 +33,9 @@ arrs=(req freq)
33 33
 vars+=(zuper_version)
34 34
 zuper_version=0.2
35 35
 
36
+zmodload zsh/system
37
+zmodload zsh/net/tcp
38
+
36 39
 # {{{ Messaging
37 40
 
38 41
 # Messaging function with pretty coloring
@@ -209,7 +212,6 @@ catch() { function TRAPZERR() { } }
209 212
 # Endgame handling
210 213
 
211 214
 arrs+=(destruens)
212
-destruens=()
213 215
 
214 216
 # Trap functions for the endgame event
215 217
 TRAPINT()  { endgame INT;   return $? }
@@ -292,21 +294,133 @@ strtok() {
292 294
     for c in {1..${#_string}}; do
293 295
         if [[ "${_string[(e)$c]}" == "$_delim" ]]; then
294 296
             # check if not empty
295
-            t=${_string[(e)$(($f + 1)),$(($c - 1))]}
296
-            [[ "$t" == "" ]] || tok+=($t)
297
+            t="${_string[(e)$(($f + 1)),$(($c - 1))]}"
298
+            if [[ "$t" == "" ]]; then
299
+                tok+=("null")
300
+            else
301
+                tok+=("$t")
302
+            fi
297 303
             # save last found
298 304
             f=$c
299 305
         fi
300 306
     done
301 307
     # add last token
302 308
     t=${_string[(e)$(($f + 1)),$c]}
303
-    [[ "$t" == "" ]] || tok+=($t)
309
+    if [[ "$t" == "" ]]; then
310
+        tok+=("null")
311
+    else
312
+        tok+=("$t")
313
+    fi
304 314
 }
305 315
 
306 316
 # TODO: move in here some helpers
307 317
 
308 318
 # }}} Strings
309 319
 
320
+# {{{ Networking
321
+
322
+# This is only tested on GNU/Linux and makes use of sysfs
323
+
324
+# index of all network devices
325
+arrs+=(net_devices)
326
+
327
+# map of ipv4 assigned addresses: [dev addr]
328
+maps+=(net_ip4_addr)
329
+# map of ipv6 assigned addresses: [dev addr]
330
+maps+=(net_ip6_addr)
331
+
332
+# map of dhcp served ipv4
333
+maps+=(ip4dhcps)
334
+# map of dhcp served ipv6
335
+maps+=(ip6dhcps)
336
+
337
+# map of external ipv4 addresses
338
+maps+=(net_ip4_exit)
339
+# map of internal ipv6 addresses
340
+# maps+=(ip6exits)
341
+
342
+net.scan_devices() {
343
+    for i in "${(f)$(find /sys/devices/ -name net)}"; do
344
+        dev=`ls --indicator-style=none $i`
345
+
346
+        # skip the loopback device
347
+        [[ "$dev" =~ "^lo" ]] && continue
348
+        func "$dev"
349
+        net_devices+=($dev)
350
+    done
351
+
352
+    # return error if no device found
353
+    if [[ ${#net_devices} = 0 ]]; then return 1
354
+    else return 0; fi
355
+}
356
+
357
+net.scan_addresses() {
358
+    [[ ${#net_devices} = 0 ]] && {
359
+        error "No network device found."
360
+        func "Have you ran net.scan_devices() first?"
361
+        return 1
362
+    }
363
+
364
+    for dev in ${net_devices}; do
365
+        # check ipv4 connections
366
+        conn=`ip addr show $dev | awk '/inet / {print $2}'`
367
+        [[ "$conn" = "" ]] || {
368
+            net_ip4_addr+=($dev $conn) }
369
+        # check ipv6 connections
370
+        conn=`ip addr show $dev | awk '/inet6/ {print $2}'`
371
+        [[ "$conn" = "" ]] || {
372
+            net_ip6_addr+=($dev $conn) }
373
+    done
374
+
375
+    # list ipv4
376
+    notice "${#net_ip4_addr} ipv4 connected devices found"
377
+    for c in ${(k)net_ip4_addr}; do
378
+        act " $c ${net_ip4_addr[$c]}"
379
+    done
380
+
381
+    # list ipv6
382
+    notice "${#net_ip6_addr} ipv6 connected devices found"
383
+    for c in ${(k)net_ip6_addr}; do
384
+        act " $c ${net_ip6_addr[$c]}"
385
+    done
386
+    # find out network addresses
387
+
388
+    return 0
389
+}
390
+
391
+net.scan_exits() {
392
+    # just ipv4 for now, also we use curl to drive the call over the
393
+    # specific interface, but if that wouldn't matter then rest.get is
394
+    # better to avoid this dependency
395
+
396
+    for dev in ${(k)net_ip4_addr}; do
397
+        addr=`curl --silent --interface $dev https://api.ipify.org`
398
+        if [[ "$?" != "0" ]]; then
399
+            error "curl returns $?: $addr"
400
+        else
401
+            [[ "$addr" = "" ]] || {
402
+                notice "$dev external ip: $addr"
403
+                net_ip4_exit+=($dev $addr)
404
+            }
405
+        fi
406
+    done
407
+
408
+    for dev in ${(k)net_ip6_addr}; do
409
+        addr=`curl --silent --ipv6 --interface $dev https://api.ipify.org`
410
+        if [[ $? != 0 ]]; then
411
+            error "curl returns $?: $addr"
412
+        else
413
+            [[ "$addr" = "" ]] || {
414
+                notice "$dev external ip: $addr"
415
+                net_ip4_exit+=($dev $addr)
416
+            }
417
+        fi
418
+    done
419
+
420
+}
421
+
422
+# }}} Networking
423
+
310 424
 # {{{ Key/Value filesave
311 425
 
312 426
 # optional: define zkv=1 on source
@@ -316,7 +430,6 @@ strtok() {
316 430
     ##########################
317 431
     # Key/Value file storage using ZSh associative maps
318 432
 
319
-    zmodload zsh/system
320 433
 
321 434
     # load a map from a file
322 435
     # map must be already instantiated with typeset -A by called
@@ -382,44 +495,40 @@ EOF
382 495
 
383 496
 # {{{ Get/Set REST API
384 497
 
385
-# optional: define restful=1 on source
386
-
387
-[[ "$restful" = "" ]] || {
388
-
389
-    ########
390
-    # Restful API client
391
-    # there is a clear zsh optimization here in get/set kv
392
-    # using zsh/tcp instead of spawning curl
393
-    # and perhaps querying with one call using ?recursive
394
-
395
-    zmodload zsh/net/tcp
498
+########
499
+# Restful API client
500
+# there is a clear zsh optimization here in get/set kv
501
+# using zsh/tcp instead of spawning curl
502
+# and perhaps querying with one call using ?recursive
396 503
 
504
+vars+=(rest_reply_body rest_reply_header)
505
+maps+=(rest_header)
397 506
 
398
-    function restful.put() {
399
-        fn "restful.put $*"
507
+function rest.put() {
508
+    fn "rest.put $*"
400 509
 
401
-        # $1 = hostname
402
-        # $2 = port
403
-        # $3 = path
404
-        # value from stdin |
510
+    # $1 = hostname
511
+    # $2 = port
512
+    # $3 = path
513
+    # value from stdin |
405 514
 
406
-        # to check if the http service is running is up to the caller
515
+    # to check if the http service is running is up to the caller
407 516
 
408
-        _host=${1} # ip address
409
-        _port=${2}
410
-        _path=${3}
411
-        sysread _v
517
+    _host=${1} # ip address
518
+    _port=${2}
519
+    _path=${3}
520
+    sysread _v
412 521
 
413
-        req=(_host)
414
-        ckreq || return $?
522
+    req=(_host)
523
+    ckreq || return $?
415 524
 
416
-        if ztcp $_host $_port; then
525
+    if ztcp $_host $_port; then
417 526
 
418
-            # TODO: work out various parsers, this one works with consul.io
527
+        # TODO: work out various parsers, this one works with consul.io
419 528
 
420
-            _fd=$REPLY
421
-            #    func "tcp open on fd $fd"
422
-            cat <<EOF >& $_fd
529
+        _fd=$REPLY
530
+        #    func "tcp open on fd $fd"
531
+        cat <<EOF >& $_fd
423 532
 PUT ${_path} HTTP/1.1
424 533
 User-Agent: Zuper/$zuper_version
425 534
 Host: ${_host}:${_port}
@@ -429,72 +538,98 @@ Content-Type: application/x-www-form-urlencoded
429 538
 
430 539
 EOF
431 540
 
432
-            print -n "$_v" >& $_fd
433
-
434
-            sysread -i $_fd _res
541
+        print -n "$_v" >& $_fd
435 542
 
436
-            # close connection
437
-            ztcp -c $_fd
543
+        sysread -i $_fd _res
438 544
 
439
-            [[ "$_res" =~ "true" ]] || {
440
-                warn "failed PUT on restful key/value"
441
-                warn "host: ${_host}"
442
-                warn "port: ${_port}"
443
-                warn "path: ${_path}"
444
-                warn "value: $_v"
445
-                print - "$_res"
446
-                zerr
447
-                return 1
448
-            }
545
+        # close connection
546
+        ztcp -c $_fd
449 547
 
450
-        else
451
-            error "cannot connect to restful service: $_host:$_port"
548
+        [[ "$_res" =~ "true" ]] || {
549
+            warn "failed PUT on restful key/value"
550
+            warn "host: ${_host}"
551
+            warn "port: ${_port}"
552
+            warn "path: ${_path}"
553
+            warn "value: $_v"
554
+            print - "$_res"
452 555
             zerr
453 556
             return 1
454
-        fi
557
+        }
455 558
 
456
-        return 0
559
+    else
560
+        error "cannot connect to restful service: $_host:$_port"
561
+        zerr
562
+        return 1
563
+    fi
457 564
 
458
-    }
565
+    return 0
459 566
 
460
-    function restful.get() {
461
-        fn "restful.get $*"
567
+}
462 568
 
463
-        _host=${1}
464
-        _port=${2}
465
-        _path=${3}
569
+function rest.get() {
570
+    fn "rest.get $*"
466 571
 
467
-        req=(_host _port)
468
-        ckreq || return $?
572
+    _host=${1}
573
+    _port=${2}
574
+    _path=${3}
469 575
 
470
-        ztcp $_host $_port || {
471
-            zerr
472
-            return 1
473
-        }
576
+    req=(_host _port)
577
+    ckreq || return $?
474 578
 
475
-        _fd=$REPLY
579
+    ztcp $_host $_port || {
580
+        zerr
581
+        return 1
582
+    }
476 583
 
477
-        # TODO: work out various parsers, this one works with consul.io
584
+    _fd=$REPLY
478 585
 
479
-        cat <<EOF >& $_fd
586
+    # TODO: work out various parsers, this one works with consul.io
587
+
588
+    cat <<EOF >& $_fd
480 589
 GET ${_path} HTTP/1.1
481 590
 User-Agent: Zuper/$zuper_version
482 591
 Host: $_host:$_port
483 592
 Accept: */*
484 593
 
485 594
 EOF
486
-        sysread -i $_fd -o 1 | awk -F: '
487
-/"Value":/ { gsub(/"|}]/,"",$7) ; print $7 }' | base64 -d
488 595
 
489
-        # close connection
490
-        ztcp -c $_fd
596
+    # read header response
597
+    rest_reply=`sysread -i $_fd -o 1`
491 598
 
492
-        return 0
599
+    for i in "${(f)rest_reply}"; do
600
+        print $i | hexdump -C
601
+        # first line is the response code
493 602
 
494
-    }
603
+        [[ "$i" -regex-match "\x0d\x0a$" ]] && {
604
+            func BLANK
605
+            break }
606
+
607
+        # # save other lines in map for fast retrieval
608
+        # _field=${i[(ws@:@)1]}
609
+        # func "$_field - header field parsed"
610
+        # rest_header[$_field]="${i[(ws@:@)2]}"
611
+
612
+        # c=$(( $c + 1 ))
613
+    done
614
+    # rest_reply_header="${(f)$(cat <&$_fd)}"
615
+
616
+    func "${#rest_reply_header} bytes response header stored in rest_reply_header"
617
+    # | awk -F: '
618
+    #/"Value":/ { gsub(/"|}]/,"",$7) ; print $7 }' | base64 -d
619
+
620
+    # TODO: read content-length and use it here
621
+
622
+    rest_reply_body="${(f)$(sysread -i $_fd -o 1)}"
623
+    func "${#rest_reply_body} bytes response body stored in rest_reply_body"
624
+
625
+    # close connection
626
+    ztcp -c $_fd
627
+
628
+    return 0
495 629
 
496 630
 }
497 631
 
632
+
498 633
 # }}} Get/Set REST API
499 634
 
500 635
 # {{{ Helpers