├── config ├── test2.cfg ├── test.cfg ├── jtest.cfg └── jcfg_test.go ├── jsontools ├── jsontools_doc.go ├── json2blob.go ├── blob_test.go ├── internal_test.go ├── jsoncache.go ├── json2map.go ├── prtmap.go ├── defrock_test.go └── defrock.go ├── token ├── token_doc.go ├── tokenise_count.go ├── tokenise_qpopulated.go ├── tokenise_drop.go ├── tokenise_populated.go ├── tokenise_keep.go └── tokenise_qsep.go ├── transform ├── README ├── array_test.go └── tomap.go ├── ssh_broker ├── README └── rsync.go ├── extcmd ├── test_script.ksh ├── extcmd_test.go └── extcmd.go ├── uuid ├── uuid_test.go └── uuid.go ├── rabbit_hole └── run_test.ksh ├── LICENSE ├── security ├── test_cert.pem ├── test_key.pem ├── security_test.go └── cert.go ├── ipc ├── pkg_doc.go ├── msgrtr │ ├── event.go │ └── audience.go ├── chmsg.go └── ipc_test.go ├── test ├── ostack_time.go ├── test_suffix.go └── debug_ssh_broker.go ├── chkpt └── chkpt_test.go ├── ostack ├── ostack_armour.go ├── ostack_time.go ├── ostack_debug.go ├── ostack_v3struct.go ├── ostack_dump.go ├── ostack_vminfo.go └── ostack_user.go ├── clike ├── header_doc.go ├── atoi.go ├── atof.go ├── clike_test.go └── atoll.go ├── README ├── README.md ├── http_logger ├── http_logger_test.go └── http_logger.go ├── bleater ├── sheep_herder.go ├── bleater_test.go └── run_test.ksh └── connman └── tls.go /config/test2.cfg: -------------------------------------------------------------------------------- 1 | 2 | :laser-spec 3 | another-thing = "another foo" 4 | -------------------------------------------------------------------------------- /jsontools/jsontools_doc.go: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | The jsontools package provides several functions which parse 4 | a json 'blob' into either a one dimensional map, or into a 5 | tree structure which may be 'walked'. 6 | */ 7 | package jsontools 8 | -------------------------------------------------------------------------------- /token/token_doc.go: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | The token package provides functions which tokenise, or provide for the 4 | manipulation of tokenised strings. Functions allow for multiple token 5 | separators, and quote aware tokenisation which provide greater flexibility 6 | over the simple strings Split and SplitN functions. 7 | */ 8 | package token 9 | -------------------------------------------------------------------------------- /transform/README: -------------------------------------------------------------------------------- 1 | 2 | This package contains various functions which support the transformation of 3 | something into something else in a generic way. 4 | 5 | tomap: 6 | struct_to_map - create a map from a structure's exported and tagged fields 7 | 8 | tostruct: 9 | map_to_struct - populate a structure's exported and tagged fields from 10 | a map's contents. 11 | -------------------------------------------------------------------------------- /ssh_broker/README: -------------------------------------------------------------------------------- 1 | 2 | The ssh_broker requires a fairly new verion of go along with the ssh package. 3 | Pull the latest with: 4 | hg clone -u release https://code.google.com/p/go 5 | 6 | Or update if you have it. Go version 1.3.3 or later works fine. You'll also 7 | need to get the crypto package in order to get the ssh support: 8 | cd $GOPATH 9 | go get golang.org/x/crypto/ssh 10 | 11 | 12 | Related doc: 13 | https://godoc.org/golang.org/x/crypto/ssh 14 | -------------------------------------------------------------------------------- /extcmd/test_script.ksh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ksh 2 | # 3 | # test script called by the test go function(s) 4 | # generate multiple lines of output with silly stuff on them. 5 | 6 | if [[ $1 == "long" ]] 7 | then 8 | for (( i=0; i < 5000; i++ )) 9 | do 10 | echo "test line: $i" 11 | done 12 | exit 0 13 | fi 14 | 15 | echo " $SHELL" 16 | echo "test script output" 17 | i=0 18 | for x in "$@" 19 | do 20 | echo "[$i] $x" 21 | (( i++ )) 22 | done 23 | 24 | echo "this is written to stderr" >&2 25 | echo "as is this is written to stderr" >&2 26 | echo "and finally this is written to stderr" >&2 27 | -------------------------------------------------------------------------------- /uuid/uuid_test.go: -------------------------------------------------------------------------------- 1 | package uuid_test 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/att/gopkgs/uuid" 8 | ) 9 | 10 | 11 | func TestUuid4( t *testing.T ) { 12 | u, err := uuid.Mk_v4() 13 | 14 | if u == nil { 15 | t.Fail() 16 | fmt.Printf( "u was nil: %s\n", err ) 17 | } 18 | //u2, _ := uuid.Mk_v4() 19 | u2 := uuid.NewRandom() 20 | 21 | fmt.Printf( "uuid = %s plain = %s\n", u, u.Plain_string() ) 22 | fmt.Printf( "uuid2 = %s\n", u2 ) 23 | fmt.Printf( "uuid == uuid2? %v (epect true)\n", u.Equals( u ) ) 24 | fmt.Printf( "uuid == uuid2? %v (epect false)\n", u.Equals( u2 ) ) 25 | } 26 | -------------------------------------------------------------------------------- /rabbit_hole/run_test.ksh: -------------------------------------------------------------------------------- 1 | 2 | # simple environment setup and then runs the go test 3 | 4 | 5 | export RHT_PW=BunnyHop # replace with your password 6 | export RHT_USER=scott # replace with your user name 7 | export RHT_HOST=$RMQ_HOST # replace with IP/host where rmq is running 8 | 9 | export RHT_PAUSE=0 10 | export RHT_DEL_EX=rhtest_del # special exchange to test delete 11 | 12 | export RHT_EXTYPE="fanout+!du>rht_queue+!du+ad+ex" 13 | export RHT_EXTYPE="fanout+!du>random+!du+ad+ex" 14 | 15 | # these tests must run in parallel as they delete exchanges etc when done and share names 16 | go test -parallel 1 "$@" 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | Copyright (c) 2013-2015 AT&T Intellectual Property 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at: 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | -------------------------------------------------------------------------------- /security/test_cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICHzCCAYqgAwIBAgIBATALBgkqhkiG9w0BAQUwLzEaMBgGA1UECgwRQVRUX0xh 3 | YnNfUmVzZWFyY2gxETAPBgNVBAMMCGZvb19jZXJ0MB4XDTE0MDYwNzE0MDMwMVoX 4 | DTE2MDYwNjE0MDMwMVowLzEaMBgGA1UECgwRQVRUX0xhYnNfUmVzZWFyY2gxETAP 5 | BgNVBAMMCGZvb19jZXJ0MIGdMAsGCSqGSIb3DQEBAQOBjQAwgYkCgYEAzI6uUZTi 6 | w9Q69nZOVD/MPwUCJKQSurCKdZCYy3Ov0UsUlRaa3c+OLRptDE8BMM/pEneFyxgb 7 | 3T5R+Y+8TaGGsaLlGQYOWSkGhD+IW0/wHDkD01PcRb9mjOLAIXxx1xiz3Dov3aBy 8 | fuZWrT70TNBgb25dUTOJTDWePKbL9se46a0CAwEAAaNRME8wDgYDVR0PAQH/BAQD 9 | AgCgMA0GA1UdDgQGBAQBAgMEMA8GA1UdIwQIMAaABAECAwQwHQYDVR0RBBYwFIIJ 10 | bG9jYWxob3N0ggdjaGFybGllMAsGCSqGSIb3DQEBBQOBgQCzRfCMetze7VFYkrEU 11 | XLRfFK7dykfQjQWTBC+1uJr2ujrTLXNoFY96foLq+bwbInW42J+h/dzuEz7kENF9 12 | vLW+d5vJ68V+VR+aud5bqXv7n5YTxoHjTaXLR1zUrvd1MZrV/GO9/9FKaTIFkJPI 13 | UV2Oel/iTPXYtdh3k9SMcNpGDQ== 14 | -----END CERTIFICATE----- 15 | -------------------------------------------------------------------------------- /security/test_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXQIBAAKBgQDMjq5RlOLD1Dr2dk5UP8w/BQIkpBK6sIp1kJjLc6/RSxSVFprd 3 | z44tGm0MTwEwz+kSd4XLGBvdPlH5j7xNoYaxouUZBg5ZKQaEP4hbT/AcOQPTU9xF 4 | v2aM4sAhfHHXGLPcOi/doHJ+5latPvRM0GBvbl1RM4lMNZ48psv2x7jprQIDAQAB 5 | AoGAfnRN6v0BWlIeyTHFpmtyFhtAgeoz8dklHxxueIdhzx588NVyk2C14cgOVaeZ 6 | ctV0ngfpW+3he9oEzyQ+IjeSja6Fm8XQqpJEDP+FznQHzUao3DcQinT+Iw8FCIBi 7 | lcsraGrHxsLb3Ei0xco28po/rDw/bLnJ+zM3bPSXOb8sfEECQQDaHcKaD70Uw354 8 | cmHVF6WzRJS9lscBelhBQsENqyev2z31yNn7/Lyj9lGrqRb4SvjolSG3+4Uqze3r 9 | Uf1ZJrcFAkEA8BYK8OoFRMlKBgDesG1qCJiRYF4HF4t6HiJGdBb1SNCbzTqB6dLq 10 | gaiqvmVgrZG2fqGYwkT3TDWYzR4PE96YiQJBALAU9GQBLqjThYbg+D/aHkfRpq5D 11 | SZbU2OieZlGNibV7LdL8+ZVnHDbVhGsrT0LU64p/0j/ACmpa4qZlgzx0d60CQQCq 12 | rSDuD4/bTLXA+tFU3xOoNL30oiOsi00vKvKnCMMrFpIMHfHJRlXMAxoO7IcEHTh9 13 | YWf1g43Csb+E4j+Zwn7xAkBGSd2MMvlCzrGMaRyCr4Y5lwI3gRlapCd/e4GOEXWv 14 | 5S6ypmj/8ZqrjZmYXZcBqCpBvamGpQAlMAO0ds3l9Wn9 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /config/test.cfg: -------------------------------------------------------------------------------- 1 | # test configuration used by the config_test.go module 2 | 3 | # comment should be ignored as is the blank line before this 4 | foo = bar 5 | qfoo = "quoted bar with trailing spaces in quotes " 6 | tfoo = unqoted multi token bar 7 | vfoo = 3 8 | ffoo = 2.567 9 | jfoo = "{ \"name\": \"fred\", \"height\": \"167 inches\", \"weight\": \"89 stone\" }" # some comment 10 | bar = "quoted string with comment trailing" # this comment should be gone 11 | moo = "quoted string with extra spaces" followed by unquoted stuff # this comment should be gone 12 | goo = 1.234 #float with comment 13 | boo="key value without spaces round =" 14 | poo = 123 456 789 # should reduce to single number on first test, and 3 token string on second pass, both with comment stripped 15 | 16 | :template # comment should be ignored 17 | default_size = 45 18 | default_name = "template name" 19 | msg = foo 20 | msg += "bar goo" 21 | msg+= "hello" 22 | msg+=end 23 | i64 = 1234567899 # must be larger than 1234567890 to pass the test 24 | 25 | :laser-spec 26 | default_size = 0.904 27 | default_name = laser name 28 | 29 | 30 | :bool-test 31 | istrue = true 32 | isfalse = false 33 | inttrue = 1 34 | intfalse = 0 35 | 36 | " ) 53 | if hloc > 0 && hloc < jloc { // html tag seems to be found before possible json 54 | dump_array( "scan4gook", 30, buf ) 55 | return fmt.Errorf( "invalid json: HTML tag, or similar, found outside of json" ) 56 | } 57 | 58 | return nil 59 | } 60 | -------------------------------------------------------------------------------- /extcmd/extcmd_test.go: -------------------------------------------------------------------------------- 1 | // vi: sw=4 ts=4: 2 | /* 3 | --------------------------------------------------------------------------- 4 | Copyright (c) 2013-2015 AT&T Intellectual Property 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | --------------------------------------------------------------------------- 18 | */ 19 | 20 | 21 | /* 22 | Run like this to test things and pretty print the json output 23 | go test|grep State|parse_json.rb 24 | 25 | Where parse_json.rb is some kind of json formatter (if parse_json.rb isn't available) 26 | */ 27 | package extcmd_test 28 | 29 | import ( 30 | "fmt" 31 | "os" 32 | "testing" 33 | 34 | "github.com/att/gopkgs/extcmd" 35 | ) 36 | 37 | func TestExec( t *testing.T ) { 38 | fmt.Fprintf( os.Stderr, "test started\n" ); 39 | jdata, err := extcmd.Cmd2json( `test_script.ksh foo bar you "this is one" last`, "test_cmd" ) 40 | 41 | if err != nil { 42 | fmt.Fprintf( os.Stderr, "command error: %s\n", err ) 43 | t.Fail() 44 | } 45 | 46 | if jdata != nil { 47 | fmt.Fprintf( os.Stderr, "%s\n", jdata ); 48 | } else { 49 | fmt.Fprintf( os.Stderr, "{jdata was nil}\n" ) 50 | } 51 | } 52 | 53 | func TestLong( t *testing.T ) { 54 | fmt.Fprintf( os.Stderr, "long test started\n" ); 55 | jdata, err := extcmd.Cmd2json( `test_script.ksh long foo bar you "this is one" last`, "test_cmd" ) 56 | 57 | if err != nil { 58 | fmt.Fprintf( os.Stderr, "command error: %s\n", err ) 59 | t.Fail() 60 | } 61 | 62 | if jdata != nil { 63 | fmt.Fprintf( os.Stderr, "%s\n", jdata ); 64 | } else { 65 | fmt.Fprintf( os.Stderr, "{jdata was nil}\n" ) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /ostack/ostack_time.go: -------------------------------------------------------------------------------- 1 | // vi: sw=4 ts=4: 2 | /* 3 | --------------------------------------------------------------------------- 4 | Copyright (c) 2013-2015 AT&T Intellectual Property 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | --------------------------------------------------------------------------- 18 | */ 19 | 20 | /* 21 | ----------------------------------------------------------------------------------------- 22 | Mnemonic: ostack_time 23 | Abstract: Functions that make it possible to deal with openstack's uncanny ability 24 | to generate only human readable times. 25 | Author: E. Scott Daniels 26 | Date: 16 August 2014 27 | 28 | Mod: 09 Nov 2014 - Check for nil pointer in Unix_time. 29 | ----------------------------------------------------------------------------------------- 30 | */ 31 | 32 | package ostack 33 | 34 | import ( 35 | "time" 36 | ) 37 | 38 | 39 | const ( 40 | GO_STD_TIME string = "2006-01-02T15:04:05Z" // go's reference time in openstack format 41 | ) 42 | 43 | /* 44 | Unix_time accepts a human readable string from openstack (presumed to be in the format 45 | 2014-08-18T02:34:48Z) and convert it into a usable, unix timestamp. 46 | 47 | If there is an error, err is set and the returned time will be 0. 48 | */ 49 | func Unix_time( os_time *string ) ( utime int64, err error ) { 50 | if os_time == nil || *os_time == "" { 51 | utime = time.Now().Unix() + 300 // no time given by openstack? assume 5 minutes 52 | return 53 | } 54 | 55 | utime = 0 56 | t, err := time.Parse( GO_STD_TIME, *os_time ) 57 | if err == nil { 58 | utime = t.Unix( ) 59 | } 60 | 61 | return 62 | } 63 | -------------------------------------------------------------------------------- /clike/header_doc.go: -------------------------------------------------------------------------------- 1 | // vi: sw=4 ts=4: 2 | /* 3 | --------------------------------------------------------------------------- 4 | Copyright (c) 2013-2015 AT&T Intellectual Property 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | --------------------------------------------------------------------------- 18 | */ 19 | 20 | // this doc is picked up and applied at the package level, makes sense to have it 21 | // here rather than in one of the other files. 22 | 23 | 24 | /* 25 | The clike package provides some functions that behave more like the traditional Clib 26 | functions than functions supported by the Go packages. Currently these all revolve 27 | around the atoX() family of Clib functions which do not error when they encounter a 28 | non-digit, and return a 0 if the leading character in the string is not numeric or a 29 | sign. These Clike functions all stop parsing when the first non digit is encountered. 30 | 31 | A small extension has been added which allows the string representation to contain a 32 | trailing suffix. The suffix causes the function to expand the value. For example the 33 | suffix M causes the value to be expanded to 1000000 times the string value. The following 34 | suffixes are supported: 35 | 36 | MiB or m power of 2 37 | GiB or g power of 2 38 | KiB or k power of 2 39 | M power of 10 40 | G power of 10 41 | K power of 10 42 | 43 | 44 | The integer based functions all accept a leading '0x' or '0' to indicate that the string 45 | should be interpreted as a hex or octal value respectively. 46 | 47 | The functions all accept either a string or an array of bytes ([]byte). 48 | 49 | */ 50 | package clike 51 | -------------------------------------------------------------------------------- /ostack/ostack_debug.go: -------------------------------------------------------------------------------- 1 | // vi: sw=4 ts=4: 2 | /* 3 | --------------------------------------------------------------------------- 4 | Copyright (c) 2013-2015 AT&T Intellectual Property 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | --------------------------------------------------------------------------- 18 | */ 19 | 20 | package ostack 21 | 22 | /* 23 | Mnemonic: ostack_debug 24 | Abstract: Some quick debug functions. 25 | Author: E. Scott Daniels 26 | Date: 7 August 2014 27 | */ 28 | 29 | import ( 30 | "fmt" 31 | "os" 32 | ) 33 | 34 | /* 35 | Dump the url if the counter max is not yet reached. 36 | */ 37 | func dump_url( id string, max int, url string ) { 38 | if dbug_url_count < max { 39 | dbug_url_count++ 40 | 41 | fmt.Fprintf( os.Stderr, "%s >>> url= %s\n", id, url ) 42 | } 43 | } 44 | 45 | /* 46 | Dump the raw json array if the counter max has not yet been reached. 47 | */ 48 | func dump_json( id string, max int, json []byte ) { 49 | if dbug_json_count < max { 50 | dbug_json_count++ 51 | 52 | fmt.Fprintf( os.Stderr, "%s >>> json= %s\n", id, json ) 53 | } 54 | } 55 | 56 | /* 57 | Dump a raw array if the counter max has not yet been reached. 58 | */ 59 | func dump_array( id string, max int, stuff []byte ) { 60 | if dbug_json_count < max { 61 | dbug_json_count++ 62 | 63 | fmt.Fprintf( os.Stderr, "%s >>> raw= %s\n", id, stuff ) 64 | } 65 | } 66 | 67 | /* 68 | Allow debugging to be reset or switched off. Set high (20) to 69 | turn off, 0 to reset. 70 | */ 71 | func Set_debugging( count int ) { 72 | dbug_json_count = count 73 | dbug_url_count = count 74 | } 75 | 76 | func Set_latency_debugging( state bool ) { 77 | debug_latency = state 78 | } 79 | -------------------------------------------------------------------------------- /jsontools/blob_test.go: -------------------------------------------------------------------------------- 1 | // vi: sw=4 ts=4: 2 | /* 3 | --------------------------------------------------------------------------- 4 | Copyright (c) 2013-2015 AT&T Intellectual Property 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | --------------------------------------------------------------------------- 18 | */ 19 | 20 | 21 | package jsontools_test 22 | 23 | import ( 24 | "fmt" 25 | "testing" 26 | "os" 27 | 28 | "github.com/att/gopkgs/jsontools" 29 | ) 30 | 31 | func TestJsonblob( t *testing.T ) { 32 | var ( 33 | jstring string 34 | jbytes []byte 35 | ) 36 | 37 | jstring = `{ "actions": [ { "action": "setqueues1", "qdata": [ "queue1", "queue2", "queue3" ] }, { "action": "setqueues2", "qdata": [ "2queue1", "2queue2", "2queue3" ] } ] }` 38 | jbytes = []byte( jstring ); 39 | jif, err := jsontools.Json2blob( jbytes[:], nil, false ) 40 | 41 | if err != nil { 42 | fmt.Fprintf( os.Stderr, "errors unpacking json: %s [FAIL]\n", err ) 43 | t.Fail(); 44 | return; 45 | } 46 | 47 | root_map := jif.( map[string]interface{} ) 48 | alist := root_map["actions"].( []interface{} ); 49 | 50 | if alist == nil { 51 | fmt.Fprintf( os.Stderr, "alist in json was nil [FAIL]\n" ) 52 | t.Fail(); 53 | return; 54 | } 55 | fmt.Fprintf( os.Stderr, "found alist, has %d actions [OK]\n", len( alist ) ) 56 | 57 | for i := range alist { 58 | action := alist[i].( map[string]interface{} ) 59 | atype := action["action"].( string ) 60 | data := action["qdata"].( []interface{} ) 61 | 62 | fmt.Fprintf( os.Stderr, "action %d has type: %s\n", i, atype ) 63 | for j := range data { 64 | fmt.Fprintf( os.Stderr, " data[%d] = %s\n", j, data[j].( string ) ) 65 | } 66 | } 67 | 68 | 69 | 70 | fmt.Fprintf( os.Stderr, "===== end blob testing ======\n\n" ) 71 | } 72 | -------------------------------------------------------------------------------- /ostack/ostack_v3struct.go: -------------------------------------------------------------------------------- 1 | // vi: sw=4 ts=4: 2 | /* 3 | --------------------------------------------------------------------------- 4 | Copyright (c) 2013-2015 AT&T Intellectual Property 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | --------------------------------------------------------------------------- 18 | */ 19 | 20 | package ostack 21 | 22 | /* 23 | Mnemonic: ostack_v3struct 24 | Abstract: Structures related to v3 api calls 25 | Date: 03 April 2015 26 | Author: E. Scott Daniels 27 | */ 28 | 29 | 30 | type osv3_domain struct { 31 | Id string 32 | Name string 33 | } 34 | 35 | type osv3_proj struct { 36 | Domain *osv3_domain 37 | Id string 38 | Name string 39 | } 40 | 41 | type osv3_role struct { 42 | Id string 43 | Name string 44 | } 45 | 46 | type osv3_endpoint struct { 47 | Url string 48 | Region string 49 | Legacy_endpoint_id string 50 | Interface string 51 | Id string 52 | } 53 | 54 | type osv3_catentry struct { 55 | Endpoints []*osv3_endpoint 56 | Type string // network compute ec2 etc. 57 | Id string 58 | } 59 | 60 | type osv3_user struct { 61 | //Domain // who knows what this is 62 | Id string 63 | Name string 64 | } 65 | 66 | /* 67 | Returned by v3/auth/tokens. This is a union of all possible top layer things. 68 | */ 69 | type osv3_token struct { 70 | Methods []string 71 | Roles []*osv3_role 72 | Project *osv3_proj 73 | Expires_at string // human readable expiry 74 | //Extras unknown 75 | User *osv3_user 76 | Issued_at string // token issue date 77 | 78 | } 79 | 80 | /* 81 | A generic list of things that might come back from ostack 82 | */ 83 | type osv3_generic struct { 84 | Token *osv3_token 85 | Project *osv3_proj 86 | Error *error_obj // we can use the generic error handling things here 87 | } 88 | 89 | -------------------------------------------------------------------------------- /token/tokenise_qpopulated.go: -------------------------------------------------------------------------------- 1 | // vi: sw=4 ts=4: 2 | /* 3 | --------------------------------------------------------------------------- 4 | Copyright (c) 2013-2015 AT&T Intellectual Property 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | --------------------------------------------------------------------------- 18 | */ 19 | 20 | 21 | package token 22 | 23 | import ( 24 | ) 25 | 26 | 27 | /* 28 | --------------------------------------------------------------------------------------------- 29 | Mnemonic: tokenise_qpopulated 30 | 31 | Returns: number of tokens, tokens[] 32 | Date: 22 Apr 2012 33 | Author: E. Scott Daniels 34 | 35 | Mods: 13 Jan 2014 - corrected bug causing quotes to be left on last token if 36 | there isn't a sep inside of the tokens. 37 | 02 May 2014 - corrected documentation, removed unneeded commented out code. 38 | 30 Nov 2014 - Now allows escaped quotes in quoted portion. 39 | 01 Dec 2014 - Now uses the work horse tokenise_all() function, so the majority 40 | of this disappears. 41 | --------------------------------------------------------------------------------------------- 42 | */ 43 | 44 | /* 45 | Takes a string and slices it into tokens using the characters in sepchrs 46 | as the breaking points, but allowing double quotes to provide protection 47 | against separation. For example, if sepchrs is ",|", then the string 48 | foo,bar,"hello,world",,"you|me" 49 | 50 | would break into 4 tokens: 51 | foo 52 | bar 53 | hello,world 54 | you|me 55 | 56 | Similar to tokenise_qsep, but this method removes empty tokens from the 57 | final result. 58 | 59 | 60 | The return value is the number of tokens and the list of tokens. 61 | */ 62 | func Tokenise_qpopulated( buf string, sepchrs string ) (int, []string) { 63 | return tokenise_all( buf, sepchrs, false ) 64 | } 65 | 66 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | 2 | This is a collection of Go (golang) packages (a.k.a. libraries) which might be useful. 3 | 4 | 5 | Currently it includes: 6 | arista Methods for interfacing with an Arista switch which is configured to 7 | use HTTP or HTTPs interface. 8 | 9 | bleater A level based logging package. 10 | 11 | chkpt Provides an easy mechanism for creating dual-tumbler checkpoint files. 12 | 13 | clike Some tools (atoi-ish) that behave in a Clib manner (not aborting if a 14 | non-digit is encountered (ato* family) and add some nice extensions 15 | for post fixing (e.g. GiB) a value. 16 | 17 | config A configuration file parser which provides for a section based file 18 | and allows for inclusion of sub files. 19 | 20 | ipc Interprocess communications support. Provies a simple request/response 21 | message block and some wrapper functions to easily send a message 22 | on a channel. Also provides a tickler function that can be started 23 | and will send messages to a channel at prescribed times. 24 | 25 | jsontools Tools which assist with the parsing and use of json strings. 26 | 27 | ostack Some methods that provide an easy authintication interface and access 28 | to some basic functions using OpenStack's API. 29 | 30 | token String to token methods. 31 | 32 | 33 | How to use 34 | Go has a very structured source environment, and while the research.forge git environment 35 | seems difficult to use with Go's 'go get' function, merging this library into the Go 36 | environment should be easy. Assuming the GOPATH environment variable is set, and there 37 | are src, bin and pkg directories inside of the GOPATH directory, then move this directory 38 | into $GOPATH/src/forge.research.att.com/gopkgs. If you haven't checked out this source 39 | yet, then create the forge.research.att.com directory, cd to it, and then use git to 40 | clone this source (git will create the gopkgs directory) 41 | 42 | In your code, use a statement like 43 | import "codecloud.att.com/gopkgs/clike" 44 | 45 | to cause the compiler to reference a package in this set. Once it is referenced on an 46 | import statement, the 'short name' can be used in the code: 47 | ival := clike.Atoi64( varname ) 48 | 49 | 50 | Go Package Doc 51 | Running the Go package documentation tool on any of the packages in this source should 52 | generate the documentation needed to make use of these packages. As an example 53 | 54 | godoc codecloud..att.com/gopkgs/token 55 | 56 | Will generate the documentation on the token pacakge. 57 | -------------------------------------------------------------------------------- /token/tokenise_drop.go: -------------------------------------------------------------------------------- 1 | // vi: sw=4 ts=4: 2 | /* 3 | --------------------------------------------------------------------------- 4 | Copyright (c) 2013-2015 AT&T Intellectual Property 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | --------------------------------------------------------------------------- 18 | */ 19 | 20 | 21 | package token 22 | 23 | import ( 24 | "strings"; 25 | ) 26 | 27 | 28 | /* 29 | --------------------------------------------------------------------------------------- 30 | Mnemonic: tokenise_drop 31 | Returns: ntokens, tokens[] 32 | Date: 22 Apr 2012 33 | Author: E. Scott Daniels 34 | Mod: 03 Feb 2015 - Removed unreachable code 35 | 07 Jun 2018 : Remove unreachable code (keep vet happy) 36 | --------------------------------------------------------------------------------------- 37 | */ 38 | 39 | /* 40 | Takes a string and slices it into tokens using the characters in sepchrs 41 | as the breaking points. The separation characters are discarded. 42 | */ 43 | func Tokenise_drop( buf string, sepchrs string ) ( ntokens int, tokens []string) { 44 | var idx int; 45 | 46 | idx = 0; 47 | tokens = make( []string, 2048 ); 48 | 49 | sepchrs += "\\"; 50 | subbuf := buf; 51 | tokens[idx] = ""; 52 | for { 53 | if i := strings.IndexAny( subbuf, sepchrs ); i >= 0 { 54 | if subbuf[i:i+1] == "\\" { // next character is escaped 55 | tokens[idx] += subbuf[0:i]; // add everything before slant 56 | tokens[idx] += subbuf[i+1:i+2]; // add escaped char 57 | subbuf = subbuf[i+2:]; // advance past escaped char 58 | } else { 59 | if i > 0 { // characters before the sep, capture them 60 | tokens[idx] += subbuf[0:i]; // add everything before separator 61 | } 62 | 63 | idx++; 64 | tokens[idx] = ""; 65 | subbuf = subbuf[i+1:]; 66 | } 67 | } else { 68 | tokens[idx] += subbuf[:len( subbuf )]; 69 | tokens = tokens[0:idx+1] 70 | ntokens = idx + 1 71 | return 72 | } 73 | 74 | if len( subbuf ) < 1 { 75 | ntokens = idx 76 | tokens = tokens[0:idx+1]; 77 | return 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /jsontools/internal_test.go: -------------------------------------------------------------------------------- 1 | // vi: sw=4 ts=4: 2 | /* 3 | --------------------------------------------------------------------------- 4 | Copyright (c) 2013-2015 AT&T Intellectual Property 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | --------------------------------------------------------------------------- 18 | */ 19 | 20 | /* 21 | Mnemonic: internal_test.go 22 | Abstract: Tests which drive internal functions and thus the package must 23 | be jsontools and not jsontools_test 24 | Author: E. Scott Daniels 25 | Date: 5 June 2017 26 | */ 27 | 28 | package jsontools 29 | 30 | import ( 31 | "fmt" 32 | "testing" 33 | "os" 34 | 35 | // "github.com/att/gopkgs/jsontools" 36 | ) 37 | 38 | 39 | func TestEscString( t *testing.T ) { 40 | s := `now is the "time" for all good "boys" to get real` 41 | es := `now is the \"time\" for all good \"boys\" to get real` // expected 42 | 43 | fmt.Fprintf( os.Stderr, "------ testing escape addition starts ----- \n" ) 44 | gs := add_esc( s ) 45 | if gs != es { 46 | fmt.Fprintf( os.Stderr, "[FAIL] escaped string not what we expected\n got (%s)\nexpect (%s)\n", gs, es ) 47 | t.Fail() 48 | } else { 49 | fmt.Fprintf( os.Stderr, "[OK] escaped string is what we expected\n got (%s)\nexpect (%s)\n", gs, es ) 50 | } 51 | 52 | fmt.Fprintf( os.Stderr, "------ testing escape addition complete -----\n" ) 53 | } 54 | 55 | func TestRmEsc( t *testing.T ) { 56 | s := "now is the \"time\" for all good \"boys\" to get \\real\\" // expected string after escapes stripped 57 | es := "now is the \\\"time\\\" for all good \\\"boys\\\" to get \\\\real\\\\" // escaped string 58 | 59 | fmt.Fprintf( os.Stderr, "------ testing escape removal starts ----- \n" ) 60 | gs := rm_esc( es ) 61 | if gs != s { 62 | fmt.Fprintf( os.Stderr, "[FAIL] escaped string not what we expected\n got (%s)\nexpect (%s)\n", gs, s ) 63 | t.Fail() 64 | } else { 65 | fmt.Fprintf( os.Stderr, "[OK] escaped string is what we expected\n got (%s)\nexpect (%s)\n", gs, s ) 66 | } 67 | 68 | fmt.Fprintf( os.Stderr, "------ testing escape removal complete -----\n" ) 69 | } 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | Gopkgs 3 | ====== 4 | 5 | Gopkgs is a collection of Go language (golang.org) packages (a.k.a. libraries) which 6 | support other projects in this github organisation (e.g. Tegu), and that might be useful 7 | to other projects. 8 | 9 | Currently, the packages include: 10 | 11 | ### arista 12 | Methods for interfacing with an Arista switch which is configured to use HTTP or HTTPs 13 | interface. 14 | 15 | ### bleater 16 | A level based logging package. 17 | 18 | ### chkpt 19 | Provides an easy mechanism for creating dual-tumbler checkpoint files. 20 | 21 | ### clike 22 | Some tools (atoi-ish) that behave in a Clib manner, such as not aborting if a 23 | non-digit is encountered (ato* family) and add some extensions for 24 | values with post-fixed units (e.g. 10GiB or 10G). 25 | 26 | ### config 27 | A configuration file parser which provides for a section based file 28 | and allows for inclusion of sub files. 29 | 30 | ### connman 31 | A TCP connection manager. 32 | 33 | ### extcmd 34 | An external command interface which bundles the results (stdout/stderr) 35 | into a manageable structure for the caller. 36 | 37 | ### http_logger 38 | Provides a basic logger to log HTTP requests in the format that will be familiar 39 | to anyone who has ever used Apache. 40 | 41 | ### ipc 42 | Interprocess communications support. Provides a simple request/response message block 43 | and some wrapper functions to easily send a message on a channel. 44 | Also provides a tickler function that can be started and will send messages to a channel 45 | at prescribed times. 46 | 47 | ### jsontools 48 | Tools which assist with the parsing and streaming JSON management. 49 | 50 | ### ostack 51 | An interface to OpenStack which provides authorisation, and general queries making use 52 | of OpenStack as a data source. 53 | 54 | ### security 55 | Support for generating self-signed certificates. 56 | 57 | ### ssh_broker 58 | A broker which manages persistent SSH sessions with one or more hosts allowing for the 59 | remote execution of commands without the session setup overhead needed if each call were 60 | executed via a 'system()' like approach. 61 | 62 | ### token 63 | Tokenising functions providing features like tokens with quotes 64 | and embedded separators, unique token generation, token counting, 65 | etc. 66 | 67 | 68 | Go Package Doc 69 | -------------- 70 | Running the Go package documentation tool on any of the packages in this source should 71 | generate the documentation needed to make use of these packages. As an example 72 | 73 | `godoc github.com/att/gopkgs/token` 74 | 75 | will generate the documentation on the token package. 76 | -------------------------------------------------------------------------------- /http_logger/http_logger_test.go: -------------------------------------------------------------------------------- 1 | // vi: sw=4 ts=4: 2 | /* 3 | --------------------------------------------------------------------------- 4 | Copyright (c) 2013-2015 AT&T Intellectual Property 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | --------------------------------------------------------------------------- 18 | */ 19 | 20 | package http_logger_test 21 | 22 | import ( 23 | "fmt" 24 | "github.com/att/gopkgs/http_logger" 25 | "io/ioutil" 26 | "net/http" 27 | "net/url" 28 | "os" 29 | "testing" 30 | "time" 31 | ) 32 | 33 | func TestHttp_logger(t *testing.T) { 34 | // --- Building a sample http request to send to log file --- 35 | request := http.Request{RemoteAddr: "192.168.0.3", 36 | Method: "GET", 37 | URL: &url.URL{ 38 | Path: "/tegu/mirrors", 39 | RawQuery: "", 40 | }, 41 | Proto: "HTTP/1.1", 42 | } 43 | msg := "" // parameter to send to log request 44 | userid := "pg754h" // Userid to send to log request 45 | code := 404 // Test parameter for log request 46 | 47 | // --- Specifying a log directory for logs testing --- 48 | log_dir := "/tmp" 49 | 50 | logger := http_logger.Mk_Http_Logger(&log_dir) // Creating http logger object 51 | 52 | // Sending request and other details to log request to store in logs 53 | logger.LogRequest(&request, userid, code, len(msg)) 54 | if err := logger.Close(); err != nil { 55 | fmt.Fprintf(os.Stderr, "Unable to close the log file : %s\n", err) 56 | t.Fail() 57 | } 58 | 59 | fname := logger.Get_fname() // get filename of log 60 | now := time.Now() // get latest time details to form logname 61 | 62 | // Forming a filename format that the log uses 63 | fname = fmt.Sprintf("%s/%s.%4d%02d%02d", log_dir, fname, now.Year(), now.Month(), now.Day()) 64 | 65 | // Reading the contents of the file 66 | _, err := ioutil.ReadFile(fname) 67 | if err != nil { 68 | fmt.Fprintf(os.Stderr, "Unable to read the file contents : %s\n", err) 69 | t.Fail() 70 | } 71 | 72 | // Change the filename of the log to the user specified name 73 | chg_fname := "changed_access_log" 74 | if err = logger.Set_fname(chg_fname); err != nil { 75 | fmt.Fprintf(os.Stderr, "Unable to rename the log file : %s\n", err) 76 | t.Fail() 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /token/tokenise_populated.go: -------------------------------------------------------------------------------- 1 | // vi: sw=4 ts=4: 2 | /* 3 | --------------------------------------------------------------------------- 4 | Copyright (c) 2013-2015 AT&T Intellectual Property 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | --------------------------------------------------------------------------- 18 | */ 19 | 20 | 21 | package token 22 | 23 | import ( 24 | "strings"; 25 | ) 26 | 27 | 28 | /* 29 | --------------------------------------------------------------------------------------- 30 | Mnemonic: tokenise_fields 31 | Returns: ntokens, tokens[] 32 | Date: 21 November 2013 33 | Author: E. Scott Daniels 34 | Mod: 03 Feb 2015 - Removed unreachable code. 35 | --------------------------------------------------------------------------------------- 36 | */ 37 | 38 | /* 39 | Takes a string and slices it into tokens using the characters in sepchrs 40 | as the breaking points keeping only populated fields. The separation characters 41 | are discarded. Null (empty) tokens are dropped allowing a space separated record to 42 | treat multiple spaces as a single separator. 43 | 44 | The return values are the number of tokens and the list of token strings. 45 | */ 46 | func Tokenise_populated( buf string, sepchrs string ) (int, []string) { 47 | var tokens []string; 48 | var idx int; 49 | 50 | idx = 0; 51 | tokens = make( []string, 2048 ); 52 | 53 | sepchrs += "\\"; 54 | subbuf := buf; 55 | tokens[idx] = ""; 56 | for { 57 | if i := strings.IndexAny( subbuf, sepchrs ); i >= 0 { 58 | if subbuf[i:i+1] == "\\" { // next character is escaped 59 | tokens[idx] += subbuf[0:i]; // add everything before slant 60 | tokens[idx] += subbuf[i+1:i+2]; // add escaped char 61 | subbuf = subbuf[i+2:]; // advance past escaped char 62 | } else { 63 | if i > 0 { // characters before the sep, capture them 64 | tokens[idx] += subbuf[0:i]; // add everything before separator 65 | idx++; // capture only non-empty tokens 66 | tokens[idx] = ""; 67 | } 68 | 69 | subbuf = subbuf[i+1:]; 70 | } 71 | } else { 72 | tokens[idx] += subbuf[:len( subbuf )]; 73 | return idx+1, tokens[0:idx+1]; 74 | } 75 | 76 | if len( subbuf ) < 1 { 77 | return idx, tokens[0:idx+1]; 78 | } 79 | } 80 | 81 | return 0, nil 82 | } 83 | -------------------------------------------------------------------------------- /security/cert.go: -------------------------------------------------------------------------------- 1 | // vi: sw=4 ts=4: 2 | /* 3 | --------------------------------------------------------------------------- 4 | Copyright (c) 2013-2015 AT&T Intellectual Property 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | --------------------------------------------------------------------------- 18 | */ 19 | 20 | /* 21 | Mnemonic: cert 22 | Abstract: Support for generating self-signed certificates 23 | Date: 06 June 2014 24 | Author: E. Scott Daniels 25 | */ 26 | 27 | package security 28 | 29 | import ( 30 | "crypto/rand" 31 | "crypto/rsa" 32 | "crypto/x509" 33 | "crypto/x509/pkix" 34 | "encoding/pem" 35 | "math/big" 36 | "os" 37 | "time" 38 | ) 39 | 40 | /* 41 | Create a certificate (self signed) and write to the output file. 42 | */ 43 | func Mk_cert( key_bits int, cert_name *string, dns_list []string, cfname *string, kfname *string ) ( err error ) { 44 | 45 | rsa_key, err := rsa.GenerateKey( rand.Reader, key_bits ) 46 | if err != nil { 47 | return 48 | } 49 | 50 | now := time.Now() 51 | 52 | // per golang doc, only these fields from the cert are used as the template: 53 | // SerialNumber, Subject, NotBefore, NotAfter, KeyUsage, ExtKeyUsage, UnknownExtKeyUsage, BasicConstraintsValid, IsCA, MaxPathLen, 54 | // SubjectKeyId, DNSNames, PermittedDNSDomainsCritical, PermittedDNSDomains. 55 | template := x509.Certificate { 56 | SerialNumber: new( big.Int ).SetInt64( 1 ), 57 | Subject: pkix.Name { 58 | CommonName: *cert_name, 59 | Organization: []string{"ATT_Labs_Research"}, 60 | }, 61 | NotBefore: now, 62 | NotAfter: now.Add( 86400 * 365 * 2 * time.Second ), 63 | IsCA: false, 64 | DNSNames: dns_list, 65 | MaxPathLen: 0, 66 | SubjectKeyId: []byte{ 1, 2, 3, 4 }, 67 | KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, 68 | } 69 | 70 | cert_bytes, err := x509.CreateCertificate( rand.Reader, &template, &template, &rsa_key.PublicKey, rsa_key ) 71 | if err != nil { 72 | return 73 | } 74 | 75 | fd, err := os.Create( *cfname ) 76 | if err != nil { 77 | return 78 | } 79 | 80 | pem.Encode( fd, &pem.Block{ Type: "CERTIFICATE", Bytes: cert_bytes} ) 81 | fd.Close( ) 82 | 83 | fd, err = os.OpenFile( *kfname, os.O_WRONLY | os.O_CREATE | os.O_TRUNC, 0600 ) 84 | if err != nil { 85 | return 86 | } 87 | 88 | pem.Encode( fd, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey( rsa_key )}) 89 | fd.Close() 90 | 91 | 92 | return 93 | } 94 | -------------------------------------------------------------------------------- /test/test_suffix.go: -------------------------------------------------------------------------------- 1 | // vi: sw=4 ts=4: 2 | /* 3 | --------------------------------------------------------------------------- 4 | Copyright (c) 2013-2015 AT&T Intellectual Property 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | --------------------------------------------------------------------------- 18 | */ 19 | 20 | 21 | package main 22 | 23 | import ( 24 | "fmt" 25 | "os" 26 | "strings" 27 | ) 28 | 29 | func add_phost_suffix( old_list *string, suffix *string ) ( *string ) { 30 | if suffix == nil || old_list == nil || *old_list == "" { 31 | return old_list 32 | } 33 | 34 | nlist := "" 35 | sep := "" 36 | 37 | htoks := strings.Split( *old_list, " " ) 38 | for i := range htoks { 39 | if htoks[i] != "" { 40 | if (htoks[i])[0:1] >= "0" && (htoks[i])[0:1] <= "9" { 41 | nlist += sep + htoks[i] // assume ip address, put on as is 42 | } else { 43 | if strings.Index( htoks[i], "." ) >= 0 { // fully qualified name 44 | dtoks := strings.SplitN( htoks[i], ".", 2 ) // add suffix after first node in the name 45 | nlist += sep + dtoks[0] + *suffix + "." + dtoks[1] 46 | } else { 47 | nlist += sep + htoks[i] + *suffix 48 | } 49 | } 50 | 51 | sep = " " 52 | } 53 | } 54 | 55 | return &nlist 56 | } 57 | 58 | func do_it( list string, suffix *string ) { 59 | 60 | ns := add_phost_suffix( &list, suffix ) 61 | fmt.Fprintf( os.Stderr, "gave: (%s)\ngot: (%s)\n\n", list, *ns ) 62 | } 63 | 64 | func main( ) { 65 | var suffix *string = nil 66 | 67 | fmt.Fprintf( os.Stderr, "These should all be the same out as went in:\n" ) 68 | do_it( "c2r3 c2r1 c1r6 o11r32 s3e4 charlie robert", suffix ) 69 | do_it( "c2r3.research.att.com c2r1.att.com c1r6.research.att.com o11r32.research.att.com s3e4.research.att.com charlie.research.att.com robert.research.att.com", suffix ) 70 | do_it( "c2r3 c2r1 c1r6 o11r32 s3e4 charlie robert.research.att.com", suffix ) 71 | do_it( "c2r3 c2r1 192.168.32.45 o11r32 9.23.4.16 charlie robert.research.att.com", suffix ) 72 | 73 | s := "-ops" 74 | fmt.Fprintf( os.Stderr, "=========\nThese should all have the suffix in the right place\n" ) 75 | suffix = &s 76 | do_it( "", suffix ) 77 | do_it( " ", suffix ) 78 | do_it( " ", suffix ) 79 | do_it( "c2r3 c2r1 c1r6 o11r32 s3e4 charlie robert", suffix ) 80 | do_it( "c2r3.research.att.com c2r1.att.com c1r6.research.att.com o11r32.research.att.com s3e4.research.att.com charlie.research.att.com robert.research.att.com", suffix ) 81 | do_it( "c2r3 c2r1 c1r6 o11r32 s3e4 charlie robert.research.att.com", suffix ) 82 | do_it( "c2r3 c2r1 192.168.32.45 o11r32 9.23.4.16 charlie robert.research.att.com", suffix ) 83 | do_it( "c2r3", suffix ) 84 | } 85 | -------------------------------------------------------------------------------- /token/tokenise_keep.go: -------------------------------------------------------------------------------- 1 | // vi: sw=4 ts=4: 2 | /* 3 | --------------------------------------------------------------------------- 4 | Copyright (c) 2013-2015 AT&T Intellectual Property 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | --------------------------------------------------------------------------- 18 | */ 19 | 20 | 21 | package token 22 | 23 | import ( 24 | "strings"; 25 | ) 26 | 27 | 28 | /* 29 | --------------------------------------------------------------------------------------- 30 | Mnemonic: tokenise_keep (split and keep separators as individual tokens) 31 | Date: 22 Apr 2012 32 | Author: E. Scott Daniels 33 | Mods: 01 May 2012 : Added escape character support 34 | 03 Feb 2015 : Removed unreachable code. 35 | 07 Jun 2018 : Remove unreachable code (keep vet happy) 36 | --------------------------------------------------------------------------------------- 37 | */ 38 | 39 | /* 40 | Tokensise_keep takes a string and slices it into tokens using the characters in sepchrs 41 | as the breaking points. The separation characters are returned as 42 | individual tokens in the list. Separater characters escaped with a backslant 43 | are NOT treated like separators. 44 | 45 | The return values are ntokens (int) and the list of tokens and separators. 46 | */ 47 | func Tokenise_keep( buf string, sepchrs string ) (int, []string) { 48 | var ( 49 | tokens []string; 50 | idx int; 51 | ) 52 | 53 | idx = 0; 54 | tokens = make( []string, 2048 ); 55 | 56 | sepchrs += "\\"; // escape character is a separator 57 | 58 | subbuf := buf; 59 | tokens[idx] = ""; // prime the first one 60 | for { 61 | if i := strings.IndexAny( subbuf, sepchrs ); i >= 0 { 62 | if subbuf[i:i+1] == "\\" { // next character is escaped 63 | tokens[idx] += subbuf[0:i]; // add everything before slant 64 | tokens[idx] += subbuf[i+1:i+2]; // add escaped char 65 | subbuf = subbuf[i+2:]; // advance past escaped char 66 | } else { 67 | if i > 0 { // characters before the sep, capture them 68 | tokens[idx] += subbuf[0:i]; // add everything before separator 69 | idx++; 70 | } else { 71 | if tokens[idx] != "" { // finish previous token 72 | idx++; 73 | } 74 | } 75 | 76 | tokens[idx] = subbuf[i:i+1]; // keep the sep as a separate token 77 | idx++; 78 | subbuf = subbuf[i+1:]; // advance past 79 | 80 | tokens[idx] = ""; // initialise next when advancing idx 81 | } 82 | } else { // no more seps, just complete the list with remaining stuff and return 83 | 84 | tokens[idx] = subbuf[:len( subbuf )]; // capture last bit in tokens and go 85 | return idx+1, tokens[0:idx+1]; 86 | } 87 | 88 | if len( subbuf ) < 1 { 89 | return idx, tokens[0:idx]; 90 | } 91 | 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /clike/atoi.go: -------------------------------------------------------------------------------- 1 | // vi: sw=4 ts=4: 2 | /* 3 | --------------------------------------------------------------------------- 4 | Copyright (c) 2013-2015 AT&T Intellectual Property 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | --------------------------------------------------------------------------- 18 | */ 19 | 20 | 21 | /* 22 | Mnemonic: clike.go: atoi 23 | Absrtract: a clike atoi, and varients, that don't error when it encounters 24 | a non-digit; returning 0 if there are no digits or if the number 25 | is not valid, and stopping at the first non-valid digit. This supports 26 | 0x or 0 values and should parse them stopping 27 | conversion at the first non-appropriate 'digit'. This also allows 28 | a lead +/-. 29 | 30 | There is an extension on the C functions... if the value is 31 | postfixed with M/K/G or m/k/g the return value will be 32 | 'expanded' accordingly with the capitalised values being 33 | powrs of 10 (e.g. MB) and the lower case indicating powers 34 | of 2 (e.g. MiB). 35 | 36 | Input can be either a string or a byte array 37 | */ 38 | 39 | package clike 40 | 41 | 42 | /* 43 | Convert a string or an array of bytes into a 64 bit integer. 44 | */ 45 | func Atoi64( objx interface{} ) (int64) { 46 | 47 | v := Atoll( objx ) 48 | return v 49 | } 50 | 51 | /* 52 | Convert a string or an array of bytes into a 32 bit integer. 53 | */ 54 | func Atoi32( objx interface{} ) (int32) { 55 | 56 | v := Atoll( objx ) 57 | return int32( v ) 58 | } 59 | 60 | /* 61 | Convert a string or an array of bytes into a 16 bit integer. 62 | */ 63 | func Atoi16( objx interface{} ) (int16) { 64 | 65 | v := Atoll( objx ) 66 | return int16( v ) 67 | } 68 | 69 | /* 70 | Convert a string or an array of bytes into a default sized integer. 71 | */ 72 | func Atoi( objx interface{} ) (int) { 73 | 74 | v := Atoll( objx ) 75 | return int( v ) 76 | } 77 | 78 | /* 79 | Convert a string or an array of bytes into an unsigned integer. 80 | */ 81 | func Atou( objx interface{} ) (uint) { 82 | 83 | v := Atoull( objx ) 84 | return uint( v ) 85 | } 86 | 87 | /* 88 | Convert a string or an array of bytes into a 64 bit integer. 89 | */ 90 | func Atou64( objx interface{} ) (uint64) { 91 | 92 | v := Atoull( objx ) 93 | return v 94 | } 95 | 96 | /* 97 | Convert a string or an array of bytes into a 32 bit integer. 98 | */ 99 | func Atou32( objx interface{} ) (uint32) { 100 | 101 | v := Atoull( objx ) 102 | return uint32( v ) 103 | } 104 | 105 | /* 106 | Convert a string or an array of bytes into a 16 bit integer. 107 | */ 108 | func Atou16( objx interface{} ) (uint16) { 109 | 110 | v := Atoull( objx ) 111 | return uint16( v ) 112 | } 113 | -------------------------------------------------------------------------------- /jsontools/jsoncache.go: -------------------------------------------------------------------------------- 1 | // vi: sw=4 ts=4: 2 | /* 3 | --------------------------------------------------------------------------- 4 | Copyright (c) 2013-2015 AT&T Intellectual Property 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | --------------------------------------------------------------------------- 18 | */ 19 | 20 | 21 | /* 22 | 23 | Mnemonic: jsoncache 24 | Abstract: Builds a cache of bytes returning an array of 'complete json' when available. 25 | Caller can use to read packets from the wire until a complete structure is 26 | discovered. If the first part of a second json struct is in the last packet 27 | it remains in the cache after the first complete struct is returned. 28 | Date: 16 December 2013 29 | Author: E. Scott Daniels 30 | */ 31 | 32 | package jsontools 33 | 34 | import ( 35 | "fmt" 36 | "os" 37 | ) 38 | 39 | // --------------------- public ---------------------------------------------------------------------- 40 | 41 | type Jsoncache struct { 42 | buf []byte 43 | open int 44 | nxt int // starting point for next get check 45 | len int 46 | insrt int // insertion point 47 | } 48 | 49 | func Mk_jsoncache( ) ( jc *Jsoncache ) { 50 | jc = &Jsoncache { 51 | open: 0, 52 | nxt: 0, 53 | insrt: 0, 54 | } 55 | 56 | jc.buf = make( []byte, 8192 ) 57 | 58 | return 59 | } 60 | 61 | /* 62 | Adds a buffer of bytes to the current cache. 63 | */ 64 | func ( jc *Jsoncache ) Add_bytes( newb []byte ) { 65 | var ( 66 | ) 67 | 68 | if jc == nil || len( jc.buf ) == 0 { 69 | fmt.Fprintf( os.Stderr, "ERR: jsoncache: object not allocated correctly\n" ) 70 | return 71 | } 72 | 73 | for ; len( newb ) + jc.insrt > len( jc.buf ) ; { 74 | bigger := make( []byte, len( jc.buf ) * 2 ) 75 | copy( bigger, jc.buf ) 76 | jc.buf = bigger 77 | } 78 | 79 | for i := range newb { // append new bytes 80 | jc.buf[jc.insrt] = newb[i] 81 | jc.insrt++ 82 | } 83 | 84 | return 85 | } 86 | 87 | /* 88 | Returns a blob of complete json if it exists in the cache; nil otherwise 89 | */ 90 | func (jc *Jsoncache) Get_blob( ) ( blob []byte ) { 91 | var ( 92 | encountered_brace bool = false 93 | ) 94 | 95 | blob = nil 96 | 97 | for ; jc.nxt < jc.insrt; jc.nxt++ { 98 | switch( jc.buf[jc.nxt] ) { 99 | case '{': 100 | jc.open++ 101 | encountered_brace = true 102 | 103 | case '}': 104 | jc.open-- 105 | encountered_brace = true 106 | } 107 | 108 | if encountered_brace && jc.open == 0 { 109 | blob = jc.buf[0:jc.nxt+1] 110 | endbuf := jc.buf[jc.nxt+1:] 111 | 112 | jc.buf = make( []byte, len( jc.buf ) ) 113 | copy( jc.buf, endbuf ) 114 | jc.insrt = jc.insrt - (jc.nxt+1) 115 | jc.nxt = 0 116 | return 117 | } 118 | } 119 | 120 | return; 121 | } 122 | 123 | 124 | -------------------------------------------------------------------------------- /ostack/ostack_dump.go: -------------------------------------------------------------------------------- 1 | // vi: sw=4 ts=4: 2 | /* 3 | --------------------------------------------------------------------------- 4 | Copyright (c) 2013-2015 AT&T Intellectual Property 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | --------------------------------------------------------------------------- 18 | */ 19 | 20 | 21 | /* 22 | ------------------------------------------------------------------------------------------------ 23 | Mnemonic: ostack_dump 24 | Abstract: Some generic debugging functions to execute some ostack api call and 25 | dump the resulting json to standard error. 26 | 27 | 28 | Date: 17 June 2014 29 | Author: E. Scott Daniels 30 | Mod: 03 Feb 2015 - Fixed fprintf() statement -- too many %s 31 | 24 Jun 2015 - Moved Dump_json from network.go to this module. 32 | ------------------------------------------------------------------------------------------------ 33 | */ 34 | 35 | package ostack 36 | 37 | import ( 38 | "bytes" 39 | "fmt" 40 | "os" 41 | ) 42 | 43 | // ------------------------------------------------------------------------------ 44 | 45 | /* 46 | Runs a command and dumps the json to stderr -- debugging mostly. 47 | */ 48 | func (o *Ostack) Dump_cmd_response( cmd string ) ( error ) { 49 | 50 | err := o.Validate_auth() // reauthorise if needed 51 | if err != nil { 52 | fmt.Fprintf( os.Stderr, "unable to validate creds: %s\n", err ); 53 | return err 54 | } 55 | 56 | body := bytes.NewBufferString( "" ) 57 | 58 | //url := fmt.Sprintf( "%sv2.0/tokens/%s", *o.host, *token ) // this returns token information if token is valid 59 | url := fmt.Sprintf( "%s%s", *o.host, cmd ) 60 | jdata, _, err := o.Send_req( "GET", &url, body ); 61 | 62 | if err != nil { 63 | return err 64 | } 65 | fmt.Fprintf( os.Stderr, "json= %s\n", jdata ); 66 | return nil 67 | } 68 | 69 | func (o *Ostack) Dump_json( uurl string ) ( err error ) { 70 | var ( 71 | jdata []byte // raw json response data 72 | ) 73 | 74 | if o == nil { 75 | err = fmt.Errorf( "net_subnets: openstack creds were nil" ) 76 | return 77 | } 78 | 79 | err = o.Validate_auth() // reauthorise if needed 80 | if err != nil { 81 | return 82 | } 83 | 84 | if o.nhost == nil || *o.nhost == "" { 85 | err = fmt.Errorf( "no network host url to query %s", o.To_str() ) 86 | return 87 | } 88 | 89 | jdata = nil 90 | body := bytes.NewBufferString( "" ) 91 | 92 | //url := fmt.Sprintf( "%s/v2.0/subnets", *o.nhost ) // nhost is the host where the network service is running 93 | //url := fmt.Sprintf( "%s/%s", *o.nhost, uurl ) // nhost is the host where the network service is running 94 | url := fmt.Sprintf( "%s/%s", *o.chost, uurl ) // nhost is the host where the network service is running 95 | jdata, _, err = o.Send_req( "GET", &url, body ) 96 | 97 | if err != nil { 98 | fmt.Fprintf( os.Stderr, "error: %s\n", err ) 99 | return 100 | } 101 | 102 | fmt.Fprintf( os.Stderr, "json= %s\n", jdata ) 103 | 104 | return 105 | } 106 | -------------------------------------------------------------------------------- /bleater/sheep_herder.go: -------------------------------------------------------------------------------- 1 | // vi: sw=4 ts=4: 2 | /* 3 | --------------------------------------------------------------------------- 4 | Copyright (c) 2013-2015 AT&T Intellectual Property 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | --------------------------------------------------------------------------- 18 | */ 19 | 20 | 21 | /* 22 | 23 | Mnemonic: sheep_herder 24 | Abstract: A simple go routine to cause the log file that sheep are writing to to be 25 | rolled now and again. 26 | 27 | Date: 30 April 2014 28 | Author: E. Scott Daniels 29 | 30 | Mods: 16 Jul 2014 - Corrected bug that was causing log file name to be gnerated 31 | based on local time and not zulu time. 32 | 02 Jul 2014 - Corrected typos in Baa message. 33 | */ 34 | 35 | package bleater 36 | 37 | import ( 38 | "fmt" 39 | "time" 40 | ) 41 | 42 | /* 43 | Generate a new logfile name based on the current date, the log directory passed in 44 | and the cycle period. 45 | 46 | The file name created will have the syntax: 47 | /.log. 48 | 49 | where is taken from the bleater. 50 | 51 | If period is >86400 seconds, then the file name is day based. 52 | If period is <86400 but > 3600 seconds, then file is based on hour, else 53 | the file includes the minute and is of the general form yyyymmddhhmm. 54 | */ 55 | func (b *Bleater) Mk_logfile_nm( ldir *string, period int64 ) ( *string ) { 56 | ref := "200601021504" 57 | 58 | if period >= 86400 { 59 | ref = "20060102" 60 | 61 | } else { 62 | if period >= 3600 { 63 | ref = "2006010215" 64 | } 65 | } 66 | 67 | s := fmt.Sprintf( "%s/%s.log.%s", *ldir, b.pfx, time.Now().UTC().Format( ref ) ) // 20060102 is Go's reference date and allows us to specify how we want to see it 68 | return &s 69 | } 70 | 71 | /* 72 | Go routine that manages the rolling over of the bleater log. 73 | We assume there is already a log open. Period defines when the log is rolled (e.g. 300 causes 74 | the log to be rolled on 5 minute boundaries, 3600 hour boundaries, and 86400 at midnight). 75 | */ 76 | func (b *Bleater) Sheep_herder( ldir *string, period int64 ) { 77 | b.Baa( 1, "sheep herder started with a period of %ds, first log roll in %d seconds", period, period - (time.Now().Unix() % period ) ) 78 | 79 | if period < 60 { // dictate some sanity 80 | period = 60 81 | } 82 | 83 | for { 84 | lfn := b.Mk_logfile_nm( ldir, period ) 85 | b.Baa( 0, "herder is rolling the log into: %s", *lfn ) 86 | 87 | err := b.Append_target( *lfn, true ) // create the next directory 88 | if err != nil { 89 | b.Baa( 0, "ERR: unable to roll the log to %s: %s", *lfn, err ) 90 | } else { 91 | b.Baa( 0, "herder rolled the log, writing starts to this log" ) 92 | } 93 | 94 | time.Sleep( 5 * time.Second ) // let some time pass before we recalc midnight so as to not loop during the same second 95 | now := time.Now().Unix() 96 | time.Sleep( time.Duration( (period - (now % period )) ) * time.Second ) // should wake us at midnight 97 | } 98 | } 99 | 100 | -------------------------------------------------------------------------------- /clike/atof.go: -------------------------------------------------------------------------------- 1 | // vi: sw=4 ts=4: 2 | /* 3 | --------------------------------------------------------------------------- 4 | Copyright (c) 2013-2015 AT&T Intellectual Property 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | --------------------------------------------------------------------------- 18 | */ 19 | 20 | 21 | /* 22 | Mnemonic: clike.go: atof 23 | Absrtract: a clike atof that doesn't error when it encounters 24 | a non-digit; returning 0 if there are no digits. The input 25 | (string or buffer) is expected to be base 10 with optional leading 26 | zeros, an optional trailing decimal and an optional fraction 27 | following the decimal. This also allows a lead +/-. 28 | 29 | There is an extension on the C functions... if the value is 30 | postfixed with M/K/G or m/k/g the return value will be 31 | 'expanded' accordingly with the capitalised values being 32 | powrs of 10 (e.g. MB) and the lower case indicating powers 33 | of 2 (e.g. MiB). 34 | 35 | Input can be either a string or a byte array 36 | Author: E. Scott Daniels 37 | Date: October 12 2013 38 | */ 39 | 40 | package clike 41 | 42 | import ( 43 | "strconv" ; 44 | ) 45 | 46 | 47 | /* 48 | Atof accepts a string or an array of bytes ([]byte) and converts the characters 49 | into a float64 value. The input may be postfixed with either any of the following 50 | characters which cause the value to be expanded: 51 | m,k,g - powers of two expansion. e.g. 10m expands to 10 * 1024 * 1024. 52 | M,K,G - powers of ten expansion. e.g. 10M expands to 10000000 53 | 54 | Unlike the Go string functions, this stops parsing at the first non-digit in the 55 | same manner that the Clib functions do. 56 | */ 57 | func Atof( objx interface{} ) (v float64) { 58 | var ( 59 | i int; 60 | buf []byte; 61 | ) 62 | 63 | v = 0; // ensure all early returns have a value of 0 64 | 65 | if objx == nil { 66 | return 67 | } 68 | 69 | switch objx.( type ) { 70 | case []byte: 71 | buf = objx.([]byte); // type assertion seems backwards doesn't it? 72 | case string: 73 | buf = []byte( objx.(string) ); 74 | default: 75 | return; // who knows, but it doesn't convert 76 | } 77 | 78 | if len( buf ) < 1 { 79 | return; 80 | } 81 | 82 | i = 0; 83 | if buf[i] == '-' || buf[i] == '+' { 84 | i++ 85 | } 86 | for ; i < len(buf) && ((buf[i] >= '0' && buf[i] <= '9') || buf[i] == '.'); i++ {} // find last valid character for conversion 87 | 88 | if i > 0 { 89 | v, _ = strconv.ParseFloat( string( buf[0:i] ), 64 ); 90 | } 91 | 92 | if i < len( buf ) { 93 | switch string( buf[i:] ) { 94 | case "M", "MB": 95 | v *= 1000000; 96 | 97 | case "G", "GB": 98 | v *= 1000000000; 99 | 100 | case "K", "KB": 101 | v *= 1000; 102 | 103 | case "m", "MiB": 104 | v *= 1048576; 105 | 106 | case "g", "GiB": 107 | v *= 1073741824; 108 | 109 | case "k", "KiB": 110 | v *= 1024; 111 | 112 | default: break; 113 | } 114 | } 115 | 116 | return; 117 | } 118 | 119 | -------------------------------------------------------------------------------- /ssh_broker/rsync.go: -------------------------------------------------------------------------------- 1 | // vi: sw=4 ts=4: 2 | /* 3 | --------------------------------------------------------------------------- 4 | Copyright (c) 2013-2015 AT&T Intellectual Property 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | --------------------------------------------------------------------------- 18 | */ 19 | 20 | /* 21 | Mnemonic: rsync.go 22 | Abstract: This module contains the support to allow an rsync command to be run on the 23 | localhost for each new remote host connection. The rsync connections are 24 | run "outside" of the ssh environment that is managed by this package. 25 | 26 | Author: E. Scott Daniels 27 | Date: 20 January 2015 28 | 29 | Mods: 13 Apr 2015 - Added explicit ssh command for rsync to use. 30 | 07 Jun 2018 - fix printf %s/v bug. 31 | 32 | CAUTION: This package reqires go 1.3.3 or later. 33 | */ 34 | 35 | package ssh_broker 36 | 37 | import ( 38 | "fmt" 39 | "os" 40 | 41 | "github.com/att/gopkgs/extcmd" 42 | ) 43 | 44 | /* 45 | Add_rsync accepts the setup for rsync commands. We assume src is one or more 46 | source files and dest_dir is the name of the directory on the remote 47 | host. When the rsync command is actually run, the two strings are 48 | combined (unchanged) with user@host: inserted just before the dest 49 | directory. 50 | */ 51 | func ( b *Broker ) Add_rsync( src *string, dest_dir *string ) { 52 | if b != nil { 53 | b.rsync_src = src 54 | 55 | b.rsync_dir = dest_dir 56 | } 57 | } 58 | 59 | func ( b *Broker ) Rm_rsync( ) { 60 | if b != nil { 61 | b.rsync_src = nil 62 | b.rsync_dir = nil 63 | } 64 | } 65 | 66 | 67 | // ---- private functions ----------------------------------------------------------------- 68 | 69 | /* 70 | Actually run the rsync command. If verbose is true, then output is written 71 | to stderr, otherwise it is ignored. Error is returned and will be nil if 72 | the command successfully executed. 73 | 74 | The -e option is used to force rsync to use ssh with a nokey checking option 75 | so that we don't get caught by the reinstall of a machine which changes its 76 | key and would possibly block our rsync attempt. 77 | */ 78 | func ( b *Broker ) synch_host( host *string ) ( err error ) { 79 | 80 | if b == nil || b.rsync_src == nil || b.rsync_dir == nil || host == nil { 81 | if b.verbose { 82 | fmt.Fprintf( os.Stderr, "ssh_broker: synch_host giving up b is nil: %v; src is nil %v dir is nil %v; host is nil %v\n", b == nil, b.rsync_src == nil, b.rsync_dir == nil , host == nil ) 83 | } 84 | return 85 | } 86 | 87 | cmd := fmt.Sprintf( `rsync -e "ssh -o StrictHostKeyChecking=no" %s %s@%s:%s`, *b.rsync_src, b.config.User, *host, *b.rsync_dir ) 88 | 89 | verbose := b.verbose 90 | if verbose { 91 | fmt.Fprintf( os.Stderr, "synch_host: %s\n", cmd ) 92 | } 93 | 94 | stdout, stderr, err := extcmd.Cmd2strings( cmd ) 95 | if err != nil { 96 | fmt.Fprintf( os.Stderr, "ssh-broker: unable to rsync for host %s: %s\n", *host, err ) 97 | fmt.Fprintf( os.Stderr, "ssh-broker: command: %s\n", cmd ) 98 | verbose = true 99 | } 100 | if verbose { 101 | for i := range stdout { 102 | fmt.Fprintf( os.Stderr, "%s\n", stdout[i] ) 103 | } 104 | 105 | for i := range stderr { 106 | fmt.Fprintf( os.Stderr, "%s\n", stderr[i] ) 107 | } 108 | } 109 | 110 | return 111 | } 112 | -------------------------------------------------------------------------------- /uuid/uuid.go: -------------------------------------------------------------------------------- 1 | // vi: ts=4 sw=4: 2 | 3 | /* 4 | Copyright (c) 2013-2015 AT&T Intellectual Property 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | /* 20 | Mnemonic: uuid.go 21 | Abstract: UUID generation support. Currently supports generating 22 | version 4 (random) UUIDs. 23 | 24 | Based on the description in section 4.4 of: 25 | https://tools.ietf.org/html/rfc4122 26 | 27 | Author: E. Scott Daniels 28 | Date: 12 Aug 2015 29 | 30 | Mods: 07 Jan 2015 - Corrected potential nil pointer issue in Plain_string() 31 | */ 32 | 33 | package uuid 34 | 35 | import ( 36 | "crypto/rand" 37 | "fmt" 38 | ) 39 | 40 | /* 41 | Timelow med hi/v clock node 42 | hi lo 43 | 0 4 6 8 a 44 | xx xx xx xx - xx xx - xx xx - xx xx - xx xx xx xx xx xx 45 | | | 46 | | |--- 01.. .... 47 | | 48 | |--- 0100 .... 49 | 50 | From the RFC: 51 | The algorithm is as follows: 52 | 53 | o Set the two most significant bits (bits 6 and 7) of the 54 | clock_seq_hi_and_reserved to zero and one, respectively. 55 | 56 | o Set the four most significant bits (bits 12 through 15) of the 57 | time_hi_and_version field to the 4-bit version number from 58 | Section 4.1.3. 59 | From 4.1.3: 0 1 0 0 (version 4) 60 | 61 | o Set all the other bits to randomly (or pseudo-randomly) chosen 62 | values. 63 | */ 64 | 65 | type Uuid struct { 66 | buf []byte 67 | } 68 | 69 | /* 70 | Take a buffer of bytes and return a uuid struct. 71 | */ 72 | func bytes2uuid( buf []byte ) ( *Uuid, error ) { 73 | 74 | if len( buf ) != 16 { return nil, fmt.Errorf( "invalid buf size (expected 16)" ) } 75 | u := &Uuid{ 76 | buf: buf[:16], 77 | } 78 | 79 | return u, nil 80 | } 81 | 82 | 83 | /* 84 | Returns a string constructed from the uuid struct. 85 | Implements stringer. 86 | */ 87 | func (u *Uuid) String( ) ( string ) { 88 | if u == nil { 89 | return "" 90 | } 91 | 92 | return fmt.Sprintf( "%x-%x-%x-%x-%x", u.buf[0:4], u.buf[4:6], u.buf[6:8], u.buf[8:10], u.buf[10:16] ) 93 | } 94 | 95 | /* 96 | Returns a string representing the UUID without any separating dashes. 97 | */ 98 | func (u *Uuid) Plain_string( ) ( string ) { 99 | if u == nil { 100 | return "" 101 | } 102 | 103 | return fmt.Sprintf( "%x", u.buf[:16] ) 104 | } 105 | 106 | 107 | /* 108 | Compares two uuids and returns true if they either represent the same struct 109 | (both point to same), or if the contents of each are identical. 110 | */ 111 | func (u *Uuid) Equals( u2 *Uuid ) ( bool ) { 112 | if u == nil && u2 == nil { 113 | return true 114 | } 115 | 116 | 117 | if u == nil || u2 == nil { 118 | return false 119 | } 120 | 121 | if u == u2 { 122 | return true 123 | } 124 | 125 | for i := range u.buf { 126 | if u.buf[i] != u2.buf[i] { 127 | return false 128 | } 129 | } 130 | 131 | return true 132 | } 133 | 134 | 135 | // ----- specific version functions ------ 136 | 137 | /* 138 | Generate a byte buffer with a random uuid. 139 | */ 140 | func Mk_v4( ) ( *Uuid, error ) { 141 | buf := make( []byte, 16 ) 142 | 143 | _, err := rand.Read( buf ) // this is crypto secure 144 | if err != nil { 145 | return nil, err 146 | } 147 | 148 | buf[8] &= 0x3f 149 | buf[8] |= 0x40 // flip 01 on in byte 8 150 | 151 | buf[6] &= 0x0f 152 | buf[6] |= 0x40 // flip 0100 on in byte 6 153 | 154 | return bytes2uuid( buf ) 155 | } 156 | 157 | /* 158 | Same as calling Mk_v4(); mirrors the name in the old (deprecated) 159 | code.google uuid code. 160 | */ 161 | func NewRandom() ( u *Uuid ) { 162 | u, _ = Mk_v4() 163 | return u 164 | } 165 | 166 | -------------------------------------------------------------------------------- /transform/array_test.go: -------------------------------------------------------------------------------- 1 | // vi: sw=4 ts=4: 2 | /* 3 | --------------------------------------------------------------------------- 4 | Copyright (c) 2013-2015 AT&T Intellectual Property 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | --------------------------------------------------------------------------- 18 | */ 19 | 20 | 21 | /* 22 | Mnemonic: array_test.go 23 | Absrtract: Test the ability to populate an array in a subobject. 24 | Date: 08 February 2017 25 | Author: E. Scott Daniels 26 | */ 27 | 28 | package transform_test 29 | 30 | import ( 31 | "fmt" 32 | "os" 33 | //"strings" 34 | "testing" 35 | 36 | "github.com/att/gopkgs/transform" 37 | "github.com/att/gopkgs/clike" 38 | ) 39 | 40 | /* 41 | Sub object embedded with an interface that was saved as an array 42 | rather than a simple value. 43 | */ 44 | type Inner_thing struct { 45 | Name string `Bar:"_"` 46 | Data interface{} `Bar:"_"` 47 | 48 | // todo -- handle this too 49 | //Data []string `Bar:"_"` 50 | } 51 | 52 | /* 53 | Array test thing; main object. 54 | */ 55 | type AT_thing struct { 56 | Bar_thing_s string `Bar:"_"` 57 | Itobj* Inner_thing `Bar:"_"` 58 | } 59 | 60 | /* 61 | Create an object to convert to a map. 62 | */ 63 | func mk_thing( ) ( *AT_thing ) { 64 | t := &AT_thing { 65 | Bar_thing_s: "hello world, I am a thing", 66 | } 67 | 68 | t.Itobj = &Inner_thing{ Name: "I'm the inner thing", } 69 | 70 | a := make( []string, 10 ) 71 | a[0] = "string 0" 72 | a[1] = "string 1" 73 | a[2] = "string 2" 74 | a[3] = "string 3" 75 | t.Itobj.Data = a 76 | // CAUTION: test pass is based on not finding anything but empty string in 4-9 77 | 78 | return t 79 | } 80 | 81 | /* 82 | Should generate a map. 83 | */ 84 | func TestIting2Map( t *testing.T ) { 85 | ok := true 86 | 87 | it := mk_thing() 88 | fmt.Fprintf( os.Stderr, "array_test: Inner AT_thing Testing\n" ) 89 | m := transform.Struct_to_map( it, "Bar" ) 90 | for k, v := range m { 91 | fmt.Fprintf( os.Stderr, "AT_thing map: %s (%v)\n", k, v ) // uncomment to dump the map 92 | } 93 | 94 | if( len( m ) != 14 ) { 95 | fmt.Fprintf( os.Stderr, "array_test: [FAIL] number of elements in the map not 14; found %d\n", len( m ) ) 96 | t.Fail() 97 | } else { 98 | fmt.Fprintf( os.Stderr, "array_test: [OK] number of elements in the map is correct\n" ) 99 | } 100 | 101 | if( clike.Atoi( m["Itobj/Data.cap"] ) != 10 ) { 102 | fmt.Fprintf( os.Stderr, "array_test: [FAIL] number of inner elements in the map not 10; found %s\n", m["Itobj/Data.cap"] ) 103 | t.Fail() 104 | ok = false 105 | } else { 106 | fmt.Fprintf( os.Stderr, "array_test: [OK] number of inner elements in the map is correct\n" ) 107 | } 108 | 109 | fmt.Fprintf( os.Stderr, "\n" ) 110 | nit := &AT_thing{} // empty struct to populate 111 | transform.Map_to_struct( m, nit, "Bar" ) // fill it in 112 | 113 | a, ok := nit.Itobj.Data.( []string ) 114 | if ok { 115 | if( len( a ) != 10 ) { 116 | fmt.Fprintf( os.Stderr, "array_test: [FAIL] len of populated inner data array is not 10: %d\n", len( a ) ) 117 | t.Fail() 118 | ok = false 119 | } else { 120 | fmt.Fprintf( os.Stderr, "array_test: [OK] len of populated inner data array is correct\n" ) 121 | 122 | for i := 0; i < 10; i++ { 123 | if i > 3 && a[i] != "" { 124 | ok = false 125 | fmt.Fprintf( os.Stderr, "array_test: [FAIL] data[4+] should be empty and isn't @i=%d\n", i ) 126 | t.Fail() 127 | } 128 | fmt.Fprintf( os.Stderr, "array_test: [INFO] data[%d] = (%s)\n", i, a[i] ) 129 | } 130 | } 131 | } else { 132 | ok = false 133 | fmt.Fprintf( os.Stderr, "array_test: [FAIL] data didn't produce an array of strings\n" ) 134 | t.Fail() 135 | } 136 | 137 | if ok { 138 | fmt.Fprintf( os.Stderr, "PASS: array_test: done\n\n" ) 139 | } else { 140 | fmt.Fprintf( os.Stderr, "FAIL: array_test: done\n\n" ) 141 | } 142 | 143 | } 144 | 145 | -------------------------------------------------------------------------------- /ipc/msgrtr/event.go: -------------------------------------------------------------------------------- 1 | // vi: sw=4 ts=4: 2 | /* 3 | --------------------------------------------------------------------------- 4 | Copyright (c) 2013-2015 AT&T Intellectual Property 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | --------------------------------------------------------------------------- 18 | */ 19 | 20 | 21 | /* 22 | 23 | Mnemonic: event.go 24 | Abstract: Event struct and related functions. The event struct is organised 25 | with publically visible fields to allow for json unmarshalling and 26 | to make it easier for the listener to process. There are some 27 | private fields which are visable only to this package (e.g. the 28 | related data block for replies). 29 | Date: 30 Oct 2015 30 | Author: E. Scott Daniels 31 | 32 | */ 33 | 34 | package msgrtr 35 | 36 | import ( 37 | "fmt" 38 | "sync" 39 | "github.com/att/gopkgs/ipc" 40 | ) 41 | 42 | 43 | /* 44 | An event expected on a POST request. Multiple events may be bundled 45 | int a single data block as an array. 46 | */ 47 | type Event struct { 48 | Event_type string // dot separated 'path' or band (e.g. network.router.add) 49 | Ack bool // true an ack is expected by a listener 50 | Payload map[string]interface{} // event message content 51 | 52 | // ------ private fields ---------------- 53 | db *Data_block // reference back for responses 54 | msg string // ack string to send 55 | ack_sent bool // only allow one ack message per event 56 | mu *sync.Mutex // replies must have the lock 57 | } 58 | 59 | /* 60 | Send the event to all who have registered in the given gallary. 61 | */ 62 | func ( e *Event ) bcast( gallery *audience ) ( acks_needed int, err error ) { 63 | acks_needed = 0 64 | 65 | sent := gallery.bcast( e, e.Event_type ) 66 | if e.Ack { 67 | acks_needed++ 68 | if ! sent { 69 | err = fmt.Errorf( "acks needed and no listener" ) 70 | } 71 | } 72 | 73 | return acks_needed, err 74 | } 75 | 76 | /* 77 | Adds a reference to the controlling data_block so that we have ack and writer 78 | info if we need to send some response. 79 | */ 80 | func ( e *Event ) add_db( db *Data_block ) { 81 | e.db = db 82 | } 83 | 84 | /* 85 | Because this class is instanced by unmarshall, it isn't initialised with the goodies we need 86 | so we provide the means for things like the mutex. 87 | */ 88 | func ( e *Event ) add_mutex() { 89 | e.mu = &sync.Mutex{} 90 | } 91 | 92 | func ( e *Event ) get_db( ) ( *Data_block ) { 93 | return e.db 94 | } 95 | 96 | // ------------ event public ---------------------------------------------------------------------------- 97 | 98 | /* 99 | Returns the currently formatted message. 100 | */ 101 | func ( e *Event ) Get_msg( ) ( string ) { 102 | return e.msg 103 | } 104 | 105 | /* 106 | Path returns the path of this event. 107 | */ 108 | func ( e *Event ) Path() ( string ) { 109 | return e.Event_type 110 | } 111 | 112 | /* 113 | Reply sends a reply back to the http requestor. This is a wrapper that puts a 114 | request on the dispatcher queue so that we serialise the access to the underlying 115 | data block. Status is presumed to be OK or ERROR or somesuch. msg is any 116 | string that is a 'commment' and data is json or other data (not quoted in the 117 | output). 118 | */ 119 | func ( e *Event ) Reply( state string, msg string, data string ) { 120 | e.mu.Lock() 121 | if e.ack_sent { 122 | e.mu.Unlock() 123 | return 124 | } 125 | e.ack_sent = true // set now then release the lock; no need to hold others while we write 126 | e.mu.Unlock() 127 | 128 | if data != "" { 129 | e.msg = fmt.Sprintf( `{ "endstate": { "status": %q, "comment": %q, "data": %s} }`, state, msg, data ) 130 | } else { 131 | e.msg = fmt.Sprintf( `{ "endstate": { "status": %q, "comment": %q } }`, state, msg ) 132 | } 133 | 134 | cmsg := ipc.Mk_chmsg() 135 | cmsg.Send_req( disp_ch, nil, SEND_ACK, e, nil ) // queue the event for a reply 136 | } 137 | 138 | /* 139 | String returns a string describing the instance of the structure. 140 | */ 141 | func ( e *Event ) String( ) ( string ) { 142 | return fmt.Sprintf( "%s ack=%v", e.Event_type, e.Ack ) 143 | } 144 | 145 | 146 | -------------------------------------------------------------------------------- /clike/clike_test.go: -------------------------------------------------------------------------------- 1 | // vi: sw=4 ts=4: 2 | /* 3 | --------------------------------------------------------------------------- 4 | Copyright (c) 2013-2015 AT&T Intellectual Property 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | --------------------------------------------------------------------------- 18 | */ 19 | 20 | /* 21 | simple testing of the clike things 22 | */ 23 | 24 | package clike_test 25 | 26 | import ( 27 | "testing" 28 | "github.com/att/gopkgs/clike" 29 | ) 30 | 31 | func TestAtoll ( t *testing.T ) { // must use bloody camel case to be recognised by go testing 32 | var ( 33 | iv interface{}; // use interface to ensure that the type coming back is 64 bit 34 | v int64; 35 | ) 36 | 37 | iv = clike.Atoll( "123456" ); 38 | switch iv.( type ) { 39 | case int64: 40 | break; 41 | 42 | default: 43 | t.Errorf( "atoll() did not return int64, no other atoll() tests executed" ); 44 | return; 45 | } 46 | 47 | if iv.(int64) != 123456 { 48 | t.Errorf( "atoll( '123456' did not return 123456, got: %d", iv.(int64) ); 49 | } 50 | 51 | v = clike.Atoll( "0x8000" ); 52 | if v != 0x8000 { 53 | t.Errorf( "atoll( '0x8000' ) did not return 0x8000" ); 54 | } 55 | 56 | v = clike.Atoll( "foo" ); 57 | if v != 0 { 58 | t.Errorf( "atoll( \"foo\" ) did not return 0 as expected." ); 59 | } 60 | 61 | v = clike.Atoll( "092" ); 62 | if v != 0 { 63 | t.Errorf( "atoll( \"092\" ) did not return 0 as expected." ); 64 | } 65 | 66 | v = clike.Atoll( "029" ); 67 | if v != 2 { 68 | t.Errorf( "atoll( \"029\" ) did not return 2 as expected." ); 69 | } 70 | 71 | s := "1234" 72 | v = clike.Atoll( &s ) 73 | if v != 1234 { 74 | t.Errorf( "atoll( &\"1234\" ) did not return 1234 as expected." ); 75 | } 76 | v = clike.Atoll( nil ) 77 | if v != 0 { 78 | t.Errorf( "atoll( nil ) did not return 0 as expected." ); 79 | } 80 | } 81 | 82 | func TestAtof( t *testing.T ) { 83 | var ( 84 | iv interface{}; 85 | ) 86 | iv = clike.Atof( "123.456" ); // pick up result as an interface so we can test type as well as value 87 | switch iv.( type ) { 88 | case float64: 89 | break; 90 | 91 | default: 92 | t.Errorf( "atof() did not return float64, no other atof() tests executed" ); 93 | return; 94 | } 95 | 96 | if iv.(float64) != 123.456 { 97 | t.Errorf( "atoll( '123.456' ) returned %.3f; did not return 123.456", iv.(float64) ); 98 | } 99 | } 100 | 101 | func TestAtoi32 ( t *testing.T ) { // must use bloody camel case to be recognised by go testing 102 | var ( 103 | iv interface{}; // use interface to ensure var type returned is int32 104 | v int32; 105 | ) 106 | 107 | iv = clike.Atoi32( "1256" ); 108 | 109 | switch iv.( type ) { 110 | case int32: 111 | break; 112 | 113 | default: 114 | t.Errorf( "atoi32() did not return int32, no other ato32() tests executed" ); 115 | return; 116 | } 117 | 118 | if iv.(int32) != 1256 { 119 | t.Fail(); 120 | } 121 | 122 | v = clike.Atoi32( "0x8000" ); 123 | if v != 0x8000 { 124 | t.Fail(); 125 | } 126 | } 127 | 128 | func TestAtoi16 ( t *testing.T ) { // must use bloody camel case to be recognised by go testing 129 | var ( 130 | iv interface{}; // use interface to ensure var type returned is int32 131 | v int16; 132 | ) 133 | 134 | iv = clike.Atoi16( "256" ); 135 | 136 | switch iv.( type ) { 137 | case int16: 138 | break; 139 | 140 | default: 141 | t.Errorf( "atoi16() did not return int16, no other atol16() tests executed" ); 142 | return; 143 | } 144 | 145 | if iv.(int16) != 256 { 146 | t.Fail(); 147 | } 148 | 149 | v = clike.Atoi16( "0x7def" ); 150 | if v != 0x7def { 151 | t.Fail(); 152 | } 153 | } 154 | 155 | 156 | func TestAtou16 ( t *testing.T ) { // must use bloody camel case to be recognised by go testing 157 | var ( 158 | iv interface{}; // use interface to ensure var type returned is int32 159 | v uint16; 160 | ) 161 | 162 | iv = clike.Atou16( "256" ); 163 | 164 | switch iv.( type ) { 165 | case uint16: 166 | break; 167 | 168 | default: 169 | t.Errorf( "atoi16() did not return uint16, no other atol16() tests executed" ); 170 | return; 171 | } 172 | 173 | if iv.(uint16) != 256 { 174 | t.Fail(); 175 | } 176 | 177 | v = clike.Atou16( "0x7def" ); 178 | if v != 0x7def { 179 | t.Fail(); 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /extcmd/extcmd.go: -------------------------------------------------------------------------------- 1 | // vi: sw=4 ts=4: 2 | /* 3 | --------------------------------------------------------------------------- 4 | Copyright (c) 2013-2015 AT&T Intellectual Property 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | --------------------------------------------------------------------------- 18 | */ 19 | 20 | /* 21 | Mnemonic: extcmd.go 22 | Abstract: Functions that execute 'external' commands and return a response structure, 23 | formatted json buffer, or write each record of the output onto a user 24 | channel (not implemented yet). 25 | Date: 04 May 2014 26 | Author: E. Scott Daniels 27 | Mods: 11 Jan 2015 - Fix bug that was improperly truncating the stdout stderr 28 | array (failing to limit it if the command could not be run). 29 | */ 30 | 31 | /* 32 | The extcmd package provides a simple interface which allows an external command to 33 | be invoked with the resulting stdout/err placed into a json formatted structure 34 | for the caller. The current limitation is 8192 output lines; if more are generated 35 | by the executed command they are truncated. 36 | */ 37 | package extcmd 38 | 39 | import ( 40 | "bufio"; 41 | "encoding/json" 42 | "os/exec" 43 | 44 | "github.com/att/gopkgs/token" 45 | ) 46 | 47 | 48 | type Response struct { 49 | Ctype string 50 | Rtype string 51 | Rstate int 52 | Rdata []string 53 | Edata []string 54 | } 55 | 56 | /* 57 | Accept and build a command, run it writing the output into a json buffer that is 58 | returned. Currently, output response is truncated at 8192 records. 59 | If a caller needs more, then it should redirect the output and parse the 60 | file rather than reading it all into core. 61 | */ 62 | func Cmd2strings( cbuf string ) ( rdata []string, edata []string, err error ) { 63 | ntokens, tokens := token.Tokenise_qpopulated( cbuf, " " ) 64 | if ntokens < 1 { 65 | return 66 | } 67 | 68 | cmd := &exec.Cmd{} // set the command up 69 | cmd.Path, _ = exec.LookPath( tokens[0] ) 70 | cmd.Args = tokens 71 | stdout, err := cmd.StdoutPipe() // create pipes for stderr/out 72 | srdr := bufio.NewReader( stdout ) // standard out reader 73 | 74 | stderr, err := cmd.StderrPipe() 75 | erdr := bufio.NewReader( stderr ) 76 | 77 | err = cmd.Start() 78 | if err != nil { 79 | rdata = make( []string, 1 ) 80 | edata = make( []string, 1 ) 81 | return 82 | } 83 | 84 | rdata = make( []string, 8192 ) 85 | edata = make( []string, 8192 ) 86 | 87 | i := 0 88 | for { 89 | buf, _, err := srdr.ReadLine( ) 90 | if err != nil { 91 | break 92 | } 93 | if i < len( rdata ) { // must read it all before calling wait, but don't overrun our buffer 94 | if len( buf ) > 0 { 95 | nb := make( []byte, len( buf ) ) 96 | copy( nb, buf ) 97 | rdata[i] = string( nb ) 98 | i++ 99 | } 100 | } 101 | } 102 | if i > 0 { 103 | rdata = rdata[0:i] // scale back the output to just what was used 104 | } else { 105 | rdata = rdata[0:1] 106 | } 107 | 108 | i = 0 109 | for { 110 | buf, _, err := erdr.ReadLine( ) 111 | if err != nil { 112 | break 113 | } 114 | if i < len( edata ) { // must read it all before calling wait, but don't overrun our buffer 115 | 116 | if len( buf ) > 0 { 117 | nb := make( []byte, len( buf ) ) 118 | copy( nb, buf ) 119 | edata[i] = string( nb ) 120 | i++ 121 | } 122 | } 123 | } 124 | if i > 0 { 125 | edata = edata[0:i] // scale back the output to just what was used 126 | } else { 127 | edata = edata[0:1] 128 | } 129 | 130 | err = cmd.Wait() 131 | 132 | return 133 | } 134 | 135 | /* 136 | Execute the command in cbuf and return a Response structure with the results. 137 | */ 138 | func Cmd2resp( cbuf string, rtype string ) ( rblock *Response, err error ) { 139 | rblock = &Response{ Ctype: "response", Rtype: rtype } 140 | 141 | rblock.Rdata, rblock.Edata, err = Cmd2strings( cbuf ) 142 | 143 | if err == nil { 144 | rblock.Rstate = 0 145 | } else { 146 | rblock.Rstate = 1 147 | } 148 | 149 | return 150 | } 151 | 152 | /* 153 | Executes a command and returns the response structure in json format 154 | */ 155 | func Cmd2json( cbuf string, rtype string ) ( jdata []byte, err error ) { 156 | resp, err := Cmd2resp( cbuf, rtype ) 157 | if err != nil { 158 | return 159 | } 160 | 161 | jdata, err = json.Marshal( *resp ) 162 | 163 | return 164 | } 165 | 166 | -------------------------------------------------------------------------------- /ipc/chmsg.go: -------------------------------------------------------------------------------- 1 | // vi: sw=4 ts=4: 2 | /* 3 | --------------------------------------------------------------------------- 4 | Copyright (c) 2013-2015 AT&T Intellectual Property 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | --------------------------------------------------------------------------- 18 | */ 19 | 20 | 21 | /* 22 | 23 | Mnemonic: chmsg 24 | Abstract: Channel message object and related methods. 25 | Date: 02 December 2013 26 | Author: E. Scott Daniels 27 | 28 | Mod: 2015 Nov 06 - Added nil ptr protection. 29 | 07 Mar 2015 - Added non-blocking send. 30 | */ 31 | 32 | /* 33 | The chmsg provides a data structure that helps to manage interprocess 34 | communications between Goroutines via channels. 35 | */ 36 | package ipc 37 | 38 | import ( 39 | ) 40 | 41 | 42 | // -------------------------------------------------------------------------------------- 43 | /* 44 | Defines a message. Request and response data pointers are used so that in the case 45 | where the requesting thread is not blocking on the response the request data will 46 | be available for response processing if needed. A third data field, requestor_data 47 | is another hook to assist with asynch response processing. This is assumed to point 48 | to a data object that is of no significance to the receiver of the message when 49 | the message is a request. It should be nil if the response channel is also nil. 50 | 51 | The struct allows the application to define how it is used, and does not really imply 52 | any meaning on the fields. 53 | 54 | */ 55 | type Chmsg struct { // all fields are externally available as a convenence 56 | Msg_type int; // action requested 57 | Response_ch chan *Chmsg; // channel to write this request back onto when done 58 | Response_data interface{}; // what might be useful in addition to the state 59 | Req_data interface{}; // necessary information to processes the request 60 | Requestor_data interface{}; // private data meaningful only to the requestor and 61 | // necessary to process an asynch response to the message. 62 | State error; // response state, nil == no error 63 | } 64 | 65 | /* 66 | constructor 67 | */ 68 | func Mk_chmsg( ) (r *Chmsg) { 69 | 70 | r = &Chmsg { }; 71 | return; 72 | } 73 | 74 | 75 | // ---- these are convenience functions that might make the code a bit easier to read ------------------ 76 | 77 | /* 78 | Real function which accepts a can-block flag sending the message along on the requested channel 79 | */ 80 | func (r *Chmsg) send_req( dest_ch chan *Chmsg, resp_ch chan *Chmsg, mtype int, data interface{}, pdata interface{}, can_block bool ) { 81 | if r == nil { 82 | return 83 | } 84 | 85 | r.Msg_type = mtype; 86 | r.Req_data = data; 87 | r.Response_ch = resp_ch; 88 | r.Requestor_data = pdata; 89 | r.Response_data = nil; 90 | r.State = nil; 91 | 92 | if can_block { 93 | dest_ch <- r; // this may block until the receiver reads it if the channel is not buffered 94 | } else { 95 | select { // this will not block if channel is full or unbuffered; message is dropped 96 | case dest_ch <- r: 97 | // we don't do anything, it just worked :) 98 | default: 99 | // we could log this, but we won't 100 | } 101 | } 102 | } 103 | 104 | /* 105 | Send the message as a request oriented packet. 106 | Data is mapped to request data, while response data and state are set to nil. 107 | Pdata is the private requestor data. 108 | */ 109 | func (r *Chmsg) Send_req( dest_ch chan *Chmsg, resp_ch chan *Chmsg, mtype int, data interface{}, pdata interface{} ) { 110 | r.send_req( dest_ch, resp_ch, mtype, data, pdata, true ) 111 | } 112 | 113 | /* 114 | Sends the message as a request to the indicated channel. The no-block flag is set which 115 | causes the message to be SILENTLY droped if the reciving channel is blocking. 116 | */ 117 | func (r *Chmsg) Send_nbreq( dest_ch chan *Chmsg, resp_ch chan *Chmsg, mtype int, data interface{}, pdata interface{} ) { 118 | r.send_req( dest_ch, resp_ch, mtype, data, pdata, false ) 119 | } 120 | 121 | /* 122 | Send the request as a response oriented packet. 123 | The response channel is used to send the block. 124 | Data is mapped to response data and state is set. All other fields are left unchanged. 125 | */ 126 | func (r *Chmsg) Send_resp( data interface{}, state error ) { 127 | if r == nil { 128 | return 129 | } 130 | 131 | r.Response_data = data; 132 | r.State = state; 133 | 134 | r.Response_ch <- r; // this may block until the receiver reads it if the channel is not buffered 135 | } 136 | -------------------------------------------------------------------------------- /jsontools/json2map.go: -------------------------------------------------------------------------------- 1 | // vi: sw=4 ts=4: 2 | /* 3 | --------------------------------------------------------------------------- 4 | Copyright (c) 2013-2015 AT&T Intellectual Property 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | --------------------------------------------------------------------------- 18 | */ 19 | 20 | 21 | /* 22 | 23 | Mnemonic: json2map 24 | Abstract: Take a bunch of json and construct a map. 25 | Date: 16 December 2013 26 | Author: E. Scott Daniels 27 | 28 | Mods: 04 Apr 2016 - comment change 29 | 18 May 2017 - Capture 'raw' interface for arrays in Jif2map. 30 | 31 | */ 32 | 33 | package jsontools 34 | 35 | import ( 36 | "encoding/json" 37 | "fmt" 38 | "os" 39 | ) 40 | 41 | // --------------------- public ---------------------------------------------------------------------- 42 | 43 | /* 44 | Builds a flat map from the interface 'structure', aka jif, (ithing) passed in putting into hashtab. 45 | The map names generated for hashtab are dot separated. For example, the json {foo:"foo-val", bar:["bar1", "bar2"] } 46 | would generate the following names in the map: 47 | root.foo, root.bar[0], root.bar[1] root.bar 48 | where 'root' is the initial ptag passed in. 49 | 50 | If an array element is an object, e.g. bar:[ {a:valuea,b:valueb}, "bar2" ], then the following keys in the map 51 | are generated: 52 | root.bar[0], root.bar[0].a, root.bar[0].b, root.bar[1] 53 | The root.bar[0] key references the actual interface for the element which is an object allowing that 54 | element to be teased out by the caller if needed (it can be passed to Jif2map() to generate a map 55 | just of that object. 56 | 57 | Alternateive: see jsontree.go 58 | */ 59 | func Jif2map( hashtab map[string]interface{}, ithing interface{}, depth int, ptag string, printit bool ) { 60 | 61 | switch ithing.( type ) { 62 | case map[string]interface{}: // named map of things 63 | for k, v := range ithing.( map[string]interface{} ) { 64 | Jif2map( hashtab, v, depth + 1, fmt.Sprintf( "%s.%s", ptag, k ), printit ) 65 | } 66 | 67 | case []interface{}: 68 | a := ithing.( []interface{} ) // unnecessary, but easier to read 69 | alen := fmt.Sprintf( "%s[len]", ptag ) 70 | hashtab[alen] = len( a ); 71 | if printit { fmt.Printf( "%s = %d\n", alen, len(a) ) } 72 | for i := range a { 73 | aidx := fmt.Sprintf( "%s[%d]", ptag, i ) 74 | hashtab[aidx] = a[i] // give a direct reference to the raw interface 75 | 76 | switch a[i].( type ) { 77 | case nil: 78 | // ignore 79 | 80 | case map[string]interface{}: 81 | Jif2map( hashtab, a[i], depth, fmt.Sprintf( "%s[%d]", ptag, i ), printit ) 82 | 83 | case interface{}: 84 | Jif2map( hashtab, a[i], depth, fmt.Sprintf( "%s[%d]", ptag, i ), printit ) 85 | 86 | case []interface{}: 87 | Jif2map( hashtab, a[i], depth, fmt.Sprintf( "%s[%d]", ptag, i ), printit ) 88 | 89 | default: 90 | fmt.Fprintf( os.Stderr, "ERR: Jif2map: unsupported type of interface array @ %s (%t); not added to the hashtable.\n", ptag, a[i] ) 91 | } 92 | } 93 | 94 | case string: 95 | if printit { fmt.Printf( "%s = %s\n", ptag, ithing.(string) ) } 96 | hashtab[ptag] = ithing 97 | 98 | case int: 99 | if printit { fmt.Printf( "%s = %d\n", ptag, ithing.(int) ) } 100 | hashtab[ptag] = ithing 101 | 102 | case float64: 103 | if printit { fmt.Printf( "%s = %.2f\n", ptag, ithing.(float64) ) } 104 | hashtab[ptag] = ithing 105 | 106 | case bool: 107 | vstate := "false" 108 | if ithing.(bool) { 109 | vstate = "true" 110 | } 111 | if printit { fmt.Printf( "%s = %s\n", ptag, vstate ) } 112 | hashtab[ptag] = ithing 113 | 114 | case interface{}: 115 | Jif2map( hashtab, ithing, depth+1, ptag, printit ) 116 | hashtab[ptag] = ithing 117 | 118 | case nil: 119 | if printit { fmt.Printf( "%s = null\n", ptag ) } 120 | hashtab[ptag] = nil 121 | 122 | default: 123 | fmt.Fprintf( os.Stderr, "ERROR: unpack: unexpected type at depth %d\n", depth ) 124 | // TODO: return error rather than aborting 125 | os.Exit( 1 ) 126 | } 127 | 128 | return; 129 | } 130 | 131 | 132 | /* 133 | Accepts the json blob as a byte string, probably just read off of the wire, and builds a symbol table. 134 | If the printit parm is true, then the representation of the json is written to the standard output 135 | in addition to the generation of the map. 136 | */ 137 | func Json2map( json_blob []byte, root_tag *string, printit bool ) ( symtab map[string]interface{}, err error ) { 138 | var ( 139 | jif interface{}; // were go's json will unpack the blob into interface form 140 | def_root_tag string = "root"; 141 | ) 142 | 143 | 144 | symtab = nil; 145 | err = json.Unmarshal( json_blob, &jif ) // unpack the json into jif 146 | if err != nil { 147 | return; 148 | } 149 | 150 | if root_tag == nil { 151 | root_tag = &def_root_tag; 152 | } 153 | 154 | symtab = make( map[string]interface{}, 1024 ); 155 | Jif2map( symtab, jif, 0, *root_tag, printit ) // unpack the jif into the symtab 156 | 157 | return; 158 | } 159 | 160 | -------------------------------------------------------------------------------- /config/jcfg_test.go: -------------------------------------------------------------------------------- 1 | 2 | package config_test 3 | 4 | import ( 5 | "fmt" 6 | "os" 7 | "testing" 8 | 9 | "github.com/att/gopkgs/config" 10 | ) 11 | 12 | /* 13 | These should all be tested: 14 | func ( cfg *Jconfig ) Extract_section( parent string, sname string, subsections string ) ( sect *Jconfig, err error ) { 15 | func ( cfg *Jconfig ) Extract_string( sects string, name string, def string ) ( string ) { 16 | func ( cfg *Jconfig ) Extract_int64( sects string, name string, def int64 ) ( int64 ) { 17 | func ( cfg *Jconfig ) Extract_int( sects string, name string, def int ) ( int ) { 18 | func ( cfg *Jconfig ) Extract_bool( sects string, name string, def bool ) ( bool ) { 19 | 20 | These need to be added: 21 | func ( cfg *Jconfig ) Extract_stringptr( sects string, name string, def interface{} ) ( *string ) { 22 | func ( cfg *Jconfig ) Extract_posint( sects string, name string, def int ) ( int ) { 23 | func ( cfg *Jconfig ) Extract_int32( sects string, name string, def int32 ) ( int32 ) { 24 | 25 | */ 26 | 27 | func TestJconfig( t *testing.T ) { 28 | fmt.Fprintf( os.Stderr, "\nTesting Json config file in jtest.cfg\n" ) 29 | 30 | jc,err := config.Mk_jconfig( "jtest.cfg", "template bool-test laser-spec" ) // order of sections should be unimportant 31 | if err != nil { 32 | fmt.Fprintf( os.Stderr, "[FAIL] unable to allocate a jconfig struct: %s\n", err ) 33 | t.Fail( ) 34 | return 35 | } 36 | 37 | v1 := jc.Extract_posint( "template bool-test default laser-spec", "posint", 0 ) // should come from default section and have good value 38 | if v1 < 0 { 39 | fmt.Fprintf( os.Stderr, "[FAIL] attempt to get positive integer from default section returned unexpected value: wanted >0 got: %d\n", v1 ) 40 | t.Fail( ) 41 | } else { 42 | fmt.Fprintf( os.Stderr, "[OK] extract positive int from correct section\n" ) 43 | } 44 | 45 | v1 = jc.Extract_posint( "template bool-test default laser-spec", "negint", 1 ) // should come from default section and have default (0) value 46 | if v1 != 1 { 47 | fmt.Fprintf( os.Stderr, "[FAIL] attempt to get positive integer from default section returned unexpected value: wanted 1 got: %d\n", v1 ) 48 | t.Fail( ) 49 | } else { 50 | fmt.Fprintf( os.Stderr, "[OK] extract positive int (default value) from correct section\n" ) 51 | } 52 | 53 | v1 = jc.Extract_int( "template bool-test laser-spec", "size", 0 ) // should come from template 54 | if v1 != 45 { 55 | fmt.Fprintf( os.Stderr, "[FAIL] attempt to get size from template section returned unexpected value: wanted 45 got: %d\n", v1 ) 56 | t.Fail( ) 57 | } else { 58 | fmt.Fprintf( os.Stderr, "[OK] extract int from correct section\n" ) 59 | } 60 | 61 | v1 = jc.Extract_int( "bool-test laser-spec template", "size", 0 ) // should not come from template 62 | if v1 == 45 { 63 | fmt.Fprintf( os.Stderr, "[FAIL] attempt to get size from section other than template returned unexpected value: wanted !45 got: %d\n", v1 ) 64 | t.Fail( ) 65 | } else { 66 | fmt.Fprintf( os.Stderr, "[OK] extract int from correct section with alternate list\n" ) 67 | } 68 | 69 | s1 := jc.Extract_string( "template bool-test laser-spec", "laser-string", "" ) 70 | if s1 != "string only in laser" { 71 | fmt.Fprintf( os.Stderr, "[FAIL] attempt to get string from laser-spec returned unexpected value: wanted 'string only in laser', got: %s\n", s1 ) 72 | t.Fail( ) 73 | } else { 74 | fmt.Fprintf( os.Stderr, "[OK] extract string from section\n" ) 75 | } 76 | 77 | b := jc.Extract_bool( "bool-test", "istrue", false ) 78 | if !b { 79 | fmt.Fprintf( os.Stderr, "[FAIL] attempt to extract boolian with true value failed\n" ) 80 | t.Fail( ) 81 | } else { 82 | fmt.Fprintf( os.Stderr, "[OK] extract true bool from section\n" ) 83 | } 84 | 85 | b = jc.Extract_bool( "bool-test", "isfalse", true ) 86 | if b { 87 | fmt.Fprintf( os.Stderr, "[FAIL] attempt to extract boolian with false value failed\n" ) 88 | t.Fail( ) 89 | } else { 90 | fmt.Fprintf( os.Stderr, "[OK] extract false booliean from section\n" ) 91 | } 92 | 93 | b = jc.Extract_bool( "bool-test", "badbool", false ) // ensure returns the default 94 | if b { 95 | fmt.Fprintf( os.Stderr, "[FAIL] bool extract for missing did not return the default\n" ) 96 | t.Fail( ) 97 | } else { 98 | fmt.Fprintf( os.Stderr, "[OK] extract false integer booliean from section\n" ) 99 | } 100 | 101 | lv := jc.Extract_int64( "default template", "i64", 0 ) // value should be > 1234567890 102 | if lv < 1234567891 { 103 | fmt.Fprintf( os.Stderr, "[FAIL] int 64 value was too small = %d\n", lv ) 104 | t.Fail( ) 105 | } else { 106 | fmt.Fprintf( os.Stderr, "[OK] int 64 value was ok %d\n", lv ) 107 | } 108 | 109 | 110 | // dig a section from the main config, and then pull values from it and it's sections 111 | lss, err := jc.Extract_section( "template laser-spec", "laser-ss", "" ) // get 'default' section from laser-spec subsection laser-ss (not found in template searched first) 112 | if err != nil { 113 | fmt.Fprintf( os.Stderr, "[FAIL] attempt to get subsection section laser-ss from laser-spec failed: %s\n", err ) 114 | t.Fail( ) 115 | } else { 116 | i := lss.Extract_int( "default", "lssv1", -1 ) 117 | if i != 1 { 118 | fmt.Fprintf( os.Stderr, "[FAIL] lssv1 = %d -- expected 1\n", i ) 119 | t.Fail( ) 120 | } 121 | i = lss.Extract_int( "default", "lssv2", -2 ) 122 | if i != 2 { 123 | fmt.Fprintf( os.Stderr, "[FAIL] lssv2 = %d -- expected 1\n", i ) 124 | t.Fail( ) 125 | } 126 | 127 | s := lss.Extract_string( "default", "type", "not found" ) 128 | if s != "laser subsection" { 129 | fmt.Fprintf( os.Stderr, "[FAIL] expected type string to be 'laser subsection' but got '%s'\n", s ) 130 | t.Fail( ) 131 | } 132 | } 133 | } 134 | 135 | -------------------------------------------------------------------------------- /ipc/ipc_test.go: -------------------------------------------------------------------------------- 1 | // vi: sw=4 ts=4: 2 | /* 3 | --------------------------------------------------------------------------- 4 | Copyright (c) 2013-2015 AT&T Intellectual Property 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | --------------------------------------------------------------------------- 18 | */ 19 | 20 | 21 | package ipc_test 22 | 23 | import ( 24 | "fmt" 25 | "os" 26 | "testing" 27 | "time" 28 | 29 | "github.com/att/gopkgs/ipc" 30 | ) 31 | 32 | func TestIpc( t *testing.T ) { 33 | req := ipc.Mk_chmsg( ); 34 | if req == nil { 35 | fmt.Fprintf( os.Stderr, "unable to create a request\n" ) 36 | t.Fail(); 37 | return; 38 | } 39 | 40 | start_ts := time.Now().Unix() 41 | fmt.Fprintf( os.Stderr, "this test runs for 60 seconds and will generate updates periodically....\n" ) 42 | req.Response_ch = nil; // use it to keep compiler happy 43 | 44 | 45 | data := "data for tickled bit"; 46 | ch := make( chan *ipc.Chmsg, 10 ); // allow 10 messages to queue on the channel 47 | // to test non-blocking aspect, set to 1 test should run longer than 60 seconds 48 | 49 | tklr := ipc.Mk_tickler( 6 ); // allow max of 6 ticklers; we test 'full' error at end 50 | 51 | tklr.Add_spot( 35, ch, 30, &data, 0 ); // will automatically start the tickler 52 | tklr.Add_spot( 20, ch, 20, &data, 0 ); 53 | tklr.Add_spot( 15, ch, 15, &data, 0 ); 54 | id, err := tklr.Add_spot( 10, ch, 10, &data, 0 ); // nick the id so we can drop it later 55 | _, err = tklr.Add_spot( 10, ch, 1, &data, 2 ); // should drive type 1 only twice; 10s apart 56 | 57 | if err != nil { 58 | fmt.Fprintf( os.Stderr, "unable to add tickle spot: %s\n", err ); 59 | t.Fail(); 60 | return; 61 | } 62 | 63 | fmt.Fprintf( os.Stderr, "type 10 and type 1 written every 10 seconds; type 1 only written twice\n" ); 64 | fmt.Fprintf( os.Stderr, "type 10 will be dropped after 35 seconds\n" ) 65 | fmt.Fprintf( os.Stderr, "type 15 will appear every 15 seconds\n" ) 66 | fmt.Fprintf( os.Stderr, "type 20 will appear every 20 seconds\n" ) 67 | fmt.Fprintf( os.Stderr, "type 30 will appear every 35 seconds\n" ) 68 | 69 | limited_count := 0; 70 | for count := 0; count < 2; { 71 | req = <- ch; // wait for tickle 72 | fmt.Fprintf( os.Stderr, "got a tickle: @%ds type=%d count=%d\n", time.Now().Unix() - start_ts, req.Msg_type, count ); 73 | if req.Msg_type == 30 { 74 | if count == 0 { 75 | fmt.Fprintf( os.Stderr, "dropping type 10 from list; no more type 10 should appear\n" ) 76 | tklr.Drop_spot( id ); // drop the 10s tickler after first 30 second one pops 77 | } 78 | count++; // count updated only at 30s point 79 | } 80 | 81 | if req.Msg_type == 1 { 82 | if limited_count > 1 { 83 | fmt.Fprintf( os.Stderr, "limited count tickle was driven more than twice [FAIL]\n" ); 84 | t.Fail(); 85 | } 86 | 87 | limited_count++; 88 | } 89 | 90 | if req.Msg_type == 10 && count > 0 { 91 | fmt.Fprintf( os.Stderr, "req 10 driven after it was dropped [FAIL]\n" ); 92 | t.Fail(); 93 | } 94 | } 95 | 96 | tklr.Stop(); 97 | 98 | 99 | // when we get here there should only be three active ticklers in the list, so we should be 100 | // able to add 3 before we get a full error. 101 | // add more spots until we max out to test the error logic in tickle 102 | err = nil; 103 | for i := 0; i < 3 && err == nil; i++ { 104 | _, err = tklr.Add_spot( 20, ch, 20, &data, 0 ); 105 | if err != nil { 106 | fmt.Fprintf( os.Stderr, "early failure when adding more: i=%d %s\n", i, err ); 107 | t.Fail(); 108 | return 109 | } 110 | } 111 | 112 | // the table should be full (6 active ticklers now) and this should return an error 113 | _, err = tklr.Add_spot( 10, ch, 10, &data, 0 ); 114 | if err != nil { 115 | fmt.Fprintf( os.Stderr, "test to over fill the table resulted in the expected error: %s [OK]\n", err ); 116 | } else { 117 | fmt.Fprintf( os.Stderr, "adding a 7th tickle spot didn't cause an error and should have [FAIL]\n" ); 118 | t.Fail(); 119 | } 120 | } 121 | 122 | 123 | func add_nbmsg( ch chan *ipc.Chmsg, mt int ) { 124 | r := ipc.Mk_chmsg() 125 | fmt.Fprintf( os.Stderr, "\tsending message type %d\n", mt ); 126 | r.Send_nbreq( ch, nil, mt, nil, nil ) 127 | fmt.Fprintf( os.Stderr, "\tsent message type %d\n", mt ); 128 | } 129 | 130 | /* 131 | Test the non-blocking send. 132 | */ 133 | func TestNBSend( t *testing.T ) { 134 | var count int = 0; 135 | 136 | ch := make( chan *ipc.Chmsg, 1 ) // make a channel which holds 1 message 137 | go add_nbmsg( ch, 1 ) // this write should be successful 138 | go add_nbmsg( ch, 2 ) // this write shouldn't make the pipe, but shouldn't block the go routine either 139 | 140 | time.Sleep( 4 * time.Second ) // pause to let go routines do their thing for sure 141 | 142 | for { 143 | select { 144 | case m := <-ch: // read from channel; this should happen only once 145 | fmt.Fprintf( os.Stderr, "[INFO] read from channel: %d\n", m.Msg_type ); 146 | count++ 147 | 148 | default: // this will happen when no data left to read 149 | if count != 1 { 150 | fmt.Fprintf( os.Stderr, "[FAIL] expected a read count of 1, got %d\n", count ); 151 | t.Fail() 152 | } else { 153 | fmt.Fprintf( os.Stderr, "[OK] expected a read count of 1, got 1\n" ); 154 | } 155 | return 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /token/tokenise_qsep.go: -------------------------------------------------------------------------------- 1 | // vi: sw=4 ts=4: 2 | /* 3 | --------------------------------------------------------------------------- 4 | Copyright (c) 2013-2015 AT&T Intellectual Property 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | --------------------------------------------------------------------------- 18 | */ 19 | 20 | 21 | package token 22 | 23 | import ( 24 | "strings" 25 | ) 26 | 27 | 28 | /* 29 | --------------------------------------------------------------------------------------------- 30 | Mnemonic: tokenise_qsep 31 | 32 | Returns: number of tokens, tokens[] 33 | Date: 22 Apr 2012 34 | Author: E. Scott Daniels 35 | 36 | Mods: 14 Jan 2014 - corrected bug that would allow quotes to remain on last token 37 | if there was not a separator between the quotes. 38 | 30 Nov 2014 - Allows escaped quote. 39 | 09 Apr 2015 - Corrected problem where index was not being checked and range 40 | was being busted causing a panic. Removed the 2k limit. 41 | 11 Aug 2018 - Correct bug if string ends in single quote. 42 | 27 Aug 2018 - Correct bug when string has escaped characters. 43 | --------------------------------------------------------------------------------------------- 44 | */ 45 | 46 | 47 | /* 48 | Takes a string and slices it into tokens using the characters in sepchrs 49 | as the breaking points, but allowing double quotes to provide protection 50 | against separation. For example, if sepchrs is ",|", then the string 51 | foo,bar,"hello,world","you|me" 52 | 53 | would break into 4 tokens: 54 | foo 55 | bar 56 | hello,world 57 | you|me 58 | 59 | If there are empty fields, they are returned as empty tokens. 60 | 61 | The return values are the number of tokens and the list of tokens. 62 | */ 63 | func Tokenise_qsep( buf string, sepchrs string ) (int, []string) { 64 | return tokenise_all( buf, sepchrs, true ) 65 | } 66 | 67 | /* 68 | Tokenises a string, but returns only an array of unique tokens. 69 | Empty tokens are discarded. 70 | */ 71 | func Tokenise_qsepu( buf string, sepchrs string ) ( int, []string ) { 72 | 73 | seen := make( map [string]bool, 1024 ) 74 | n, toks := tokenise_all( buf, sepchrs, false ) 75 | rtoks := make( []string, n ) 76 | j := 0 77 | for _, v := range toks { 78 | if ! seen[v] { 79 | seen[v] = true 80 | rtoks[j] = v 81 | j++ 82 | } 83 | } 84 | 85 | return j, rtoks[0:j] 86 | } 87 | 88 | /* 89 | This is the work horse for qsep and qpopulated. If keep_empty is true, then 90 | empty fields (adjacent separators) are kept as empty strings. 91 | */ 92 | func tokenise_all( buf string, sepchrs string, keep_empty bool ) (int, []string) { 93 | var tokens []string 94 | var idx int 95 | var i int 96 | var q int 97 | 98 | idx = 0 99 | tokens = make( []string, 2048 ) 100 | 101 | subbuf := buf 102 | for { 103 | i = strings.IndexAny( subbuf, sepchrs ) // index of the next sep character 104 | q = strings.IndexAny( subbuf, "\"" ) // index of the next quote 105 | 106 | if idx >= len( tokens ) { // more than we had room for; alloc new 107 | tnew := make( []string, len( tokens ) * 2 ) 108 | copy( tnew[:], tokens ) 109 | tokens = tnew 110 | } 111 | 112 | if q < 0 || (q >= i && i >= 0) { // sep before quote, or no quotes 113 | if i > 0 { 114 | tokens[idx] = subbuf[0:i]; // snarf up to the sep 115 | idx++ 116 | } else { 117 | if i == 0 { 118 | if keep_empty { 119 | tokens[idx] = "" // empty token when sep character @ 0 120 | idx++ 121 | } 122 | } else { // no more sep chrs and no quotes; capture last and bail 123 | if q < 1 { 124 | tokens[idx] = subbuf 125 | return idx+1, tokens[0:idx+1] 126 | } 127 | } 128 | } 129 | 130 | subbuf = subbuf[i+1:]; // skip what was added as token, and the sep 131 | } else { 132 | 133 | if q > 0 { // stuf before quote -- capture it now 134 | tokens[idx] = subbuf[0:q] 135 | } else { 136 | tokens[idx] = "" 137 | } 138 | 139 | subbuf = subbuf[q+1:]; // skip what we just snarfed, and opening quote 140 | ttok := make( []byte, len( subbuf ) ) // work space to strip escape characters in 141 | q = 0 142 | ttidx := 0 143 | for ttidx = 0; q < len( subbuf ) && subbuf[q] != '"'; ttidx++ { // find end trashing escape characters 144 | if subbuf[q] == '\\' { 145 | q++ 146 | } 147 | ttok[ttidx] = subbuf[q] 148 | q++ 149 | } 150 | if q > 0 { // could have been ,foo"" 151 | tokens[idx] += string( ttok[0:ttidx] ) 152 | } 153 | if q < len( subbuf ) - 1 { // prevent bounds exception if single trailing quote 154 | subbuf = subbuf[q+1:] 155 | } else { 156 | subbuf = "" 157 | } 158 | i = strings.IndexAny( subbuf, sepchrs ) // next sep, if there, past quoted stuff 159 | q = 0 160 | if q < i { 161 | tokens[idx] += subbuf[0:i-q] // snarf anything after quote and before sep 162 | if len( subbuf ) > 0 { 163 | subbuf = subbuf[(i-q)+1:] // finally skip past sep 164 | } 165 | } else { 166 | if len( subbuf ) > 0 { 167 | subbuf = subbuf[q+1:] 168 | } 169 | } 170 | 171 | idx++ 172 | } 173 | 174 | if len( subbuf ) < 1 { 175 | return idx, tokens[0:idx] 176 | } 177 | 178 | } 179 | 180 | return 0, nil // unreacable and vet will complain, but without this older compilers refuse to compile! 181 | } 182 | 183 | -------------------------------------------------------------------------------- /clike/atoll.go: -------------------------------------------------------------------------------- 1 | // vi: sw=4 ts=4: 2 | /* 3 | --------------------------------------------------------------------------- 4 | Copyright (c) 2013-2015 AT&T Intellectual Property 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | --------------------------------------------------------------------------- 18 | */ 19 | 20 | 21 | /* 22 | Mnemonic: atoll 23 | Absrtract: a clike atoll that doesn't error when it encounters 24 | a non-digit; returning 0 if there are no digits. This supports 25 | 0x or 0 values and should parse them stopping 26 | conversion at the first non-appropriate 'digit'. This also allows 27 | a lead +/-. 28 | 29 | There is an extension on the C functions... if the value is 30 | postfixed with M/K/G or m/k/g the return value will be 31 | 'expanded' accordingly with the capitalised values being 32 | powrs of 10 (e.g. MB) and the lower case indicating powers 33 | of 2 (e.g. MiB). This might cause unwanted side effects should 34 | the goal be to take a string 200grains and capture just the 35 | value; these functions will interpret the leading g incorrectly. 36 | 37 | Input can be either a string, pointer to string, or a byte array. 38 | 39 | If the string/array passed in does not begin with a valid digit 40 | or sign, or is a pointer that is nil, a value of 0 is returned 41 | rather than an error. 42 | */ 43 | 44 | package clike 45 | 46 | import ( 47 | "strconv" 48 | ) 49 | 50 | 51 | /* 52 | Convert a string or an array of bytes into a 64 bit integer. 53 | */ 54 | func Atoll( objx interface{} ) (v int64) { 55 | var ( 56 | i int 57 | buf []byte 58 | ) 59 | 60 | v = 0 // ensure all early returns have a value of 0 61 | 62 | if objx == nil { 63 | return 64 | } 65 | 66 | switch objx.( type ) { // place into a container we can deal with 67 | case []byte: 68 | buf = objx.([]byte) 69 | case string: 70 | buf = []byte( objx.(string) ) 71 | case *string: 72 | bp := objx.( *string ) 73 | if bp == nil { 74 | return 0 75 | } 76 | buf = []byte( *bp ); 77 | default: 78 | return // who knows, but it doesn't convert 79 | } 80 | 81 | if len( buf ) < 1 { 82 | return 83 | } 84 | 85 | i = 0 86 | if buf[i] == '-' || buf[i] == '+' { 87 | i++ 88 | } 89 | if buf[i] == '0' { 90 | if len( buf ) > 1 && buf[i+1] == 'x' { 91 | i += 2 92 | for ; i < len(buf) && ((buf[i] >= '0' && buf[i] <= '9') || (buf[i] >= 'a' && buf[i] <= 'f') ); i++ {} // find last digit 93 | } else { 94 | i++ 95 | for ; i < len(buf) && (buf[i] >= '0' && buf[i] <= '7'); i++ {} // find last octal digit 96 | } 97 | } else { 98 | for ; i < len(buf) && (buf[i] >= '0' && buf[i] <= '9'); i++ {} // find last digit 99 | } 100 | 101 | if i > 0 { 102 | v, _ = strconv.ParseInt( string( buf[0:i] ), 0, 64 ) 103 | } 104 | 105 | if i < len( buf ) { 106 | switch string( buf[i:] ) { 107 | case "M", "MB": 108 | v *= 1000000 109 | 110 | case "G", "GB": 111 | v *= 1000000000 112 | 113 | case "K", "KB": 114 | v *= 1000 115 | 116 | case "m", "MiB": 117 | v *= 1048576 118 | 119 | case "g", "GiB": 120 | v *= 1073741824 121 | 122 | case "k", "KiB": 123 | v *= 1024 124 | 125 | default: break; 126 | } 127 | } 128 | 129 | return 130 | } 131 | 132 | /* 133 | Convert to unsigned 64bit. 134 | */ 135 | func Atoull( objx interface{} ) (v uint64) { 136 | var ( 137 | i int 138 | buf []byte 139 | ) 140 | 141 | v = 0 // ensure all early returns have a value of 0 142 | 143 | if objx == nil { 144 | return 145 | } 146 | 147 | switch objx.( type ) { // place into a container we can deal with 148 | case []byte: 149 | buf = objx.([]byte) 150 | case string: 151 | buf = []byte( objx.(string) ) 152 | case *string: 153 | bp := objx.( *string ) 154 | if bp == nil { 155 | return 0 156 | } 157 | buf = []byte( *bp ); 158 | default: 159 | return // who knows, but it doesn't convert 160 | } 161 | 162 | if len( buf ) < 1 { 163 | return 164 | } 165 | 166 | i = 0 167 | if buf[i] == '-' || buf[i] == '+' { 168 | i++ 169 | } 170 | if buf[i] == '0' { 171 | if len( buf ) > 1 && buf[i+1] == 'x' { 172 | i += 2 173 | for ; i < len(buf) && ((buf[i] >= '0' && buf[i] <= '9') || (buf[i] >= 'a' && buf[i] <= 'f') ); i++ {} // find last digit 174 | } else { 175 | i++ 176 | for ; i < len(buf) && (buf[i] >= '0' && buf[i] <= '7'); i++ {} // find last octal digit 177 | } 178 | } else { 179 | for ; i < len(buf) && (buf[i] >= '0' && buf[i] <= '9'); i++ {} // find last digit 180 | } 181 | 182 | if i > 0 { 183 | v, _ = strconv.ParseUint( string( buf[0:i] ), 0, 64 ) 184 | } 185 | 186 | if i < len( buf ) { 187 | switch string( buf[i:] ) { 188 | case "M", "MB": 189 | v *= 1000000 190 | 191 | case "G", "GB": 192 | v *= 1000000000 193 | 194 | case "K", "KB": 195 | v *= 1000 196 | 197 | case "m", "MiB": 198 | v *= 1048576 199 | 200 | case "g", "GiB": 201 | v *= 1073741824 202 | 203 | case "k", "KiB": 204 | v *= 1024 205 | 206 | default: break; 207 | } 208 | } 209 | 210 | return 211 | } 212 | -------------------------------------------------------------------------------- /connman/tls.go: -------------------------------------------------------------------------------- 1 | // vi: sw=4 ts=4: 2 | /* 3 | --------------------------------------------------------------------------- 4 | Copyright (c) 2013-2015 AT&T Intellectual Property 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | --------------------------------------------------------------------------- 18 | */ 19 | 20 | /* 21 | Mnemonic: tls.go 22 | Abstract: Functions which support a TLS listener within a connman environment. 23 | 24 | Date: 30 October 2016 25 | Author: E. Scott Daniels 26 | 27 | Mods: 28 | */ 29 | 30 | package connman 31 | 32 | import ( 33 | "fmt" 34 | "strings" 35 | "os" 36 | "crypto/tls" 37 | 38 | "github.com/att/gopkgs/security" 39 | ) 40 | 41 | /* 42 | Generate a self-singed certificate and key which are plced into the files 43 | with the given names passed in. 44 | */ 45 | func mk_cert( cert_name string, cfname *string, kfname *string ) ( err error ) { 46 | this_host, _ := os.Hostname( ) 47 | tokens := strings.Split( this_host, "." ) 48 | 49 | dns_list := make( []string, 3 ) 50 | dns_list[0] = "localhost" 51 | dns_list[1] = this_host 52 | dns_list[2] = tokens[0] 53 | 54 | err = security.Mk_cert( 1024, &cert_name, dns_list, cfname, kfname ) 55 | 56 | return err 57 | } 58 | 59 | /* 60 | Given a cert and key filename pair, load them and return a cert that can be 61 | used in a tls configuration struct. 62 | */ 63 | func load_cert( cfname *string, kfname *string ) ( cert tls.Certificate, err error ) { 64 | if cfname == nil || kfname == nil { 65 | return cert, fmt.Errorf( "one of both filename pointers were nil" ) 66 | } 67 | 68 | cert, err = tls.LoadX509KeyPair( *cfname, *kfname ) 69 | return cert, err 70 | } 71 | 72 | /* 73 | Create a base tls config to give to tls.Listen() etc. 74 | */ 75 | func mk_tls_config( certs []tls.Certificate ) ( *tls.Config ) { 76 | base_config := &tls.Config { 77 | Rand: nil, // TLS will use crypto/rand 78 | Time: nil, // now is used 79 | NameToCertificate: nil, // cause 1st cert to always be used 80 | GetCertificate: nil, // we don't supply a 'lookup' func 81 | RootCAs: nil, // use host os's set 82 | NextProtos: nil, 83 | ClientAuth: tls.NoClientCert, 84 | InsecureSkipVerify: true, 85 | CipherSuites: nil, // use implementation available suites 86 | PreferServerCipherSuites: false, 87 | SessionTicketsDisabled: true, // don't allow session tickets 88 | //SessionTicketKey (allow to be zeros) 89 | MinVersion: 0, 90 | MaxVersion: 0, 91 | //CurvePreferences (allow to be zeros) 92 | //Renegotiation: tls.RenegotiateNever, 93 | } 94 | 95 | 96 | base_config.ServerName, _ = os.Hostname() 97 | base_config.Certificates = certs 98 | return base_config 99 | } 100 | 101 | /* 102 | Starts a listener (TCP or UDP) for tls connections on the indicated port and interface. The 103 | data2usr channel is used to pass back connections when accepted in the same manner as is 104 | done by the Listen() function in this package. The actual listener is started in a parallel 105 | goroutine which passes back accept information on the channel; this function returns and 106 | the caller does not need to invoke as a goroutine. 107 | 108 | The cert_base parameter is the filename path (e.g. /usr2/foo/bar/progx) to which '.key' and '.cert' 109 | will be appended to create the cert and key filenames. If these files do not exist, then a 110 | self-signed certificate and key will be created and written to them. 111 | 112 | The listener ID string is returned (allowing the listener to be cancelled) along with an error 113 | object if there was a failure. 114 | */ 115 | func (this *Cmgr) TLS_listen( kind string, port string, iface string, data2usr chan *Sess_data, cert_base string ) ( lid string, err error ) { 116 | if this == nil { 117 | return "", nil 118 | } 119 | 120 | if port == "" || port == "0" { // user probably called constructor not wanting a listener 121 | return "", nil 122 | } 123 | if cert_base == "" { 124 | return "", fmt.Errorf( "must supply a basename string that is non-empty" ) 125 | } 126 | 127 | kfname := fmt.Sprintf( "%s.key", cert_base ) 128 | cfname := fmt.Sprintf( "%s.cert", cert_base ) 129 | 130 | have_count := 0 131 | _, err = os.Stat( kfname ) // check files; both or neither must exist 132 | if err == nil { 133 | have_count++ 134 | } 135 | _, err = os.Stat( cfname ) 136 | if err == nil { 137 | have_count++ 138 | } 139 | 140 | switch have_count { 141 | case 0: // both missing, create into the filenames given 142 | toks := strings.Split( cert_base, "/" ) 143 | cert_name := toks[len(toks)-1] 144 | err = mk_cert( cert_name, &cfname, &kfname ) 145 | if err != nil { 146 | return "", fmt.Errorf( "unable to make certificate: %s", err ) 147 | } 148 | 149 | case 1: // found one, but not both -- don't know what to do so abort 150 | return "", fmt.Errorf( "found one of the key/cert pair, but not both. must have both or neither" ) 151 | } 152 | 153 | certs := make( []tls.Certificate, 1 ) 154 | certs[0], err = load_cert( &cfname, &kfname ) 155 | if err != nil { 156 | return "", err 157 | } 158 | 159 | config := mk_tls_config( certs ) // create a configuration 160 | 161 | lid = "" 162 | l, err := tls.Listen( kind, fmt.Sprintf( "%s:%s", iface, port ), config ) 163 | if err != nil { 164 | err = fmt.Errorf( "unable to create a TLS listener on port: %s; %s", port, err ) 165 | return 166 | } 167 | 168 | lid = fmt.Sprintf( "l%d", this.lcount ) 169 | this.lcount += 1 170 | 171 | this.llist[lid] = l 172 | go this.listener( this.llist[lid], data2usr ) 173 | return 174 | } 175 | -------------------------------------------------------------------------------- /jsontools/prtmap.go: -------------------------------------------------------------------------------- 1 | // vi: sw=4 ts=4: 2 | /* 3 | --------------------------------------------------------------------------- 4 | Copyright (c) 2013-2015 AT&T Intellectual Property 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | --------------------------------------------------------------------------- 18 | */ 19 | 20 | 21 | /* 22 | 23 | Mnemonic: prtmap 24 | Abstract: This may apply outside of the json world, but plays well with json 25 | needs so it's here. These functions take a bunch of unknown 26 | stuff (described as an interface) and prints them in a hierarchical manner. 27 | 28 | The public Print() function takes the the 'root' of the data and 29 | invokes either prettyprint() to print it in a 'tree-ish' format, o 30 | or dotprint() to print it in a dotted notation. The ptag supplied 31 | is used to name the 'root'. 32 | 33 | Date: 02 January 2014 34 | Author: E. Scott Daniels 35 | */ 36 | 37 | package jsontools 38 | 39 | import ( 40 | "fmt" 41 | "os" 42 | ) 43 | 44 | /* 45 | users can pass on calls to make their code more readable 46 | */ 47 | const ( 48 | PRINT bool = true 49 | NOPRINT bool = false 50 | ) 51 | 52 | var pp_need_blank bool; 53 | 54 | /* 55 | take an unmarshalled blob and print it as a hierarchy. If dotted output is desired 56 | the blob must be converted into a map (see jsontools.Unpack) and then printed by 57 | the dotprint method here. 58 | ptag is the name of the item's parent 59 | */ 60 | func prettyprint( ithing interface{}, depth int, ptag string ) { 61 | var ( 62 | indention string = " " 63 | ) 64 | 65 | istr := indention[0:depth*4] 66 | switch ithing.( type ) { 67 | case map[string]interface{}: // named map of things 68 | pp_need_blank = false; 69 | if ptag != "" { 70 | fmt.Printf( "%s%s:\n", istr, ptag ) 71 | } 72 | ptag = "" 73 | for k, v := range ithing.( map[string]interface{} ) { 74 | prettyprint( v, depth + 1, k ) 75 | } 76 | 77 | case []interface{}: 78 | pp_need_blank = true; 79 | fmt.Printf( "\n" ) 80 | a := ithing.( []interface{} ) // unnecessary, but easier to read 81 | for i := range a { 82 | switch a[i].( type ) { 83 | case map[string]interface{}: 84 | fmt.Printf( "%s%s[%d]:\n", istr, ptag, i ) 85 | prettyprint( a[i], depth, "" ) 86 | 87 | case interface{}: 88 | prettyprint( a[i], depth, fmt.Sprintf( "%s[%d]", ptag, i ) ) 89 | 90 | default: 91 | fmt.Fprintf( os.Stderr, "%sERROR: unhandled type of interface array\n", istr ) 92 | } 93 | } 94 | 95 | case string: 96 | fmt.Printf( "%s%s = %s\n", istr, ptag, ithing.(string) ) 97 | 98 | case int: 99 | fmt.Printf( "%s%s = %d\n", istr, ptag, ithing.(int) ) 100 | 101 | case float64: 102 | fmt.Printf( "%s%s = %.2f\n", istr, ptag, ithing.(float64) ) 103 | 104 | case bool: 105 | vstate := "false" 106 | if ithing.(bool) { 107 | vstate = "true" 108 | } 109 | fmt.Printf( "%s%s = %s\n", istr, ptag, vstate ) 110 | 111 | case interface{}: 112 | pp_need_blank = true; 113 | fmt.Printf( "%s%s:\n", istr, ptag ) 114 | prettyprint( ithing, depth+1, ptag ) 115 | //if pp_need_blank { 116 | //fmt.Printf( "\n" ) 117 | //} 118 | //pp_need_blank = false; 119 | 120 | case nil: 121 | fmt.Printf( "%s%s = null/undefined\n", istr, ptag ) 122 | 123 | default: 124 | fmt.Printf( "error: unexpected type at depth %d\n", depth ) 125 | os.Exit( 1 ) 126 | } 127 | 128 | if pp_need_blank { 129 | fmt.Printf( "\n" ) 130 | } 131 | pp_need_blank = false; 132 | } 133 | 134 | /* 135 | Takes an unknown thing and prints it in a hierarchical form as a set of dot separated names. 136 | ptag is the name of the item's parent 137 | */ 138 | func dotprint( ithing interface{}, depth int, ptag string ) { 139 | 140 | switch ithing.( type ) { 141 | case map[string]interface{}: // named map of things 142 | ptag = "" 143 | for k, v := range ithing.( map[string]interface{} ) { 144 | dotprint( v, depth + 1, k ) 145 | } 146 | fmt.Printf( "\n" ) 147 | 148 | case []interface{}: 149 | a := ithing.( []interface{} ) // unnecessary, but easier to read 150 | for i := range a { 151 | switch a[i].( type ) { 152 | case map[string]interface{}: 153 | fmt.Printf( "%s[%d]:\n", ptag, i ) 154 | dotprint( a[i], depth, "" ) 155 | 156 | case interface{}: 157 | dotprint( a[i], depth, fmt.Sprintf( "%s[%d]", ptag, i ) ) 158 | 159 | default: 160 | fmt.Fprintf( os.Stderr, "ERROR: unhandled type of interface array\n" ) 161 | } 162 | } 163 | fmt.Printf( "\n" ) 164 | 165 | case string: 166 | fmt.Printf( "%s = %s\n", ptag, ithing.(string) ) 167 | 168 | case int: 169 | fmt.Printf( "%s = %d\n", ptag, ithing.(int) ) 170 | 171 | case float64: 172 | fmt.Printf( "%s = %.2f\n", ptag, ithing.(float64) ) 173 | 174 | case bool: 175 | vstate := "false" 176 | if ithing.(bool) { 177 | vstate = "true" 178 | } 179 | fmt.Printf( "%s = %s\n", ptag, vstate ) 180 | 181 | case interface{}: 182 | fmt.Printf( "%s:\n", ptag ) 183 | dotprint( ithing, depth+1, ptag ) 184 | fmt.Printf( "\n" ) 185 | 186 | case nil: 187 | fmt.Printf( "%s = null/undefined\n", ptag ) 188 | 189 | default: 190 | fmt.Printf( "error: unexpected type at depth %d\n", depth ) 191 | os.Exit( 1 ) 192 | } 193 | } 194 | // -------------------- public---------------------------------------------------------------------------------- 195 | 196 | /* 197 | A simple method that allows an interface representation to easily be printed in either hierarchical 198 | or "dotted" format. 199 | */ 200 | func Print( stuff interface{}, ptag string, dotted bool ) { 201 | if dotted { 202 | dotprint( stuff, 0, ptag ); 203 | } else { 204 | prettyprint( stuff, 0, ptag ); 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /ostack/ostack_vminfo.go: -------------------------------------------------------------------------------- 1 | // vi: sw=4 ts=4: 2 | /* 3 | --------------------------------------------------------------------------- 4 | Copyright (c) 2013-2015 AT&T Intellectual Property 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | --------------------------------------------------------------------------- 18 | */ 19 | 20 | 21 | /* 22 | ------------------------------------------------------------------------------------------------ 23 | Mnemonic: ostack_vminfo 24 | Abstract: Functions that generate and/or operate on a VM_info struct. 25 | 26 | Date: 01 May 2015 27 | Author: E. Scott Daniels 28 | 29 | Mod: 10 Sep 2015 - Added extra calls to get information about individual interfaces 30 | for each VM; sepecifally the uuid. 31 | 23 Sep 2015 - Added ability to get endpoint info for each VM 32 | interface that is listed by os-interface. 33 | ------------------------------------------------------------------------------------------------ 34 | */ 35 | 36 | package ostack 37 | 38 | import ( 39 | "bytes" 40 | "encoding/json" 41 | "fmt" 42 | ) 43 | 44 | /* 45 | Returns a map of VM information keyed by VM id. 46 | If umap is passed in (not nil), then the information is added to that map, otherwise 47 | a new map is created. 48 | */ 49 | func (o *Ostack) Map_vm_info( umap map[string]*VM_info ) ( info map[string]*VM_info, err error ) { 50 | var ( 51 | vm_data generic_response // "root" of the response goo after pulling out of json format 52 | jdata []byte // raw json response data 53 | ) 54 | 55 | if umap != nil { 56 | info = umap 57 | } else { 58 | info = make( map[string]*VM_info, 256 ) // 256 is a hint, not a hard limit 59 | } 60 | 61 | if o == nil { 62 | err = fmt.Errorf( "ostact struct was nil" ) 63 | return 64 | } 65 | 66 | err = o.Validate_auth() // reauthorise if needed 67 | if err != nil { 68 | return 69 | } 70 | 71 | jdata = nil 72 | body := bytes.NewBufferString( "" ) 73 | 74 | url := *o.chost + "/servers/detail" 75 | dump_url( "get_vm_info", 10, url ) 76 | jdata, _, err = o.Send_req( "GET", &url, body ) 77 | dump_json( "get_vm_info", 10, jdata ) 78 | 79 | if err != nil { 80 | return 81 | } 82 | 83 | err = json.Unmarshal( jdata, &vm_data ) // unpack the json into jif 84 | if err != nil { 85 | dump_json( fmt.Sprintf( "get_vm_info: unpack err: %s\n", err ), 30, jdata ) 86 | return 87 | } 88 | 89 | 90 | // TODO -- add address information 91 | for i := range vm_data.Servers { // for each vm 92 | vm := vm_data.Servers[i] 93 | id := vm.Id 94 | info[id] = &VM_info { 95 | id: id, // must carry id so String and To_json work 96 | zone: vm.Azone, 97 | created: vm.Created, 98 | flavour: vm.Flavor.Id, 99 | hostid: vm.Hostid, 100 | host_name: vm.Host_name, 101 | //image: vm.Image.Id, // due to openstack inconsistency we don't grab the image info 102 | name: vm.Name, 103 | status: vm.Status, 104 | tenant_id: vm.Tenant_id, 105 | updated: vm.Updated, 106 | launched: vm.Launched, 107 | terminated: vm.Terminated, 108 | } 109 | 110 | info[id].endpoints, _ = o.Get_endpoints( &info[id].id, &info[id].host_name ) 111 | } 112 | 113 | return 114 | } 115 | 116 | func (vi *VM_info) Get_name() ( string ) { 117 | if vi == nil { 118 | return "" 119 | } 120 | 121 | return vi.name 122 | } 123 | 124 | func (vi *VM_info) Get_zone() ( string ) { 125 | if vi == nil { 126 | return "" 127 | } 128 | 129 | return vi.zone 130 | } 131 | 132 | func (vi *VM_info) Get_flavour() ( string ) { 133 | if vi == nil { 134 | return "" 135 | } 136 | 137 | return vi.flavour 138 | } 139 | 140 | func (vi *VM_info) Get_created() ( string ) { 141 | if vi == nil { 142 | return "" 143 | } 144 | 145 | return vi.created 146 | } 147 | 148 | func (vi *VM_info) Get_hostid() ( string ) { 149 | if vi == nil { 150 | return "" 151 | } 152 | 153 | return vi.hostid 154 | } 155 | 156 | func (vi *VM_info) Get_hostname() ( string ) { 157 | if vi == nil { 158 | return "" 159 | } 160 | 161 | return vi.host_name 162 | } 163 | 164 | func (vi *VM_info) Get_image() ( string ) { 165 | if vi == nil { 166 | return "" 167 | } 168 | 169 | return vi.image 170 | } 171 | 172 | func (vi *VM_info) Get_status() ( string ) { 173 | if vi == nil { 174 | return "" 175 | } 176 | 177 | return vi.status} 178 | 179 | func (vi *VM_info) Get_tenantid() ( string ) { 180 | if vi == nil { 181 | return "" 182 | } 183 | 184 | return vi.tenant_id 185 | } 186 | 187 | func (vi *VM_info) Get_updated() ( string ) { 188 | if vi == nil { 189 | return "" 190 | } 191 | 192 | return vi.updated} 193 | 194 | func (vi *VM_info) Get_launched() ( string ) { 195 | if vi == nil { 196 | return "" 197 | } 198 | 199 | return vi.launched} 200 | 201 | func (vi *VM_info) Get_terminated() ( string ) { 202 | if vi == nil { 203 | return "" 204 | } 205 | 206 | return vi.terminated 207 | } 208 | 209 | 210 | /* 211 | Implements the string interface. 212 | */ 213 | func (vi *VM_info) String() ( string ) { 214 | s := "" 215 | if vi == nil { 216 | return s 217 | } 218 | 219 | s = fmt.Sprintf( "%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,[", 220 | vi.id, vi.name, vi.hostid, vi.host_name, vi.status, 221 | vi.tenant_id, vi.flavour, vi.image, vi.zone, vi.created, 222 | vi.launched, vi.updated, vi.terminated ) 223 | 224 | 225 | sep := "" 226 | for _, v := range( vi.endpoints ) { 227 | s += fmt.Sprintf( "%s%s", sep, v ) 228 | sep = "," 229 | } 230 | 231 | s += "]" 232 | 233 | return s 234 | } 235 | 236 | 237 | /* 238 | Generates a json string representing the struct. 239 | */ 240 | func (vi *VM_info) To_json() ( string ) { 241 | if vi == nil { 242 | return "{ }" 243 | } 244 | 245 | return fmt.Sprintf( `{ "id": %q, "name": %q, "hostid": %q, "host_name": %q, "status": %q, "tenant_id": %q, "flavour": %q, "image": %q, "zone": %q, "created": %q, "launched": %q, "updated": %q, "terminated": %q }`, 246 | vi.id, vi.name, vi.hostid, vi.host_name, vi.status, vi.tenant_id, vi.flavour, vi.image, vi.zone, vi.created, 247 | vi.launched, vi.updated, vi.terminated ) 248 | } 249 | 250 | 251 | -------------------------------------------------------------------------------- /ostack/ostack_user.go: -------------------------------------------------------------------------------- 1 | // vi: sw=4 ts=4: 2 | /* 3 | --------------------------------------------------------------------------- 4 | Copyright (c) 2013-2015 AT&T Intellectual Property 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | --------------------------------------------------------------------------- 18 | */ 19 | 20 | 21 | /* 22 | ------------------------------------------------------------------------------------------------ 23 | Mnemonic: ostack_user 24 | Abstract: Interface functions providing user information such as lists of roles. 25 | Date: 30 October 2014 26 | Authors: E. Scott Daniels, Matti Hiltnuen, Kaustubh Joshi 27 | 28 | Mods: 29 | 10 Nov 2014 : Added checks to ensure response data structs aren't nil 30 | 13 Apr 2015 : Converted to more generic error structure use. 31 | 19 Jun 2015 : Corrected bug in URL for roll gathering. 32 | ------------------------------------------------------------------------------------------------ 33 | */ 34 | 35 | package ostack 36 | 37 | import ( 38 | "bytes" 39 | "encoding/json" 40 | "fmt" 41 | "os" 42 | ) 43 | 44 | 45 | // ------------------------------------------------------------------------------------------------- 46 | 47 | /* 48 | Builds a map of the role names to ID strings. 49 | */ 50 | func (o *Ostack) Map_roles( ) ( rmap map[string]*string, err error ) { 51 | var ( 52 | role_data generic_response 53 | rjson string = "" 54 | ) 55 | 56 | rmap = nil 57 | 58 | if o == nil || o.user == nil || o.passwd == nil { 59 | err = fmt.Errorf( "no openstack object to work on, or missing data inside" ) 60 | return 61 | } 62 | 63 | if o.iahost == nil { 64 | err = fmt.Errorf( "no identity url associated with ostack data object: %s", o.To_str() ) 65 | return 66 | } 67 | 68 | body := bytes.NewBufferString( rjson ); 69 | 70 | url := *o.iahost + "v2.0/OS-KSADM/roles"; 71 | dump_url( "map-roles", 10, url ) 72 | jdata, _, err := o.Send_req( "GET", &url, body ); 73 | dump_json( "map-roles", 10, jdata ) 74 | 75 | if err != nil { 76 | return; 77 | } 78 | 79 | err = json.Unmarshal( jdata, &role_data ) // unpack the json into generic struct 80 | if err != nil { 81 | fmt.Fprintf( os.Stderr, "ostack: map-roles: unable to unpack json: %s\n", err ); 82 | return; 83 | } 84 | 85 | if role_data.Error != nil { 86 | o.token = nil 87 | o.small_tok = nil 88 | err = fmt.Errorf( "map-role failed: %s\n", role_data.Error ) 89 | return 90 | } 91 | 92 | if role_data.Roles != nil { 93 | if l := len( role_data.Roles ); l > 0 { 94 | rmap = make( map[string]*string, l ) 95 | 96 | for _, v := range role_data.Roles { 97 | dup_str := v.Id // must duplicate to pull from receive buffer 98 | rmap[v.Name] = &dup_str 99 | } 100 | } 101 | } 102 | 103 | return; 104 | } 105 | 106 | 107 | /* 108 | Map the roles assigned to the user and given project (must be id). If pid is nil then 109 | the project ID associated with the struct (returned on auth) will be used. 110 | */ 111 | func (o *Ostack) Map_user_roles( pid *string ) ( rmap map[string]*string, err error ) { 112 | var ( 113 | usr_data generic_response 114 | rjson string = "" 115 | ) 116 | 117 | rmap = nil 118 | 119 | if o == nil || o.user == nil || o.passwd == nil { 120 | err = fmt.Errorf( "no openstack object to work on, or missing data inside" ) 121 | return 122 | } 123 | if o.iahost == nil { 124 | err = fmt.Errorf( "no identity url associated with ostack data object: %s", o.To_str() ) 125 | return 126 | } 127 | 128 | body := bytes.NewBufferString( rjson ); 129 | 130 | if pid == nil { 131 | pid = o.project_id // this project if not given by caller 132 | } 133 | 134 | url := fmt.Sprintf( "%s/v2.0/tenants/%s/users/%s/roles", *o.iahost, *pid, *o.user_id ) 135 | dump_url( "map-uroles", 10, url ) 136 | jdata, _, err := o.Send_req( "GET", &url, body ); 137 | dump_json( "map-uroles", 10, jdata ) 138 | 139 | if err != nil { 140 | return; 141 | } 142 | 143 | err = json.Unmarshal( jdata, &usr_data ) // unpack the json into usr_data 144 | if err != nil { 145 | fmt.Fprintf( os.Stderr, "ostack: map-usr-roles: unable to unpack json: %s\n", err ); 146 | return; 147 | } 148 | 149 | if usr_data.Error != nil { 150 | o.token = nil 151 | o.small_tok = nil 152 | err = fmt.Errorf( "map-usr-roles failed: %s\n", usr_data.Error ) 153 | return 154 | } 155 | 156 | if usr_data.Roles != nil { 157 | if l := len( usr_data.Roles ); l > 0 { 158 | rmap = make( map[string]*string, l ) 159 | 160 | for _, v := range usr_data.Roles { 161 | rmap[v.Name] = &v.Id 162 | } 163 | } 164 | } 165 | 166 | return 167 | } 168 | 169 | /* 170 | Map the global roles assigned to the user associated with the structure. 171 | This may not always work -- depends on flavour of openstack it seems -- 172 | and if it fails it seems to affect subsequent calls to roles (huh?). 173 | */ 174 | func (o *Ostack) Map_user_groles( ) ( rmap map[string]*string, err error ) { 175 | var ( 176 | usr_data generic_response 177 | rjson string = "" 178 | ) 179 | 180 | rmap = nil 181 | 182 | if o == nil || o.user == nil || o.passwd == nil { 183 | err = fmt.Errorf( "no openstack object to work on, or missing data inside" ) 184 | return 185 | } 186 | if o.iahost == nil { 187 | err = fmt.Errorf( "no identity url associated with ostack data object: %s", o.To_str() ) 188 | return 189 | } 190 | 191 | body := bytes.NewBufferString( rjson ); 192 | 193 | url := fmt.Sprintf( "%s/users/%s/roles", *o.iahost, *o.user_id ) 194 | dump_url( "map-groles", 100, url ) 195 | jdata, _, err := o.Send_req( "GET", &url, body ); 196 | dump_json( "map-groles", 10, jdata ) 197 | 198 | if err != nil { 199 | return; 200 | } 201 | 202 | err = json.Unmarshal( jdata, &usr_data ) // unpack the json into usr_data 203 | if err != nil { 204 | fmt.Fprintf( os.Stderr, "ostack: map-usr-groles: unable to unpack json: %s\n", err ); 205 | return; 206 | } 207 | 208 | if usr_data.Error != nil { 209 | o.token = nil 210 | o.small_tok = nil 211 | err = fmt.Errorf( "map-usr-groles failed: %s\n", usr_data.Error ) 212 | return 213 | } 214 | 215 | if usr_data.Roles != nil { 216 | if l := len( usr_data.Roles ); l > 0 { 217 | rmap = make( map[string]*string, l ) 218 | 219 | for _, v := range usr_data.Roles { 220 | rmap[v.Name] = &v.Id 221 | } 222 | } 223 | } 224 | 225 | return 226 | } 227 | -------------------------------------------------------------------------------- /jsontools/defrock_test.go: -------------------------------------------------------------------------------- 1 | // vi: sw=4 ts=4: 2 | /* 3 | --------------------------------------------------------------------------- 4 | Copyright (c) 2013-2015 AT&T Intellectual Property 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | --------------------------------------------------------------------------- 18 | */ 19 | 20 | 21 | package jsontools_test 22 | 23 | import ( 24 | "fmt" 25 | "testing" 26 | "os" 27 | 28 | "github.com/att/gopkgs/jsontools" 29 | ) 30 | 31 | /* 32 | Returns true if there are any escape characters in the string. 33 | */ 34 | func has_escapes( s string ) ( bool ) { 35 | for _, c := range s { 36 | if c == '\\' { 37 | return true 38 | } 39 | } 40 | 41 | return false 42 | } 43 | 44 | func TestDefrock( t *testing.T ) { 45 | var ( 46 | jstring string 47 | jbytes []byte 48 | ) 49 | 50 | fmt.Fprintf( os.Stderr, "===== start defrock testing ======\n\n" ) 51 | 52 | // this is similar to an openstack oslo message which feels the need to embed json as a string (network_info in tis case) 53 | // the resulting refrock should be valid json and should not have any escapes 54 | jstring = "{\"_context_domain\": null, \"_msg_id\": \"342f25641ede44f381c4222ca93ff018\", \"_context_quota_class\": null, \"_context_read_only\": false, \"_context_request_id\": \"req-840657d6-c568-4b03-aebc-005d7c0e6fa4\", \"_context_service_catalog\": [], \"args\": {\"instance\": {\"vm_state\": \"active\", \"availability_zone\": \"nova\", \"terminated_at\": null, \"ephemeral_gb\": 0, \"instance_type_id\": 3, \"user_data\": null, \"cleaned\": false, \"vm_mode\": null, \"deleted_at\": null, \"reservation_id\": \"r-loov99le\", \"id\": 2125, \"security_groups\": [], \"disable_terminate\": false, \"root_device_name\": \"/dev/vda\", \"display_name\": \"jd-tmp\", \"uuid\": \"bc353bd2-0f02-46b7-8366-51879cf820f5\", \"default_swap_device\": null, \"info_cache\": {\"instance_uuid\": \"bc353bd2-0f02-46b7-8366-51879cf820f5\", \"deleted\": false, \"network_info\": [{\"profile\": {}, \"ovs_interfaceid\": \"ebf58f66-7f56-4111-bdd6-6f81954972cd\", \"preserve_on_delete\": false, \"network\": {\"bridge\": \"br-int\", \"label\": \"cloudqos-private\", \"meta\": {\"injected\": false, \"tenant_id\": \"65c3e5ee5ee0428caa5e5275c58ead61\"}, \"id\": \"e174ae6a-ef11-45e4-b888-add340e98c4f\", \"subnets\": [{\"ips\": [{\"meta\": {}, \"type\": \"fixed\", \"floating_ips\": [], \"version\": 4, \"address\": \"10.7.1.173\"}], \"version\": 4, \"meta\": {\"dhcp_server\": \"10.7.0.2\"}, \"dns\": [{\"meta\": {}, \"type\": \"dns\", \"version\": 4, \"address\": \"135.207.177.11\"}, {\"meta\": {}, \"type\": \"dns\", \"version\": 4, \"address\": \"135.207.179.11\"}], \"routes\": [], \"cidr\": \"10.7.0.0/16\", \"gateway\": {\"meta\": {}, \"type\": \"gateway\", \"version\": 4, \"address\": \"10.7.0.1\"}}]}, \"devname\": \"tapebf58f66-7f\", \"qbh_params\": null, \"vnic_type\": \"normal\", \"meta\": {}, \"details\": {\"port_filter\": true, \"ovs_hybrid_plug\": true}, \"address\": \"fa:16:3e:43:28:54\", \"active\": true, \"type\": \"ovs\", \"id\": \"ebf58f66-7f56-4111-bdd6-6f81954972cd\", \"qbg_params\": null}], \"created_at\": \"2016-06-06T22:24:36.000000\", \"deleted_at\": null, \"updated_at\": \"2016-06-06T22:41:44.000000\"}, \"hostname\": \"jd-tmp\", \"launched_on\": \"qos101\", \"display_description\": \"jd-tmp\", \"key_data\": \"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCc3+Qt64MiXHeCoIIbeLvwUYrm2vxXRyv5qyan+J/51hYfu2myVJdV+1X0vnpg27ZgdmgzrHRvvRExG9jjtI/+TvAQ3ZQZkvxxGARl6kP+xAYyUvBvyrzEqTzb7BPpN7Nr4K2Fv0ZNW0uTByQuCXR7IrfuX+0B5aTYVSDNVh2vGDLZ815FOAEUsJsV9LV4jg26MbuePwahGf0OsbiTAfgLyxGbiUb3vl6Yvzojd/GvcWcaLGw4OO7DvSbNMjXq8e6bOuFqBVaAxaPSvvyP1LKrAXQjuCDTCxAH255vbVYTpfGedeGzWF7mTZNb5NQAczbZkYdEHz8NbVmF7sRQA69L Generated by Nova\", \"deleted\": false, \"power_state\": 1, \"default_ephemeral_device\": null, \"progress\": 0, \"project_id\": \"65c3e5ee5ee0428caa5e5275c58ead61\", \"launched_at\": \"2016-06-06T22:24:53.000000\", \"config_drive\": \"\", \"node\": \"qos101\", \"ramdisk_id\": \"\", \"access_ip_v6\": null, \"access_ip_v4\": null, \"kernel_id\": \"\", \"key_name\": \"demo\", \"updated_at\": \"2016-06-06T22:24:53.000000\", \"host\": \"qos101\", \"root_gb\": 40, \"user_id\": \"1702902d510646838cff9303c7ff16ce\", \"task_state\": null, \"shutdown_terminate\": false, \"cell_name\": null, \"ephemeral_key_uuid\": null, \"locked\": false, \"name\": \"instance-0000084d\", \"created_at\": \"2016-06-06T22:24:36.000000\", \"locked_by\": null, \"launch_index\": 0, \"memory_mb\": 4096, \"vcpus\": 2, \"image_ref\": \"e19aa5bf-bb11-4e39-a706-0fd438beee50\", \"architecture\": null, \"auto_disk_config\": false, \"os_type\": null, \"scheduled_at\": null}}, \"_unique_id\": \"8b5f263f7b5a4642acd47861ee53222d\", \"_context_resource_uuid\": null, \"_context_instance_lock_checked\": false, \"_context_user\": null, \"_context_user_id\": null, \"_context_project_name\": null, \"_context_read_deleted\": \"no\", \"_context_user_identity\": \"- - - - -\", \"_reply_q\": \"reply_0d0c2a1003ac41c2b2d3ad0e0de76d1c\", \"_context_auth_token\": null, \"_context_show_deleted\": false, \"_context_tenant\": null, \"_context_roles\": [], \"_context_is_admin\": true, \"version\": \"2.0\", \"_context_project_id\": null, \"_context_project_domain\": null, \"_context_timestamp\": \"2017-06-05T19:32:34.641731\", \"_context_user_domain\": null, \"_context_user_name\": null, \"method\": \"get_ec2_ids\", \"_context_remote_address\": null}" 55 | 56 | //fmt.Fprintf( os.Stderr, ">>>> original\n %s\n\n", jstring ) 57 | 58 | jbytes = []byte( jstring ); 59 | jif, err := jsontools.Defrock_2_jif( jbytes[:] ) 60 | 61 | if err != nil { 62 | fmt.Fprintf( os.Stderr, "errors unpacking json: %s [FAIL]\n", err ) 63 | t.Fail() 64 | return 65 | } 66 | 67 | if jif == nil { 68 | fmt.Fprintf( os.Stderr, "no error, but jif was nil" ) 69 | t.Fail(); 70 | return 71 | } 72 | fmt.Fprintf( os.Stderr, "[OK] defrocked without errors\n" ) 73 | 74 | // --- refrock the resulting map from jstring which should produce a valid json string, but without the bleeding escaped bits of json 75 | s := jsontools.Frock_jmap( jif ) 76 | fmt.Fprintf( os.Stderr, "frocked output:\n%s\n\n", s ) 77 | fmt.Fprintf( os.Stderr, "\nvalidate above output at https://jsonlint.com/ \n" ) 78 | if has_escapes( s ) { 79 | fmt.Fprintf( os.Stderr, "[FAIL] there are unexpected escapes in the resulting json string\n" ) 80 | t.Fail( ) 81 | } 82 | 83 | s, err = jsontools.Refrock( jstring ) // does the same thing as above via the Refrock() function 84 | if err != nil { 85 | fmt.Fprintf( os.Stderr, "[FAIL] refrock attempt failed: %s", err ) 86 | t.Fail() 87 | } else { 88 | fmt.Fprintf( os.Stderr, "refrocked output:\n%s\n\n", s ) 89 | fmt.Fprintf( os.Stderr, "\nvalidate above output at https://jsonlint.com/ \n" ) 90 | if has_escapes( s ) { 91 | fmt.Fprintf( os.Stderr, "[FAIL] there are unexpected escapes in the resulting json string\n" ) 92 | t.Fail( ) 93 | } 94 | } 95 | 96 | fmt.Fprintf( os.Stderr, "===== end defrock testing ======\n\n" ) 97 | } 98 | 99 | -------------------------------------------------------------------------------- /http_logger/http_logger.go: -------------------------------------------------------------------------------- 1 | // vi: sw=4 ts=4: 2 | /* 3 | --------------------------------------------------------------------------- 4 | Copyright (c) 2013-2015 AT&T Intellectual Property 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | --------------------------------------------------------------------------- 18 | */ 19 | 20 | /* 21 | Mnemonic: http_logger 22 | Author: Robert Eby 23 | Mods: 10 Aug 2015 - Created. 24 | 17 Nov 2015 - Log query string as well 25 | */ 26 | 27 | /* 28 | This package provides a basic logger to log HTTP requests in the format that will 29 | be familiar to anyone who has ever used Apache. The logfiles are placed in the 30 | directory specified when the Http_Logger object is created. For now only Apache 31 | "Common Log Format" is supported, although this could be extended in the future. 32 | The logfiles themselves will always be named access.log.YYYYMMDD, and will be rolled 33 | daily. 34 | */ 35 | package http_logger 36 | 37 | import ( 38 | "bytes" 39 | "fmt" 40 | "net/http" 41 | "os" 42 | "strings" 43 | "sync" 44 | "time" 45 | ) 46 | 47 | /* 48 | An object that knows how to log requests in Apache format to a logfile. 49 | For the time being, only Apache "Common Log Format" 50 | (http://httpd.apache.org/docs/1.3/logs.html#common) is supported. 51 | */ 52 | type Http_Logger struct { 53 | mut sync.Mutex // for synchronization 54 | fmt string // for right now, always set to the "common" format 55 | basenm string // for now, always "access.log" 56 | dir string // directory for logfiles 57 | lastday time.Time // day for last log entry 58 | logfile *os.File // if we opened the file, we can close it 59 | } 60 | 61 | /* 62 | Make a Http_Logger object. Specify the directory where logfiles should be placed. 63 | */ 64 | func Mk_Http_Logger(dir *string) (p *Http_Logger) { 65 | if dir == nil { 66 | s := "/tmp" 67 | dir = &s 68 | } 69 | p = &Http_Logger{ 70 | fmt: `%h %l %u %t "%r" %s %b`, 71 | basenm: "access.log", 72 | dir: *dir, 73 | lastday: time.Unix(0, 0), 74 | logfile: nil, 75 | } 76 | return 77 | } 78 | 79 | /* 80 | Rolling the log file, this is checked before a new log file is created 81 | */ 82 | func (p *Http_Logger) doRollover() bool { 83 | now := time.Now() 84 | a := (now.YearDay() != p.lastday.YearDay()) 85 | b := (now.Year() != p.lastday.Year()) 86 | return a || b 87 | } 88 | 89 | /* 90 | Closing the logfile making sure to close the logfile 91 | */ 92 | func (f *Http_Logger) Close() (err error) { 93 | f.mut.Lock() 94 | defer f.mut.Unlock() 95 | err = f.close() // sending to local close function to handle file close 96 | if err != nil { 97 | err = fmt.Errorf("unable to close the file: %s", err) 98 | } 99 | f.logfile = nil 100 | return 101 | } 102 | 103 | /* 104 | Have a private close function to avoid ambiguity while renaming 105 | */ 106 | func (f *Http_Logger) close() error { 107 | if f.logfile == nil { 108 | return nil 109 | } else { 110 | return f.logfile.Close() 111 | } 112 | } 113 | 114 | /* 115 | Change file name if the user want to change the default filename 116 | */ 117 | func (f *Http_Logger) Set_fname(fname string) (err error) { 118 | f.mut.Lock() 119 | defer f.mut.Unlock() 120 | if f.logfile == nil { // if the logfile is empty create file with new name 121 | f.basenm = fname 122 | return 123 | } else { 124 | err = f.close() // closing the file before create a new one 125 | if err != nil { 126 | err = fmt.Errorf("unable to close the file to set new filename: %s", err) 127 | return 128 | } 129 | f.logfile = nil 130 | now := time.Now() // to get the latest time for building log file name 131 | 132 | // building old file name format 133 | old_name := fmt.Sprintf("%s/%s.%4d%02d%02d", f.dir, f.basenm, now.Year(), now.Month(), now.Day()) 134 | f.basenm = fname // updating the basename var in struct object with user specified name 135 | new_name := fmt.Sprintf("%s/%s.%4d%02d%02d", f.dir, fname, now.Year(), now.Month(), now.Day()) 136 | 137 | err = os.Rename(old_name, new_name) // renaming the old file 138 | if err != nil { 139 | err = fmt.Errorf("Unable to rename the file : %s\n", err) 140 | return 141 | } 142 | return 143 | } 144 | } 145 | 146 | /* 147 | Get the filename to read the file 148 | */ 149 | func (f *Http_Logger) Get_fname() string { 150 | return f.basenm 151 | } 152 | 153 | /* 154 | Log the HTTP request represented by in to the logfile. user should be the user who made 155 | the request, and code and length are the HTTP status code, and length of the response body. 156 | */ 157 | func (p *Http_Logger) LogRequest(in *http.Request, user string, code int, length int) { 158 | p.mut.Lock() 159 | defer p.mut.Unlock() 160 | 161 | if p.logfile == nil || p.doRollover() { 162 | // Time to roll - close old file and open new one. 163 | now := time.Now() 164 | fname := fmt.Sprintf("%s/%s.%4d%02d%02d", p.dir, p.basenm, now.Year(), now.Month(), now.Day()) 165 | if p.logfile != nil { 166 | p.logfile.Close() 167 | p.logfile = nil 168 | } 169 | f, err := os.OpenFile(fname, os.O_CREATE|os.O_WRONLY, 0664) 170 | if err != nil { 171 | return 172 | } 173 | p.lastday = now 174 | p.logfile = f 175 | } 176 | 177 | msg := bytes.NewBufferString("") 178 | ch := strings.Split(p.fmt, "") 179 | for ix := 0; ix < len(ch); ix++ { 180 | if ch[ix] == "%" && (ix+1) < len(ch) { 181 | ix++ 182 | switch ch[ix] { 183 | case "b": 184 | msg.WriteString(fmt.Sprintf("%d", length)) 185 | 186 | case "h": 187 | addr := in.RemoteAddr 188 | if addr[0:1] == "[" { 189 | // Strip port from IPv6 address 190 | ix := strings.Index(addr, "]") 191 | if ix > 0 { 192 | addr = addr[0 : ix+1] 193 | } 194 | } else { 195 | // Strip port from IPv4 address 196 | ix := strings.Index(addr, ":") 197 | if ix > 0 { 198 | addr = addr[0:ix] 199 | } 200 | } 201 | msg.WriteString(addr) 202 | 203 | case "l": 204 | msg.WriteString("-") 205 | 206 | case "r": 207 | url := in.URL.Path 208 | q := in.URL.RawQuery 209 | if q != "" { 210 | url = url + "?" + q 211 | } 212 | msg.WriteString(fmt.Sprintf("%s %s %s", in.Method, url, in.Proto)) 213 | 214 | case "s": 215 | msg.WriteString(fmt.Sprintf("%d", code)) 216 | 217 | case "t": 218 | t := time.Now().UTC() 219 | mon := t.Month().String()[0:3] 220 | date := fmt.Sprintf(`[%02d/%s/%4d:%02d:%02d:%02d -0000]`, t.Day(), mon, t.Year(), t.Hour(), t.Minute(), t.Second()) 221 | msg.WriteString(date) 222 | 223 | case "u": 224 | msg.WriteString(user) 225 | 226 | default: 227 | msg.WriteString("%") 228 | msg.WriteString(ch[ix]) 229 | } 230 | } else { 231 | msg.WriteString(ch[ix]) 232 | } 233 | } 234 | msg.WriteString("\n") 235 | if p.logfile != nil { 236 | p.logfile.Seek(0, os.SEEK_END) 237 | fmt.Fprint(p.logfile, msg.String()) 238 | } else { 239 | fmt.Print(msg.String()) 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /jsontools/defrock.go: -------------------------------------------------------------------------------- 1 | // vi: sw=4 ts=4: 2 | /* 3 | --------------------------------------------------------------------------- 4 | Copyright (c) 2013-2015 AT&T Intellectual Property 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | --------------------------------------------------------------------------- 18 | */ 19 | 20 | 21 | /* 22 | 23 | Mnemonic: defrock 24 | Abstract: Functions to support 'refrocking' a json string where some contents 25 | of the json are strings of json (embedded). When the desired result 26 | is to have the embedded strings actually be a part of the hierarchy 27 | the Refrock() function can be used to convert the string into a 28 | string of json. If a usable map of json is desired, then the 29 | Defrock_2_jif() function can be used to completely defrock the 30 | string leaving it in a map[string]interface{} which can be used by 31 | other tools. 32 | 33 | Date: 2 June 2017 34 | Author: E. Scott Daniels 35 | 36 | Mods: 37 | */ 38 | 39 | package jsontools 40 | 41 | import ( 42 | "encoding/json" 43 | "fmt" 44 | "reflect" 45 | ) 46 | 47 | // --------------------- public ---------------------------------------------------------------------- 48 | /* 49 | Refrock takes json in the form of string, *string or []byte and unencapsulate any embedded strings which 50 | are json. The result is a string with valid json but with any embedded json strings in the original 51 | made into a part of the resulting 'hierarchy'. This is a two step process which simply uses the 52 | Defrock_2_jif() and Frock_jmap() functions below. 53 | */ 54 | func Refrock( jblob interface{} ) ( string, error ) { 55 | jif, err := Defrock_2_jif( jblob ) 56 | if err != nil { 57 | return "", err 58 | } 59 | 60 | return Frock_jmap( jif ), nil 61 | } 62 | 63 | /* 64 | Defrock_2_jif takes either json input (as string, *string or []byte), or a map[]interface{} and completely defrock 65 | it into a json inteface (jif). This assumes that some strings may be json which should be recursively 66 | defrocked. Yes, what system might actually embed json as a string in json.... Openstack; grrrr. 67 | */ 68 | func Defrock_2_jif( iblob interface{} ) ( jif interface{}, err error ) { 69 | var ( 70 | jblob []byte 71 | ojif interface{} // orignal unpacked -- may contain json strings 72 | ) 73 | 74 | need_unpack := true 75 | 76 | switch thing := iblob.(type) { 77 | case string: 78 | jblob = []byte( thing ) 79 | 80 | case *string: 81 | jblob = []byte( *thing ) 82 | 83 | case []byte: 84 | jblob = thing 85 | 86 | default: 87 | need_unpack = false // we assume it's a map[] of interface and will fail later if not 88 | ojif = thing 89 | } 90 | 91 | if need_unpack { 92 | err = json.Unmarshal( jblob, &ojif ) // unpack the json into a map[]interface{} 93 | if err != nil { 94 | return nil, fmt.Errorf( "unable to unpack json into jif: %s\n", err ) 95 | } 96 | } 97 | 98 | if m, ok := ojif.( map[string]interface{} ); ok { // run the result and look for strings that might be embedded json; unpack those too 99 | for k, v := range m { 100 | switch thing := v.(type) { 101 | case string: 102 | sjif, err := Defrock_2_jif( rm_esc( thing ) ) // attempt to defrock json; if successful then we save the interface 103 | if err == nil { 104 | m[k] = sjif 105 | } 106 | 107 | case map[string]interface{}: 108 | sjif, err := Defrock_2_jif( thing ) // attempt to defrock embedded json strings; if successful then we save the interface 109 | if err == nil { 110 | m[k] = sjif 111 | } 112 | 113 | case []interface{}: 114 | for e, ele := range thing { 115 | switch ele.(type) { 116 | case string: 117 | sjif, err := Defrock_2_jif( rm_esc( ele.(string) ) ) // if it's json, defrock and save the interface 118 | if err == nil { 119 | thing[e] = sjif 120 | } 121 | 122 | case map[string]interface{}: // recurse to possibly defrock what lies below 123 | sjif, err := Defrock_2_jif( ele ) 124 | if err == nil { 125 | thing[e] = sjif 126 | } 127 | 128 | default: 129 | // do nothing; just leave what we found 130 | } 131 | } 132 | 133 | default: 134 | // do nothing; leave what we found 135 | } 136 | } 137 | } else { 138 | return nil, fmt.Errorf( "pointer to jif map wasn't to a map[string]interface{}: %s", reflect.TypeOf( ojif ) ) 139 | } 140 | 141 | return ojif, nil 142 | } 143 | 144 | /* 145 | Given an interface, put it into a json frock (format it with quotes and squiggles). 146 | */ 147 | func frock_if( jif interface{} ) ( string ) { 148 | 149 | switch thing := jif.(type) { 150 | case string: 151 | return fmt.Sprintf( "%q", add_esc( thing ) ) 152 | 153 | case int: 154 | return fmt.Sprintf( "%d", thing ) 155 | 156 | case bool: 157 | return fmt.Sprintf( "%v", thing ) 158 | 159 | case float64: 160 | return fmt.Sprintf( "%0.3f", thing ) 161 | 162 | case map[string]interface{}: 163 | return Frock_jmap( thing ) 164 | 165 | case []interface{}: 166 | asep := " " 167 | jstr := "[" 168 | for _, iv := range thing { 169 | jstr += asep + frock_if( iv ) 170 | asep = ", " 171 | } 172 | jstr += " ]" 173 | return jstr 174 | 175 | case nil: 176 | return "null" 177 | 178 | default: 179 | //fmt.Fprintf( os.Stderr, "frock_if: unknown type: %s\n", reflect.TypeOf( thing ) ) 180 | } 181 | 182 | return "" 183 | } 184 | /* 185 | Frock_jmap accepts a jmap (keyed interface values) and generates the corresponding formatted (frocked) json 186 | in a string. 187 | */ 188 | func Frock_jmap( jif interface{} ) ( json string ) { 189 | jmap := make( map[string]interface{}, 1024 ) 190 | 191 | jmap, ok := jif.( map[string]interface{} ) // right now we only support a jmap 192 | if ! ok { 193 | return "" 194 | } 195 | 196 | jstr := "{" 197 | sep := " " 198 | 199 | for k, v := range jmap { 200 | jstr += fmt.Sprintf( "%s%q: ", sep, k ) 201 | jstr += frock_if( v ) 202 | sep = ", " 203 | } 204 | 205 | jstr += "}" 206 | return jstr 207 | } 208 | 209 | 210 | // ----------------- private --------------- 211 | /* 212 | Escape quotes in the string. 213 | */ 214 | func add_esc( unq string ) ( quoted string ) { 215 | 216 | b := make( []byte, len( unq ) * 2 ) 217 | bi := 0 218 | for _, c := range( unq ) { 219 | if c == '"' { 220 | b[bi] = '\\' 221 | bi++ 222 | } 223 | b[bi] = byte( c ) 224 | bi++ 225 | } 226 | 227 | return string( b[0:bi] ) 228 | } 229 | 230 | /* 231 | Remove a layer of escapes from a string. 232 | */ 233 | func rm_esc( esc string ) ( string ) { 234 | b := make( []byte, len( esc ) ) 235 | s := []byte( esc ) 236 | 237 | bi := 0 238 | si := 0 239 | for ; si < len( esc )-1; { 240 | if s[si] == '\\' { 241 | si++ 242 | } 243 | b[bi] = s[si] 244 | bi++ 245 | si++ 246 | } 247 | 248 | if si < len( esc ) { 249 | b[bi] = s[si] 250 | bi++ 251 | } 252 | 253 | return string( b[0:bi] ) 254 | } 255 | -------------------------------------------------------------------------------- /bleater/bleater_test.go: -------------------------------------------------------------------------------- 1 | // vi: sw=4 ts=4: 2 | /* 3 | --------------------------------------------------------------------------- 4 | Copyright (c) 2013-2015 AT&T Intellectual Property 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | --------------------------------------------------------------------------- 18 | */ 19 | 20 | 21 | /* 22 | This test is a bit different than most 'go test' function sets as the 23 | test verifies that the package can be used, but output from the test 24 | is vetted by an external script which looks for various things, or the 25 | absence of things, in the log files that are created. Specifically, 26 | the strings looked for in the output are: 27 | '.*should show.*' (these are counted) 28 | '.*should NOT show.*' (these cause an error if seen) 29 | '.*baa_some.*:.*' (last field expected to be a multiple of m in n:m) 30 | '.*different time format.*' (time stamp only, no date expected) 31 | 32 | as well as the labels big, little and black. For the alternate time 33 | test, the timestamp is expected to present the time first (the date 34 | is not checked so omitting it, or presenting it second is fine). 35 | 36 | The vetting script also expects two files in /tmp to be created: 37 | bleat_test.log (containing messages from both little and big sheep) 38 | bleat_ls_test.log (containing only little sheep messages) 39 | 40 | It is possible for the 'go test' to pass, but for the vetting script to 41 | be unhappy with the output and fail the overall test. The vetting script 42 | should eliminate the human effort with respect to automating the test. 43 | */ 44 | 45 | package bleater_test 46 | 47 | import ( 48 | "testing" 49 | "os" 50 | "fmt" 51 | 52 | "github.com/att/gopkgs/bleater" 53 | ) 54 | 55 | /* 56 | create three sheep that bleat. Big sheep acts as the parent and thus 57 | the 'master' volume of all children can be controlled by setting the 58 | level on the big sheep. The little sheep is given to the big sheep 59 | as a child, but black-sheep is left on its own. Thus, when the level 60 | of big-sheep is changed to 2, a bleat by little sheep with a setting 61 | of 2 should be heard, but when black sheep bleats at 2 it should still 62 | be silent as it's level has not been increased and it has no parent. 63 | */ 64 | func TestBleater( t *testing.T ) { 65 | big_sheep := bleater.Mk_bleater( 1, os.Stderr ); // create the sheep 66 | little_sheep := bleater.Mk_bleater( 1, os.Stderr ); 67 | black_sheep := bleater.Mk_bleater( 1, os.Stderr ); // black sheep not added as child, so it should not get trickle down things 68 | 69 | big_sheep.Set_prefix( "big" ); // give them prefixes so we can see difference 70 | little_sheep.Set_prefix( "little" ); 71 | black_sheep.Set_prefix( "black" ); 72 | 73 | big_sheep.Add_child( little_sheep ); // big sheep gets baby 74 | 75 | 76 | little_sheep.Baa( 2, "%s", "should NOT show" ); // first tests from little sheep 77 | little_sheep.Baa( 1, "%s", "should show (1)" ); 78 | 79 | big_sheep.Set_level( 2 ); // up the master level 80 | little_sheep.Baa( 2, "%s", "should show (2)" ); // now little sheep's level 2 bleats should be heard 81 | 82 | black_sheep.Baa( 2, "%s", "should NOT show" ); // but not those from blackie 83 | 84 | big_sheep.Inc_level( ); 85 | little_sheep.Baa( 2, "%s", "should show after inc (2)" ); // both level 2 and 3 should continue to show 86 | little_sheep.Baa( 3, "%s", "should show after inc (3)" ); 87 | big_sheep.Dec_level( ); 88 | little_sheep.Baa( 3, "%s", "should NOT show after dec (3)" ); // but not after the level was dec'd (down to 2) 89 | little_sheep.Baa( 2, "%s", "should show after inc (2)" ); // but level 2 should continue to show 90 | 91 | big_sheep.Set_level( 0 ); // off the master volume 92 | little_sheep.Baa( 2, "%s", "should NOT show after mster off (1)" ); // with master off and little set to 1 this should not show 93 | little_sheep.Inc_level( ); // little should now be two and next should show 94 | little_sheep.Baa( 2, "%s", "should show after little sheep inc (2)" ); 95 | 96 | 97 | little_sheep.Set_tsformat( "15:04" ); 98 | little_sheep.Baa( 1, "%s", "should show with different time format" ); // little sheeps level2 bleats should be heard 99 | 100 | // --- test rolling of the log; big and little messages should cease to appear in stderr ------ 101 | fname := "/tmp/bleater_test.log" 102 | big_sheep.Baa( 0, "switching log file to %s; all big and little sheep messages should go there now; black messages should continue to stderr", fname ) 103 | 104 | err := big_sheep.Append_target( fname, true ) // will push to the child so both big and little sheep should write to file now 105 | if err != nil { 106 | fmt.Fprintf( os.Stderr, "failed to open log file for roll: %s: %s\n", fname, err ) 107 | t.Fail( ) 108 | os.Exit( 1 ) 109 | } 110 | 111 | big_sheep.Baa( 0, "big-sheep message should appear in new log file" ) 112 | little_sheep.Baa( 0, "little sheep message should appear in log file" ) 113 | black_sheep.Baa( 0, "black sheep should still be writing to stderr" ) 114 | 115 | 116 | black_sheep.Baa( 0, "Testing baa_some now" ) 117 | for i := 0; i < 50; i++ { 118 | black_sheep.Baa_some( "foo", 15, 1, "foo baa_some message 1:15 %d", i ) 119 | black_sheep.Baa_some( "bar", 5, 1, "bar baa_some message 1:5 %d", i ) 120 | } 121 | 122 | black_sheep.Baa_some_reset( "foo" ) // foo should write straight away, but not bar 123 | for i := 0; i < 50; i++ { 124 | black_sheep.Baa_some( "foo", 15, 1, "after reset: foo baa_some message 1:15 %d", i ) 125 | black_sheep.Baa_some( "bar", 5, 1, "after reset: bar baa_some message 1:5 %d", i ) 126 | } 127 | 128 | black_sheep.Baa( 0, "testing baa some reset" ) 129 | black_sheep.Baa_some_reset( "foo" ) 130 | black_sheep.Baa_some_reset( "bar" ) 131 | black_sheep.Set_level( 0 ) 132 | black_sheep.Baa( 1, "should NOT show [FAIL]" ) 133 | for i := 0; i < 15; i++ { // these should NOT be written 134 | black_sheep.Baa_some( "foo", 15, 1, "after reset: foo baa_some message 1:15 %d should NOT apear! [FAIL]", i ) 135 | black_sheep.Baa_some( "bar", 5, 1, "after reset: bar baa_some message 1:5 %d should NOT appear! [FAIL]", i ) 136 | } 137 | black_sheep.Baa( 0, "end suppression test (no lines should say 'fail' above." ) 138 | 139 | black_sheep.Set_level( 1 ) // reset level, these should both appear! 140 | black_sheep.Baa_some( "foo", 15, 1, "after reset: foo baa_some message (after level reset) 1:15 0" ) 141 | black_sheep.Baa_some( "bar", 5, 1, "after reset: bar baa_some message (after level reset) 1:5 0" ) 142 | black_sheep.Baa( 0, "two lines should have been written between the end suppression message and this" ) 143 | 144 | fname = "/tmp/bleater_ls_test.log" 145 | little_sheep.Create_target( fname, true ) // direct little sheep off to it's own log now 146 | black_sheep.Baa( 0, "should not go into little sheep log file" ) 147 | big_sheep.Baa( 0, "should not go into little sheep log file" ) 148 | little_sheep.Baa( 0, "should go into little sheep log file" ) 149 | } 150 | 151 | 152 | -------------------------------------------------------------------------------- /test/debug_ssh_broker.go: -------------------------------------------------------------------------------- 1 | // vi: sw=4 ts=4: 2 | /* 3 | --------------------------------------------------------------------------- 4 | Copyright (c) 2013-2015 AT&T Intellectual Property 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | --------------------------------------------------------------------------- 18 | */ 19 | 20 | /* 21 | Mnemonic: debug_ssh_broker.go 22 | Abstract: More than just functional tests that might be provided in a *_test.go 23 | module, this provides an end to end test of the ssh_broker. 24 | Author: E. Scott Daniels 25 | Date: 23 December 2014 26 | Mods: Fixed fmt statement in printf. 27 | 21 Sep 2015 - Added repeat function. 28 | */ 29 | 30 | package main 31 | 32 | import ( 33 | "flag" 34 | "fmt" 35 | "os" 36 | "strings" 37 | "time" 38 | 39 | "github.com/att/gopkgs/ssh_broker" // CAUTION: ssh_broker requires a fairly recent version of go 40 | ) 41 | 42 | 43 | /* 44 | run a local script -- run as a go routine to run multiple in parallel 45 | */ 46 | func test_script( broker *ssh_broker.Broker, ch chan int, host *string, script *string, parms *string, env_file *string ) { 47 | 48 | fmt.Fprintf( os.Stderr, "running commnand=%s parms=%s\n", *script, *parms ) 49 | stdout, stderr, err := broker.Run_on_host( *host, *script, *parms, *env_file ) 50 | if err != nil { 51 | fmt.Fprintf( os.Stderr, "command failed: %s: %s \n", *host, err ) 52 | fmt.Fprintf( os.Stderr, "%s", stderr.String() ) 53 | } else { 54 | fmt.Fprintf( os.Stderr, "command was successful:\n" ) 55 | fmt.Printf( "%s\n", stdout.String() ) 56 | } 57 | 58 | fmt.Fprintf( os.Stderr, "go routine done:%s \n", *parms ) 59 | ch <- 1 60 | 61 | return 62 | } 63 | 64 | /* 65 | run a command -- run as a go routine to run multiple in parallel 66 | */ 67 | func test_cmd( broker *ssh_broker.Broker, ch chan int, host *string, cmd *string ) { 68 | 69 | fmt.Fprintf( os.Stderr, "test_cmd: running command %s\n", *cmd ) 70 | stdout, stderr, err := broker.Run_cmd( *host, *cmd ) 71 | if err != nil { 72 | fmt.Fprintf( os.Stderr, "command failed: %s: %s \n", *host, err ) 73 | fmt.Fprintf( os.Stderr, "%s", stderr.String() ) 74 | } else { 75 | fmt.Fprintf( os.Stderr, "command was successful:\n" ) 76 | fmt.Printf( "%s\n", stdout.String() ) 77 | } 78 | 79 | fmt.Fprintf( os.Stderr, "go routine done: %s \n", *cmd ) 80 | ch <- 1 81 | 82 | return 83 | } 84 | 85 | /* 86 | Run as a goroutine to handle responses that are generated by 87 | asynch request submission. 88 | */ 89 | func handle_responses( ch chan int, rch chan *ssh_broker.Broker_msg ) { 90 | for { 91 | msg, is_open := <- rch 92 | if !is_open { 93 | fmt.Fprintf( os.Stderr, "ssh response channel was closed!\n" ) 94 | os.Exit( 1 ) 95 | } 96 | 97 | stdout, stderr, _, err := msg.Get_results( ) 98 | host, script, id := msg.Get_info( ) 99 | fmt.Fprintf( os.Stderr, "received response host=%s script=%s id=%d\n", host, script, id ) 100 | if err != nil { 101 | fmt.Fprintf( os.Stderr, "command failed: %s: %s \n", host, err ) 102 | fmt.Fprintf( os.Stderr, "%s", stderr.String() ) 103 | } else { 104 | fmt.Fprintf( os.Stderr, "command was successful:\n" ) 105 | fmt.Printf( "%s\n", stdout.String() ) 106 | } 107 | 108 | ch <- 1 // signal to main that one command finished 109 | } 110 | } 111 | 112 | func main( ) { 113 | var ch chan int 114 | var rch chan *ssh_broker.Broker_msg // response channel 115 | var err error 116 | var user *string 117 | 118 | def_user := os.Getenv( "USER" ) 119 | def_key := os.Getenv( "HOME" ) + "/.ssh/id_dsa" 120 | 121 | asynch := flag.Bool( "a", false, "asynch processing" ) 122 | cmd := flag.String( "c", "", "command to execute" ) 123 | repeat_delay := flag.Int( "d", 30, "repeat delay (seconds)" ) 124 | env_file := flag.String( "e", "", "environment file for script" ) 125 | host_list := flag.String( "h", "localhost", "host name(s) (comma sep)" ) 126 | key := flag.String( "k", def_key, "key file" ) 127 | parms := flag.String( "p", "", "parms" ) 128 | parallel := flag.Int( "P", 1, "parallel scripts" ) 129 | rsync := flag.String( "r", "", "rsync files:dir" ) 130 | repeat := flag.Int( "R", 0, "Repeat n times" ) 131 | script := flag.String( "s", "test_script", "script to execute" ) 132 | user = flag.String ( "u", def_user, "user name" ) 133 | flag.Parse() 134 | 135 | if *key == "" { 136 | home := os.Getenv( "HOME" ) 137 | k := fmt.Sprintf( "%s/.ssh/id_rsa", home ) 138 | _, err = os.Stat( k ) 139 | if err != nil { 140 | k = fmt.Sprintf( "%s/.ssh/id_dsa", home ) 141 | _, err = os.Stat( k ) 142 | if err != nil { 143 | fmt.Fprintf( os.Stderr, "cannot find default key file ~/.ssh/id_dsa\n" ) 144 | os.Exit( 1 ) 145 | } 146 | } 147 | key = &k 148 | } 149 | 150 | keys := []string { *key } 151 | broker := ssh_broker.Mk_broker( *user, keys ) 152 | if broker == nil { 153 | fmt.Fprintf( os.Stderr, "unable to create an ssh broker\n" ) 154 | os.Exit( 1 ) 155 | } 156 | defer broker.Close( ) 157 | broker.Start_initiators( 30 ) // start three more initiators 158 | 159 | ch = make( chan int, 10 ) // goroutine writes back to us on this 160 | if *asynch { 161 | rch = make( chan *ssh_broker.Broker_msg, 10 ) 162 | go handle_responses( ch, rch ) 163 | } 164 | 165 | if *rsync != "" { 166 | toks := strings.Split( *rsync, ":" ) // assume file,file,file:dest_dir 167 | if len( toks ) != 2 { 168 | fmt.Fprintf( os.Stderr, "bad rsynch string, expected file,file,file...:destdir\n" ) 169 | os.Exit( 1 ) 170 | } 171 | 172 | broker.Add_rsync( &toks[0], &toks[1] ) 173 | broker.Set_verbose( true ) 174 | } 175 | 176 | host := strings.Split( *host_list, "," ) 177 | 178 | for { 179 | wait4 := 0 180 | for i := 0; i < *parallel; i++ { 181 | for j := range host { 182 | wait4++ 183 | if ! *asynch { 184 | if *cmd == "" { 185 | fmt.Fprintf( os.Stderr, "running synch script parms=%s\n", *parms ) 186 | go test_script( broker, ch, &host[j], script, parms, env_file ) 187 | } else { 188 | fmt.Fprintf( os.Stderr, "running synch command %s\n", *cmd ) 189 | go test_cmd( broker, ch, &host[j], cmd ) 190 | } 191 | } else { 192 | fmt.Fprintf( os.Stderr, "running asynch command on %s parallel=%d parms=%s\n", host[j], i, *parms ) 193 | if *cmd == "" { // -c not supplied 194 | err = broker.NBRun_on_host( host[j], *script, *parms, (i*100)+j, rch ) 195 | } else { 196 | err = broker.NBRun_cmd( host[j], *cmd, (i*100)+j, rch ) 197 | } 198 | if err != nil { 199 | fmt.Fprintf( os.Stderr, "asynch command submit failed: host=%s parms=%s: %s\n", host[j], *parms, err ) 200 | } 201 | } 202 | } 203 | } 204 | 205 | for i := 0; i < wait4; i++ { 206 | fmt.Fprintf( os.Stderr, "waiting for %d to finish\n", wait4 - i ) 207 | <- ch 208 | } 209 | 210 | if *repeat <= 0 { 211 | break 212 | } else { 213 | fmt.Fprintf( os.Stderr, "delay before repeat: %ds\n", *repeat_delay ) 214 | time.Sleep( time.Second * time.Duration( *repeat_delay ) ) 215 | } 216 | 217 | *repeat-- 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /ipc/msgrtr/audience.go: -------------------------------------------------------------------------------- 1 | // vi: sw=4 ts=4: 2 | /* 3 | --------------------------------------------------------------------------- 4 | Copyright (c) 2013-2015 AT&T Intellectual Property 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | --------------------------------------------------------------------------- 18 | */ 19 | 20 | 21 | /* 22 | 23 | Mnemonic: audience.go 24 | Abstract: An audience is a tree of groups of listeners. Each node represents a 25 | specific set of messages (e.g. network link add) and contains a list of 26 | channels to broadcast matching messages to. A node's children are referenced 27 | by a map suc that the network node might have 'link' and 'router' children 28 | while the endpoint node might just have 'add', 'del', and 'mod' children. 29 | 30 | Date: 30 Oct 2015 31 | Author: E. Scott Daniels 32 | 33 | Mods: 12 Nov 2015 - Added support to accept listener data and include that 34 | when events are sent out. 35 | */ 36 | 37 | package msgrtr 38 | 39 | import ( 40 | "fmt" 41 | "strings" 42 | ) 43 | 44 | /* 45 | Envelope wraps an event such that user's data can be passed to each listener 46 | without exposing it and without the need to duplicate the event. 47 | */ 48 | type Envelope struct { 49 | Event *Event // the event received 50 | Ldata interface{} // user data that the listener registered with their channel 51 | } 52 | 53 | /* 54 | Specific info needed to manage a listener for an event 55 | */ 56 | type listener struct { 57 | ch chan *Envelope 58 | ldata interface{} // their data passed on an event call (in event as Ldata) 59 | } 60 | 61 | type audience struct { // describes a group of listeners 62 | llist []*listener // listeners registered at this 'level' 63 | subsect map[string]*audience // subsections (children) 64 | } 65 | 66 | 67 | /* 68 | Create an audience structure. subsect is the list of nodes in the tree 69 | below this node (e.g. network link add, or network link del). Subsects can be 70 | empty ("") if this is the leaf. 71 | */ 72 | func mk_audience( subsect string, lch chan *Envelope, ldata interface{} ) ( a *audience ) { 73 | 74 | a = &audience { } 75 | 76 | a.llist = make( []*listener, 0, 16 ) // this will automatically extend if needed 77 | a.subsect = make( map[string]*audience, 16 ) // 16 is a hint, not a hard limit 78 | 79 | if subsect != "" { 80 | tokens := strings.SplitN( subsect, ".", 2 ) 81 | rest := "" 82 | if len( tokens ) > 1 { 83 | rest = tokens[1] 84 | } 85 | 86 | a.subsect[tokens[0]] = mk_audience( rest, lch, ldata ) 87 | } else { 88 | if lch != nil { 89 | l := &listener { 90 | ch: lch, 91 | ldata: ldata, 92 | } 93 | a.llist = append( a.llist, l ) // add the first listener 94 | } 95 | } 96 | 97 | return a 98 | } 99 | 100 | /* 101 | Add a listener to an audience. Creates the child sections if needed. 102 | Subect is the rest of the path below this node where the listener is 103 | to be placed. 104 | */ 105 | func ( a *audience ) add_listener( subsect string, lch chan *Envelope, ldata interface{} ) { 106 | if subsect != "" { 107 | tokens := strings.SplitN( subsect, ".", 2 ) 108 | rest := "" 109 | if len( tokens ) > 1 { 110 | rest = tokens[1] 111 | } 112 | if a.subsect[tokens[0]] == nil { // new child; make it 113 | a.subsect[tokens[0]] = mk_audience( rest, lch, ldata ) 114 | } else { 115 | a.subsect[tokens[0]].add_listener( rest, lch, ldata ) // just add if existing 116 | } 117 | } else { // at the leaf, add the listener to this list 118 | if lch != nil { 119 | l := &listener { 120 | ch: lch, 121 | ldata: ldata, 122 | } 123 | a.llist = append( a.llist, l ) // this will grow the slice if needed so must reassign 124 | } 125 | } 126 | } 127 | 128 | /* 129 | Remove a listener from an audience. The path (subsection) is searched until 130 | the first occurrance of channel is found. If the user has registered the 131 | channel in multiple places along the same path (dumb) this will only delete 132 | the 'top' one. 133 | */ 134 | func ( a *audience ) drop_listener( subsect string, lch chan *Envelope ) { 135 | if lch == nil { 136 | return 137 | } 138 | 139 | for k, v := range a.llist { // it could be here if full event path was longer, so must check 140 | if v.ch == lch { 141 | new_len := len( a.llist ) - 1 142 | //cl := make( []chan *Envelope, new_len, new_len + (new_len/2) ) // some room to grow 143 | ll := make( []*listener, new_len, new_len + (new_len/2) ) // some room to grow 144 | if k > 0 { 145 | copy( ll, a.llist[0:k-1] ) 146 | } 147 | if k < len( a.llist ) -1 { 148 | ll = append( a.llist[k+1:] ) 149 | } 150 | 151 | a.llist = ll 152 | return 153 | } 154 | } 155 | 156 | if subsect != "" { // just pass further down 157 | tokens := strings.SplitN( subsect, ".", 2 ) 158 | rest := "" 159 | if len( tokens ) > 1 { 160 | rest = tokens[1] 161 | } 162 | if a.subsect[tokens[0]] != nil { // existing child; send it on, no action if child doesn't exist 163 | a.subsect[tokens[0]].drop_listener( rest, lch ) 164 | } 165 | } 166 | } 167 | 168 | /* 169 | Send the event to all audience members. Path is the current point in the 170 | message band which is of the form a/b/c. We use 'a' to find the audience 171 | for the next level down assuming that our level string has been removed. 172 | We pass b/c (maybe more) to the next lower structure in the tree where 173 | the process repeats. 174 | 175 | Returns boolean indicating whether there was actually a listener which matched. 176 | If there wasn't, and the message has the ack flag on, the top level must send 177 | it to the dev/null path so that it can be acked with a 'missed' message. 178 | */ 179 | func ( a *audience ) bcast( event *Event, path string ) ( bool ){ 180 | 181 | sent := false 182 | 183 | for i := range a.llist { // for each listener registered here 184 | if a.llist[i] != nil { // shouldn't be nil ones, but don't chance it 185 | env := &Envelope { // stuff the envelope for this listener with their data 186 | Ldata: a.llist[i].ldata, 187 | Event: event, 188 | } 189 | 190 | sent = true 191 | sheep.Baa( 2, "send message to next channel" ) 192 | a.llist[i].ch <- env 193 | } 194 | } 195 | 196 | if path == "" { // nothing below here in the path, our job is finished 197 | return sent 198 | } 199 | 200 | child_sent := false 201 | tokens := strings.SplitN( path, ".", 2 ) 202 | if sa := a.subsect[tokens[0]]; sa != nil { 203 | sheep.Baa( 1, "send to child: %s", tokens[0] ) 204 | if len( tokens ) > 1 { 205 | child_sent = sa.bcast( event, tokens[1] ) 206 | } else { 207 | child_sent = sa.bcast( event, "" ) 208 | } 209 | } 210 | 211 | return sent || child_sent 212 | } 213 | 214 | /* 215 | Implement stringer interface. 216 | */ 217 | func ( a *audience ) String( ) ( string ) { 218 | s := fmt.Sprintf( "%d listeners, %d children", len( a.llist ), len( a.subsect ) ) 219 | for k, v := range a.subsect { 220 | s += fmt.Sprintf( " <%s: %s> ", k, v ) 221 | } 222 | 223 | return s 224 | } 225 | -------------------------------------------------------------------------------- /transform/tomap.go: -------------------------------------------------------------------------------- 1 | // vi: sw=4 ts=4: 2 | /* 3 | --------------------------------------------------------------------------- 4 | Copyright (c) 2013-2015 AT&T Intellectual Property 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | --------------------------------------------------------------------------- 18 | */ 19 | 20 | 21 | /* 22 | Mnemonic: tomap.go 23 | Absrtract: Various functions which transform something into a map. 24 | Date: 25 November 2015 25 | Author: E. Scott Daniels 26 | 27 | Mods: 24 Oct 2016 - Fix bug when inserting an interface. 28 | */ 29 | 30 | package transform 31 | 32 | import ( 33 | "fmt" 34 | "os" 35 | "reflect" 36 | ) 37 | 38 | var ( 39 | depth int = 0 40 | ) 41 | 42 | /* 43 | Accept a structure and build a map from its values. The map 44 | is [string]string, and the keys are taken from fields tagged with 45 | tags that match the tag_id string passed in. 46 | 47 | If the tag id is map, then a tag might be `map:"xyz"` where xyz is used 48 | as the name in the map, or `map:"_"` where the structure field name is 49 | used in the map. If the tag id "_" is passed in, then all fields in the 50 | structure are captured. 51 | 52 | This function will capture all simple fields (int, bool, float, etc.) and 53 | structures, anonynmous structures and pointers to structures. It will _NOT_ 54 | capture arrays or maps. 55 | */ 56 | func Struct_to_map( ustruct interface{}, tag_id string ) ( m map[string]string ) { 57 | var imeta reflect.Type 58 | var thing reflect.Value 59 | 60 | thing = reflect.ValueOf( ustruct ) // thing is the actual usr struct (in reflect container) 61 | if thing.Kind() == reflect.Ptr { 62 | thing = thing.Elem() // deref the pointer to get the real container 63 | imeta = thing.Type() // snag the type allowing for extraction of meta data 64 | } else { 65 | imeta = reflect.TypeOf( thing ) // convert input to a Type allowing for extraction of meta data 66 | } 67 | 68 | m = make( map[string]string ) 69 | return struct_to_map( thing, imeta, tag_id, m, "" ) 70 | } 71 | 72 | func dec_depth() { 73 | if depth > 0 { 74 | depth-- 75 | } 76 | } 77 | 78 | /* 79 | Insert a value into the map using its 'tag'. If the value is a struct, map or array (slice) 80 | then we recurse to insert each component (array/map) and invoke the struct function to 81 | dive into the struct extracting tagged fields. 82 | */ 83 | func insert_value( thing reflect.Value, tkind reflect.Kind, anon bool, tag string, tag_id string, m map[string]string, pfx string ) { 84 | depth++ 85 | if depth > 50 { 86 | fmt.Fprintf( os.Stderr, "recursion to deep in transform insert value: %s\n%s\n", pfx, tkind ) 87 | os.Exit( 1 ) 88 | } 89 | defer dec_depth() 90 | 91 | tag = pfx + tag 92 | switch tkind { 93 | case reflect.String: 94 | m[tag] = fmt.Sprintf( "%s", thing ) 95 | 96 | case reflect.Ptr: 97 | p := thing.Elem() 98 | switch p.Kind() { 99 | case reflect.String: 100 | m[tag] = fmt.Sprintf( "%s", p ) 101 | 102 | case reflect.Int, reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8: 103 | m[tag] = fmt.Sprintf( "%d", p.Int() ) 104 | 105 | case reflect.Uint, reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8: 106 | m[tag] = fmt.Sprintf( "%d", p.Uint() ) 107 | 108 | case reflect.Float64, reflect.Float32: 109 | m[tag] = fmt.Sprintf( "%f", p.Float() ) 110 | 111 | case reflect.Bool: 112 | m[tag] = fmt.Sprintf( "%v", p.Bool() ) 113 | 114 | case reflect.Struct: 115 | struct_to_map( p, p.Type(), tag_id, m, pfx + tag + "/" ) // recurse to process with a prefix which matches the field 116 | 117 | default: 118 | fmt.Fprintf( os.Stderr, "transform: ptr of this kind is not handled: %s\n", p.Kind() ) 119 | } 120 | 121 | case reflect.Uintptr: 122 | p := thing.Elem() 123 | m[tag] = fmt.Sprintf( "%d", p.Uint() ) 124 | 125 | case reflect.Int, reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8: 126 | m[tag] = fmt.Sprintf( "%d", thing.Int() ) 127 | 128 | case reflect.Uint, reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8: 129 | m[tag] = fmt.Sprintf( "%d", thing.Uint() ) 130 | 131 | case reflect.Float64, reflect.Float32: 132 | m[tag] = fmt.Sprintf( "%f", thing.Float() ) 133 | 134 | case reflect.Bool: 135 | m[tag] = fmt.Sprintf( "%v", thing.Bool() ) 136 | 137 | case reflect.Struct: 138 | if anon { 139 | struct_to_map( thing, thing.Type(), tag_id, m, pfx ) // recurse to process; anonymous fields share this level namespace 140 | } else { 141 | struct_to_map( thing, thing.Type(), tag_id, m, tag + "/" ) // recurse to process; tag becomes the prefix 142 | } 143 | 144 | case reflect.Slice: 145 | length := thing.Len() 146 | capacity := thing.Cap() 147 | 148 | mtag := fmt.Sprintf( "%s.cap", tag ) // set capacity and length of the slice 149 | m[mtag] = fmt.Sprintf( "%d", capacity ) 150 | mtag = fmt.Sprintf( "%s.len", tag ) 151 | m[mtag] = fmt.Sprintf( "%d", length ) 152 | 153 | for j := 0; j < length; j++ { 154 | v := thing.Slice( j, j+1 ) // value struct for the jth element (which will be a slice too :( 155 | vk := v.Type().Elem().Kind() // must drill down for the kind 156 | 157 | new_tag := fmt.Sprintf( "%s/%d", tag, j ) 158 | insert_value( thing.Index( j ), vk, false, new_tag, tag_id, m, "" ) // prefix already on the tag, the 'index' changes as we go through the slice 159 | } 160 | 161 | case reflect.Map: 162 | keys := thing.MapKeys() // list of all of the keys (values) 163 | for _, key := range keys { 164 | vk := thing.Type().Elem().Kind() // must drill down for the kind 165 | new_tag := fmt.Sprintf( "%s/%s", tag, key ) 166 | insert_value( thing.MapIndex( key ), vk, false, new_tag, tag_id, m, pfx ) // prefix stays the same, just gets a new tag 167 | } 168 | 169 | case reflect.Interface: 170 | p := thing.Elem() 171 | insert_value( p, p.Kind(), anon, tag, tag_id, m, "" ) // tag stays the same, so pfx is empty 172 | 173 | default: 174 | //fmt.Fprintf( os.Stderr, "transform: stm: field cannot be captured in a map: tag=%s type=%s val=%s\n", tag, thing.Kind(), reflect.ValueOf( thing ) ) 175 | } 176 | } 177 | 178 | 179 | /* 180 | We require the initial 'thing' passed to be a struct, this then is the real entry point, but 181 | is broken so that it can be recursively called as we encounter structs in the insert code. 182 | */ 183 | func struct_to_map( thing reflect.Value, imeta reflect.Type, tag_id string, m map[string]string, pfx string ) ( map[string]string ) { 184 | 185 | if thing.Kind() != reflect.Struct { 186 | return m 187 | } 188 | 189 | if m == nil { 190 | m = make( map[string]string ) 191 | } 192 | 193 | for i := 0; i < thing.NumField(); i++ { 194 | f := thing.Field( i ) // get the _value_ of the ith field 195 | fmeta := imeta.Field( i ) // get the ith field's metadata from Type (a struct_type) 196 | ftag := fmeta.Tag.Get( tag_id ) // get the field's datacache tag 197 | if ftag == "_" || tag_id == "_" { 198 | ftag = fmeta.Name 199 | } 200 | 201 | if ftag != "" || fmeta.Anonymous { // process all structs regardless of tag 202 | insert_value( f, f.Kind(), fmeta.Anonymous, ftag, tag_id, m, pfx ) 203 | } 204 | } 205 | 206 | return m 207 | } 208 | -------------------------------------------------------------------------------- /bleater/run_test.ksh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ksh 2 | 3 | # Abstract: The go test environment will test some things, but cannot verify output of 4 | # messages such as whether or not the baa_some functions supressed the correct 5 | # messages, and similar. This script will execute the test, and scan the output 6 | # for things expected and report success or failure based on that and the fail/pass 7 | # indication from the go test itself. 8 | # Author: E. Scott Daniels 9 | # Date: 03 January 2017 10 | # ---------------------------------------------------------------------------------------------- 11 | 12 | if ! go test >test.out 2>&1 13 | then 14 | echo "test failed, secondary checks not made [FAIL]" 15 | cat test.out 16 | rm test.out 17 | exit 1 18 | else 19 | echo "go test checks passed, making secondary checks" 20 | awk ' 21 | BEGIN { 22 | errors = 0 23 | } 24 | 25 | { 26 | print 27 | } 28 | 29 | /FAIL/ { next } 30 | 31 | /should show/ { 32 | show_count++ 33 | next 34 | } 35 | 36 | /shoud NOT show/ { 37 | errors++ 38 | printf( "[FAIL] previous message should not have appeareed in the output\n\n" ) 39 | next 40 | } 41 | 42 | /different time format/ { 43 | if( split( $2, a, ":" ) < 2 ) { # second field should just be a timestamp for this 44 | errors++ 45 | printf( "[FAIL] expected just a timestamp in previous message, not date then time\n\n" ) 46 | } 47 | next 48 | } 49 | 50 | /switching/ { 51 | switched = 1 52 | next 53 | } 54 | 55 | $3 == "little" { # after switch little timestamp is only time, so id is $3 56 | if( switched ) { 57 | printf( "[FAIL] little sheep message found in stderr stream after switch\n\n" ) 58 | errors++ 59 | } 60 | next 61 | } 62 | 63 | $4 == "big" { 64 | if( switched ) { 65 | printf( "[FAIL] big sheep message found in stderr stream after switch\n\n" ) 66 | errors++ 67 | } 68 | next 69 | } 70 | 71 | /baa_some.*:/ { # the last field should be a multiple of the second value in NF-1 72 | split( $(NF-1), a, ":" ) 73 | if( ($NF % a[2] ) != 0 ) { 74 | errors++ 75 | printf( "[FAIL] expected %d to be a multiple of %d in line %d\n\n", $NF, a[2], NR ) 76 | } 77 | next 78 | } 79 | 80 | END { 81 | if( show_count != 6 ) { 82 | printf( "[FAIL] did not see enough 'should' messages in the output\n\n" ) 83 | errors++ 84 | } 85 | 86 | exit( errors ? 1 : 0 ) 87 | } 88 | ' 0 )) 135 | then 136 | echo "[FAIL] one or more issues during secondary checks ($rc1, $rc2)" 137 | else 138 | echo "[PASS] all secondary checks good" 139 | fi 140 | 141 | rm -f test.out /tmp/bleater_test.log /tmp/bleater_ls_test.log 142 | exit $(( (rc1 + rc2) > 0 )) 143 | fi 144 | 145 | exit 146 | 147 | 148 | Expected output on stderr: 149 | 483468602 2017/01/03 18:36Z little [1] should show (1) 150 | 1483468602 2017/01/03 18:36Z little [2] should show (2) 151 | 1483468602 2017/01/03 18:36Z little [2] should show after inc (2) 152 | 1483468602 2017/01/03 18:36Z little [2] should show after little sheep inc (2) 153 | 1483468602 18:36 little [2] should show with different time format 154 | 1483468602 2017/01/03 18:36Z big [0] switching log file to /tmp/bleater_test.log; all messages should go there now 155 | 1483468602 2017/01/03 18:36Z [0] black sheep should still be writing to stderr 156 | 1483468602 2017/01/03 18:36Z [0] Testing baa_some now 157 | 1483468602 2017/01/03 18:36Z [1] foo baa_some message 1:15 0 158 | 1483468602 2017/01/03 18:36Z [1] bar baa_some message 1:5 0 159 | 1483468602 2017/01/03 18:36Z [1] bar baa_some message 1:5 5 160 | 1483468602 2017/01/03 18:36Z [1] bar baa_some message 1:5 10 161 | 1483468602 2017/01/03 18:36Z [1] foo baa_some message 1:15 15 162 | 1483468602 2017/01/03 18:36Z [1] bar baa_some message 1:5 15 163 | 1483468602 2017/01/03 18:36Z [1] bar baa_some message 1:5 20 164 | 1483468602 2017/01/03 18:36Z [1] bar baa_some message 1:5 25 165 | 1483468602 2017/01/03 18:36Z [1] foo baa_some message 1:15 30 166 | 1483468602 2017/01/03 18:36Z [1] bar baa_some message 1:5 30 167 | 1483468602 2017/01/03 18:36Z [1] bar baa_some message 1:5 35 168 | 1483468602 2017/01/03 18:36Z [1] bar baa_some message 1:5 40 169 | 1483468602 2017/01/03 18:36Z [1] foo baa_some message 1:15 45 170 | 1483468602 2017/01/03 18:36Z [1] bar baa_some message 1:5 45 171 | 1483468602 2017/01/03 18:36Z [1] after reset: foo baa_some message 1:15 0 172 | 1483468602 2017/01/03 18:36Z [1] after reset: bar baa_some message 1:5 0 173 | 1483468602 2017/01/03 18:36Z [1] after reset: bar baa_some message 1:5 5 174 | 1483468602 2017/01/03 18:36Z [1] after reset: bar baa_some message 1:5 10 175 | 1483468602 2017/01/03 18:36Z [1] after reset: foo baa_some message 1:15 15 176 | 1483468602 2017/01/03 18:36Z [1] after reset: bar baa_some message 1:5 15 177 | 1483468602 2017/01/03 18:36Z [1] after reset: bar baa_some message 1:5 20 178 | 1483468602 2017/01/03 18:36Z [1] after reset: bar baa_some message 1:5 25 179 | 1483468602 2017/01/03 18:36Z [1] after reset: foo baa_some message 1:15 30 180 | 1483468602 2017/01/03 18:36Z [1] after reset: bar baa_some message 1:5 30 181 | 1483468602 2017/01/03 18:36Z [1] after reset: bar baa_some message 1:5 35 182 | 1483468602 2017/01/03 18:36Z [1] after reset: bar baa_some message 1:5 40 183 | 1483468602 2017/01/03 18:36Z [1] after reset: foo baa_some message 1:15 45 184 | 1483468602 2017/01/03 18:36Z [1] after reset: bar baa_some message 1:5 45 185 | 1483468602 2017/01/03 18:36Z [0] testing baa some reset 186 | 1483468602 2017/01/03 18:36Z [0] end suppression test (no lines should say 'fail' above. 187 | 1483468602 2017/01/03 18:36Z [1] after reset: foo baa_some message 1:15 (after level reset) 188 | 1483468602 2017/01/03 18:36Z [1] after reset: bar baa_some message 1:5 (after level reset) 189 | 1483468602 2017/01/03 18:36Z [0] two lines should have been written between the end suppression message and this 190 | 191 | Expected output from the little sheep only log: 192 | 1483472633 19:43 little [0] should go into little sheep log file 193 | 194 | --------------------------------------------------------------------------------