diff --git a/.version b/.version index 66e2ae6..3989355 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -1.19.1 +1.20.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index 1912991..ffb15d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.20.0] - 2023-09-08 + +### Added + +- Added support for the VirtualNode type + ## [1.19.1] - 2023-01-11 ### Changed diff --git a/cmd/hbtd/api.go b/cmd/hbtd/api.go index 424cf90..1cf821d 100644 --- a/cmd/hbtd/api.go +++ b/cmd/hbtd/api.go @@ -1,6 +1,6 @@ // MIT License // -// (C) Copyright [2020-2021] Hewlett Packard Enterprise Development LP +// (C) Copyright [2020-2021,2023] Hewlett Packard Enterprise Development LP // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), @@ -30,10 +30,10 @@ import ( ) type Route struct { - Name string - Method string - Pattern string - HandlerFunc http.HandlerFunc + Name string + Method string + Pattern string + HandlerFunc http.HandlerFunc } type Routes []Route @@ -51,71 +51,69 @@ const ( URL_HEALTH = URL_ROOT + "/health" ) - // Generate the API routes func newRouter(routes []Route) *mux.Router { - router := mux.NewRouter().StrictSlash(true) - for _, route := range routes { - var handler http.Handler - handler = route.HandlerFunc - router. - Methods(route.Method). - Path(route.Pattern). - Name(route.Name). - Handler(handler) - } - return router + router := mux.NewRouter().StrictSlash(true) + for _, route := range routes { + var handler http.Handler + handler = route.HandlerFunc + router. + Methods(route.Method). + Path(route.Pattern). + Name(route.Name). + Handler(handler) + } + return router } // Create the API route descriptors. func generateRoutes() Routes { - return Routes{ - Route{"hbRcv", - strings.ToUpper("Post"), - URL_HEARTBEAT, - hbRcv, - }, - Route{"hbRcvXName", - strings.ToUpper("Post"), - URL_HEARTBEAT + "/{xname}", - hbRcvXName, - }, - Route{"params_get", - strings.ToUpper("Get"), - URL_PARAMS, - paramsIO, - }, - Route{"params_patch", - strings.ToUpper("Patch"), - URL_PARAMS, - paramsIO, - }, - Route{"doHealth", - strings.ToUpper("Get"), - URL_HEALTH, - doHealth, - }, - Route{"doLiveness", - strings.ToUpper("Get"), - URL_LIVENESS, - doLiveness, - }, - Route{"doReadiness", - strings.ToUpper("Get"), - URL_READINESS, - doReadiness, - }, - Route{"hbStates", - strings.ToUpper("Post"), - URL_HB_STATES, - hbStates, - }, - Route{"hbStateSingle", - strings.ToUpper("Get"), - URL_HB_STATE + "/{xname}", - hbStateSingle, - }, + return Routes{ + Route{"hbRcv", + strings.ToUpper("Post"), + URL_HEARTBEAT, + hbRcv, + }, + Route{"hbRcvXName", + strings.ToUpper("Post"), + URL_HEARTBEAT + "/{xname}", + hbRcvXName, + }, + Route{"params_get", + strings.ToUpper("Get"), + URL_PARAMS, + paramsIO, + }, + Route{"params_patch", + strings.ToUpper("Patch"), + URL_PARAMS, + paramsIO, + }, + Route{"doHealth", + strings.ToUpper("Get"), + URL_HEALTH, + doHealth, + }, + Route{"doLiveness", + strings.ToUpper("Get"), + URL_LIVENESS, + doLiveness, + }, + Route{"doReadiness", + strings.ToUpper("Get"), + URL_READINESS, + doReadiness, + }, + Route{"hbStates", + strings.ToUpper("Post"), + URL_HB_STATES, + hbStates, + }, + Route{"hbStateSingle", + strings.ToUpper("Get"), + URL_HB_STATE + "/{xname}", + hbStateSingle, + }, } } - diff --git a/cmd/hbtd/hbtd.go b/cmd/hbtd/hbtd.go index 53926ba..1b2edd9 100644 --- a/cmd/hbtd/hbtd.go +++ b/cmd/hbtd/hbtd.go @@ -1,6 +1,6 @@ // MIT License // -// (C) Copyright [2018-2021] Hewlett Packard Enterprise Development LP +// (C) Copyright [2018-2021,2023] Hewlett Packard Enterprise Development LP // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), @@ -52,7 +52,7 @@ import ( "syscall" "time" - "github.com/Cray-HPE/hms-base" + "github.com/Cray-HPE/hms-base/v2" "github.com/Cray-HPE/hms-hmetcd" "github.com/Cray-HPE/hms-msgbus" ) diff --git a/cmd/hbtd/health.go b/cmd/hbtd/health.go index ce6ba14..ca4b884 100644 --- a/cmd/hbtd/health.go +++ b/cmd/hbtd/health.go @@ -1,17 +1,17 @@ // MIT License -// -// (C) Copyright [2020-2021] Hewlett Packard Enterprise Development LP -// +// +// (C) Copyright [2020-2021,2023] Hewlett Packard Enterprise Development LP +// // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: -// +// // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL @@ -20,8 +20,6 @@ // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. - - package main import ( @@ -31,7 +29,7 @@ import ( "net/http" "time" - "github.com/Cray-HPE/hms-base" + "github.com/Cray-HPE/hms-base/v2" ) // HealthResponse - used to report service health stats @@ -44,13 +42,12 @@ type HealthResponse struct { var hsmReady = false var stopCheckHSM = false - // Periodically check on the availability of HSM. func checkHSM() { var offBase int64 - if (app_params.nosm.int_param != 0) { + if app_params.nosm.int_param != 0 { return } @@ -59,33 +56,33 @@ func checkHSM() { offBase = time.Now().Unix() for { - if (stopCheckHSM) { + if stopCheckHSM { return } lrdy := false - req,err := http.NewRequest("GET",url,nil) - if (err != nil) { - hbtdPrintf("ERROR: HSM check, can't create request: %v",err) + req, err := http.NewRequest("GET", url, nil) + if err != nil { + hbtdPrintf("ERROR: HSM check, can't create request: %v", err) } else { - base.SetHTTPUserAgent(req,serviceName) - rsp,rerr := htrans.client.Do(req) - if (rerr == nil) { - if (rsp.Body != nil) { + base.SetHTTPUserAgent(req, serviceName) + rsp, rerr := htrans.client.Do(req) + if rerr == nil { + if rsp.Body != nil { rsp.Body.Close() } - if (rsp.StatusCode == http.StatusOK) { + if rsp.StatusCode == http.StatusOK { lrdy = true } } } hsmReady = lrdy - if (!lrdy) { + if !lrdy { tdiff := time.Now().Unix() - offBase - hbtdPrintf("HSM is not responsive (%d seconds).",tdiff) + hbtdPrintf("HSM is not responsive (%d seconds).", tdiff) } else { offBase = time.Now().Unix() - if (!pstat) { + if !pstat { offBase = time.Now().Unix() hbtdPrintf("HSM is responsive.") } @@ -96,17 +93,17 @@ func checkHSM() { } //Wait until HSM is ready. NOTE: this may be temporary. There needs to -//be improvements on how to handle HSM coming and going. Once that is +//be improvements on how to handle HSM coming and going. Once that is //done this may be able to be removed. func waitForHSM() { - if (app_params.nosm.int_param != 0) { + if app_params.nosm.int_param != 0 { return } hbtdPrintf("Waiting for HSM to be responsive...") for { - if (hsmReady) { + if hsmReady { return } diff --git a/cmd/hbtd/health_test.go b/cmd/hbtd/health_test.go index a6b2bfd..6a5ce9b 100644 --- a/cmd/hbtd/health_test.go +++ b/cmd/hbtd/health_test.go @@ -1,6 +1,6 @@ // MIT License // -// (C) Copyright [2020-2021] Hewlett Packard Enterprise Development LP +// (C) Copyright [2020-2021,2023] Hewlett Packard Enterprise Development LP // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), @@ -20,14 +20,13 @@ // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. - package main import ( - "fmt" - "crypto/tls" "bytes" + "crypto/tls" "encoding/json" + "fmt" "io/ioutil" "net/http" "net/http/httptest" @@ -212,7 +211,7 @@ func TestHealth(t *testing.T) { if err != nil { t.Fatalf("ERROR unmarshalling GET response body:%s", err.Error()) } - kp = fmt.Sprintf("Initialization key present: %s",HBTD_HEALTH_OK) + kp = fmt.Sprintf("Initialization key present: %s", HBTD_HEALTH_OK) if stats.KvStoreStatus != kp { t.Fatal("Expected KV Store initialized") } @@ -236,7 +235,7 @@ func TestHSMReadies(t *testing.T) { t.Logf("**** Testing HSM readiness ****") go checkHSM() time.Sleep(10 * time.Second) - if (hsmReady == true) { + if hsmReady == true { t.Errorf("HSM shows ready, should not be.") } stopCheckHSM = true diff --git a/cmd/hbtd/track.go b/cmd/hbtd/track.go index 5975d37..b41b008 100644 --- a/cmd/hbtd/track.go +++ b/cmd/hbtd/track.go @@ -1,17 +1,17 @@ // MIT License -// -// (C) Copyright [2018-2021] Hewlett Packard Enterprise Development LP -// +// +// (C) Copyright [2018-2021,2023] Hewlett Packard Enterprise Development LP +// // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: -// +// // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL @@ -20,26 +20,27 @@ // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. - package main import ( - "net/http" - "encoding/json" - "time" - "io/ioutil" - "bytes" - "context" - "strconv" - "github.com/Cray-HPE/hms-base" - "reflect" - "fmt" - "os" - "strings" - "sync" - - "github.com/gorilla/mux" - "github.com/Cray-HPE/hms-hmetcd" + "bytes" + "context" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "os" + "reflect" + "strconv" + "strings" + "sync" + "time" + + base "github.com/Cray-HPE/hms-base/v2" + "github.com/Cray-HPE/hms-xname/xnametypes" + + hmetcd "github.com/Cray-HPE/hms-hmetcd" + "github.com/gorilla/mux" ) ///////////////////////////////////////////////////////////////////////////// @@ -49,71 +50,71 @@ import ( // Data structure to hold heartbeat tracking info type hbinfo struct { - Component string `json:"Component"` //Component XName - Last_hb_rcv_time string `json:"Last_hb_rcv_time"` //Time last HB was received - Last_hb_timestamp string `json:"Last_hb_timestamp"` //ISO8601 time stamp, set by sender - Last_hb_status string `json:"Last_hb_status"` //Any special status of last HB, from sender - Had_warning string `json:"Had_warning"` //Flag to mark start/stop edge conditions + Component string `json:"Component"` //Component XName + Last_hb_rcv_time string `json:"Last_hb_rcv_time"` //Time last HB was received + Last_hb_timestamp string `json:"Last_hb_timestamp"` //ISO8601 time stamp, set by sender + Last_hb_status string `json:"Last_hb_status"` //Any special status of last HB, from sender + Had_warning string `json:"Had_warning"` //Flag to mark start/stop edge conditions } // Heartbeat JSON. This is the HB message format, which must follow all // versioning constraints. type hbjson_full_v1 struct { - Component string `json:"Component"` - Hostname string `json:"Hostname"` - NID string `json:"NID"` - Status string `json:"Status"` - Timestamp string `json:"Timestamp"` + Component string `json:"Component"` + Hostname string `json:"Hostname"` + NID string `json:"NID"` + Status string `json:"Status"` + Timestamp string `json:"Timestamp"` } type hbjson_v1 struct { - Status string `json:"Status"` - Timestamp string `json:"Timestamp"` + Status string `json:"Status"` + Timestamp string `json:"Timestamp"` } // Data passed to the SM message sender thread type sminfo struct { - component string - status string - to_state int - last_hb_timestamp string + component string + status string + to_state int + last_hb_timestamp string } // For sending PATCH to /State/Components/{xname}/StateData type smjson_einfo struct { - Id string `json:"ID"` - Message string `json:"Message"` - Flag string `json:"Flag"` + Id string `json:"ID"` + Message string `json:"Message"` + Flag string `json:"Flag"` } type smjbulk_v1 struct { - ComponentIDs []string `json:"ComponentIDs"` - State string `json:"State"` - Flag string `json:"Flag"` - ExtendedInfo smjson_einfo + ComponentIDs []string `json:"ComponentIDs"` + State string `json:"State"` + Flag string `json:"Flag"` + ExtendedInfo smjson_einfo //Unmarshallable, used for HSM communication status - needSend bool - sentOK bool + needSend bool + sentOK bool } // for sending HB state changes to the telemetry bus type telemetry_json_v1 struct { - MessageID string `json:"MessageID"` - Id string `json:"ID"` - NewState string `json:"NewState"` - NewFlag string `json:"NewFlag"` - LastHBTimeStamp string `json:"LastHBTimeStamp"` - Info string `json:"Info"` + MessageID string `json:"MessageID"` + Id string `json:"ID"` + NewState string `json:"NewState"` + NewFlag string `json:"NewFlag"` + LastHBTimeStamp string `json:"LastHBTimeStamp"` + Info string `json:"Info"` } type hbSingleStateRsp struct { - XName string `json:"XName"` - Heartbeating bool `json:"Heartbeating"` + XName string `json:"XName"` + Heartbeating bool `json:"Heartbeating"` } type hbStatesRsp struct { @@ -131,17 +132,17 @@ type hbStatesReq struct { // HB states const ( - HB_started = 1 - HB_stopped_warn = 2 - HB_restarted_warn = 3 - HB_stopped_error = 4 - HB_quit = 0x8675309 + HB_started = 1 + HB_stopped_warn = 2 + HB_restarted_warn = 3 + HB_stopped_error = 4 + HB_quit = 0x8675309 ) const ( - HB_WARN_NONE = "" - HB_WARN_NORMAL = "WN" - HB_WARN_GAP = "WG" + HB_WARN_NONE = "" + HB_WARN_NORMAL = "WN" + HB_WARN_GAP = "WG" ) const TELEMETRY_MESSAGE_ID = "Heartbeat Change Notification" @@ -151,18 +152,17 @@ const TELEMETRY_MESSAGE_ID = "Heartbeat Change Notification" const HSMQ_DIE = 0x8675309 const HSMQ_NEW = 0x11223344 - ///////////////////////////////////////////////////////////////////////////// // Global variables ///////////////////////////////////////////////////////////////////////////// // Chan/async Qs -var hsmUpdateQ = make(chan int, 100000) -var telemetryQ = make(chan telemetry_json_v1, 50000) -var StartMap = make(map[string]uint64) -var RestartMap = make(map[string]uint64) -var StopWarnMap = make(map[string]uint64) +var hsmUpdateQ = make(chan int, 100000) +var telemetryQ = make(chan telemetry_json_v1, 50000) +var StartMap = make(map[string]uint64) +var RestartMap = make(map[string]uint64) +var StopWarnMap = make(map[string]uint64) var StopErrorMap = make(map[string]uint64) var hbSeq uint64 var hsmWG sync.WaitGroup @@ -173,10 +173,9 @@ var testMode bool var sg_ncomp = 0 - ///////////////////////////////////////////////////////////////////////////// -// Send a patch to the State Mgr to indicate heartbeat status change. Note -// that this is called as part of a wait group for synchronization. The +// Send a patch to the State Mgr to indicate heartbeat status change. Note +// that this is called as part of a wait group for synchronization. The // result of the operation (pass/fail) is indicated in the passed-in data // struct. // @@ -185,82 +184,82 @@ var sg_ncomp = 0 ///////////////////////////////////////////////////////////////////////////// func send_sm_patch(smjinfo *smjbulk_v1) { - defer hsmWG.Done() - - barr,err := json.Marshal(smjinfo) - if (err != nil) { - hbtdPrintln("INTERNAL ERROR marshalling SM info:",err) - return - } - - url := app_params.statemgr_url.string_param - - if (!testMode) { - url = url + "/" + SM_URL_MID + "/" + SM_URL_SUFFIX - } - - if (app_params.debug_level.int_param > 1) { - hbtdPrintf("Sending PATCH to State Mgr URL: '%s', Data: '%s'", - url,string(barr)) - } - - //Don't actually send anything to the SM if we're in "--nosm" mode. - - if (app_params.nosm.int_param != 0) { - return - } - - // Make PATCH requests this way since http.Client has no Patch() method. - - ctx,cancel := context.WithTimeout(context.Background(), - (time.Duration(app_params.statemgr_timeout.int_param) * - time.Second)) - defer cancel() - req,_ := http.NewRequestWithContext(ctx,"PATCH", url, bytes.NewBuffer(barr)) - req.Header.Set("Content-Type","application/json") - base.SetHTTPUserAgent(req,serviceName) - - rsp,err := htrans.client.Do(req) - - if (err != nil) { - hbtdPrintln("ERROR sending PATCH to SM:",err) - return - } else { - defer rsp.Body.Close() - _,_ = ioutil.ReadAll(rsp.Body) - if ((rsp.StatusCode == http.StatusOK) || - (rsp.StatusCode == http.StatusNoContent) || - (rsp.StatusCode == http.StatusAccepted)) { - if (app_params.debug_level.int_param > 1) { - hbtdPrintln("SUCCESS sending PATCH to SM, response:",rsp) - } - } else { - hbtdPrintln("ERROR response from State Manager:",rsp.Status,"Error code:",rsp.StatusCode) - return - } - } - - smjinfo.sentOK = true - return + defer hsmWG.Done() + + barr, err := json.Marshal(smjinfo) + if err != nil { + hbtdPrintln("INTERNAL ERROR marshalling SM info:", err) + return + } + + url := app_params.statemgr_url.string_param + + if !testMode { + url = url + "/" + SM_URL_MID + "/" + SM_URL_SUFFIX + } + + if app_params.debug_level.int_param > 1 { + hbtdPrintf("Sending PATCH to State Mgr URL: '%s', Data: '%s'", + url, string(barr)) + } + + //Don't actually send anything to the SM if we're in "--nosm" mode. + + if app_params.nosm.int_param != 0 { + return + } + + // Make PATCH requests this way since http.Client has no Patch() method. + + ctx, cancel := context.WithTimeout(context.Background(), + (time.Duration(app_params.statemgr_timeout.int_param) * + time.Second)) + defer cancel() + req, _ := http.NewRequestWithContext(ctx, "PATCH", url, bytes.NewBuffer(barr)) + req.Header.Set("Content-Type", "application/json") + base.SetHTTPUserAgent(req, serviceName) + + rsp, err := htrans.client.Do(req) + + if err != nil { + hbtdPrintln("ERROR sending PATCH to SM:", err) + return + } else { + defer rsp.Body.Close() + _, _ = ioutil.ReadAll(rsp.Body) + if (rsp.StatusCode == http.StatusOK) || + (rsp.StatusCode == http.StatusNoContent) || + (rsp.StatusCode == http.StatusAccepted) { + if app_params.debug_level.int_param > 1 { + hbtdPrintln("SUCCESS sending PATCH to SM, response:", rsp) + } + } else { + hbtdPrintln("ERROR response from State Manager:", rsp.Status, "Error code:", rsp.StatusCode) + return + } + } + + smjinfo.sentOK = true + return } // Convenience function. Takes component heartbeat status maps and prepares // them for HB change processing. Populates an "all components map" that is // the superset of recent HB changes to persistent ones. -func groomCompLocalMapsPRE(allCompsMap map[string]bool, cpStartMap,cpRestartMap,cpStopWarnMap,cpStopErrorMap map[string]uint64) { +func groomCompLocalMapsPRE(allCompsMap map[string]bool, cpStartMap, cpRestartMap, cpStopWarnMap, cpStopErrorMap map[string]uint64) { //For each HB change map, populate the "all components" map, plus //copy the global HB change map entries for each node into the more //persistent one. // HB Started maps - for k,v := range(cpStartMap) { - if (v != 0) { + for k, v := range cpStartMap { + if v != 0 { allCompsMap[k] = true } } - for k,v := range(StartMap) { - if (v != 0) { + for k, v := range StartMap { + if v != 0 { cpStartMap[k] = v StartMap[k] = 0 allCompsMap[k] = true @@ -268,13 +267,13 @@ func groomCompLocalMapsPRE(allCompsMap map[string]bool, cpStartMap,cpRestartMap, } // HB Restarted maps - for k,v := range(cpRestartMap) { - if (v != 0) { + for k, v := range cpRestartMap { + if v != 0 { allCompsMap[k] = true } } - for k,v := range(RestartMap) { - if (v != 0) { + for k, v := range RestartMap { + if v != 0 { cpRestartMap[k] = v RestartMap[k] = 0 allCompsMap[k] = true @@ -282,13 +281,13 @@ func groomCompLocalMapsPRE(allCompsMap map[string]bool, cpStartMap,cpRestartMap, } // HB Stopped/warning maps - for k,v := range(cpStopWarnMap) { - if (v != 0) { + for k, v := range cpStopWarnMap { + if v != 0 { allCompsMap[k] = true } } - for k,v := range(StopWarnMap) { - if (v != 0) { + for k, v := range StopWarnMap { + if v != 0 { cpStopWarnMap[k] = v StopWarnMap[k] = 0 allCompsMap[k] = true @@ -296,13 +295,13 @@ func groomCompLocalMapsPRE(allCompsMap map[string]bool, cpStartMap,cpRestartMap, } // HB Stopped/error maps - for k,v := range(cpStopErrorMap) { - if (v != 0) { + for k, v := range cpStopErrorMap { + if v != 0 { allCompsMap[k] = true } } - for k,v := range(StopErrorMap) { - if (v != 0) { + for k, v := range StopErrorMap { + if v != 0 { cpStopErrorMap[k] = v StopErrorMap[k] = 0 allCompsMap[k] = true @@ -314,8 +313,8 @@ func groomCompLocalMapsPRE(allCompsMap map[string]bool, cpStartMap,cpRestartMap, // entry for all other maps. This is done after successful SM PATCH // operations to clear the HB state maps. -func groomCompLocalMapsPOST(map1,map2,map3,map4 map[string]uint64) { - for k,_ := range(map1) { +func groomCompLocalMapsPOST(map1, map2, map3, map4 map[string]uint64) { + for k, _ := range map1 { map2[k] = 0 map3[k] = 0 map4[k] = 0 @@ -327,7 +326,7 @@ func groomCompLocalMapsPOST(map1,map2,map3,map4 map[string]uint64) { // populates default values. func createBSI() (smjbulk_v1, smjbulk_v1, smjbulk_v1, smjbulk_v1) { - var bsiStart,bsiRestart,bsiStopWarn,bsiStopError smjbulk_v1 + var bsiStart, bsiRestart, bsiStopWarn, bsiStopError smjbulk_v1 bsiStart.State = base.StateReady.String() bsiStart.Flag = base.FlagOK.String() bsiStart.ExtendedInfo.Message = "Heartbeat started" @@ -340,12 +339,12 @@ func createBSI() (smjbulk_v1, smjbulk_v1, smjbulk_v1, smjbulk_v1) { bsiStopError.State = base.StateStandby.String() bsiStopError.Flag = base.FlagAlert.String() bsiStopError.ExtendedInfo.Message = "Heartbeat stopped -- declared dead" - return bsiStart,bsiRestart,bsiStopWarn,bsiStopError + return bsiStart, bsiRestart, bsiStopWarn, bsiStopError } ///////////////////////////////////////////////////////////////////////////// -// Thread func. Takes the values found in the global and local/persistent -// HB status change maps, determines which ones to place into an HSM +// Thread func. Takes the values found in the global and local/persistent +// HB status change maps, determines which ones to place into an HSM // BulkStateChange data structure and then PATCH them to HSM. If there are // failures with the PATCH, the persistent maps will help resolve conflicts // when more than one HB state change occurs while HSM is unavailable. @@ -360,106 +359,106 @@ func send_sm_req() { for { //Wait for next HB scan to complete. - if (app_params.debug_level.int_param > 1) { + if app_params.debug_level.int_param > 1 { hbtdPrintf("Waiting for a Q Pop.") } qval := <-hsmUpdateQ - if (app_params.debug_level.int_param > 1) { - hbtdPrintf("Q Popped, val: %x.",qval) + if app_params.debug_level.int_param > 1 { + hbtdPrintf("Q Popped, val: %x.", qval) } - if (qval == HSMQ_DIE) { + if qval == HSMQ_DIE { hbtdPrintf("DIE message received, exiting send_sm_req().") break } - //Copy HB state changes from the global component maps into into local + //Copy HB state changes from the global component maps into into local //maps. //Also build a map of all pertinent components (superset of local and //global maps). allCompsMap := make(map[string]bool) hbMapLock.Lock() - groomCompLocalMapsPRE(allCompsMap,cpStartMap,cpRestartMap,cpStopWarnMap,cpStopErrorMap) + groomCompLocalMapsPRE(allCompsMap, cpStartMap, cpRestartMap, cpStopWarnMap, cpStopErrorMap) hbMapLock.Unlock() //Populate local copies of the HB state maps from the global ones. //De-duplicate the maps. If any component saw more than one HB - //change, take the one with the highest sequence number. Note that - //each time through the outer most for() loop (indicating a new HB - //scan was done) if there were any HSM send errors from the previous + //change, take the one with the highest sequence number. Note that + //each time through the outer most for() loop (indicating a new HB + //scan was done) if there were any HSM send errors from the previous //scan, the HB states will still be retained in local copies of the //maps, so we'll just add from the global state maps (not start over). - //If the HSM data was sent OK, the local maps are cleared and new + //If the HSM data was sent OK, the local maps are cleared and new //data is sent. - bsiStart,bsiRestart,bsiStopWarn,bsiStopError := createBSI() + bsiStart, bsiRestart, bsiStopWarn, bsiStopError := createBSI() - for k,_ := range(allCompsMap) { - start,_ := cpStartMap[k] - restart,_ := cpRestartMap[k] - swarn,_ := cpStopWarnMap[k] - serr,_ := cpStopErrorMap[k] + for k, _ := range allCompsMap { + start, _ := cpStartMap[k] + restart, _ := cpRestartMap[k] + swarn, _ := cpStopWarnMap[k] + serr, _ := cpStopErrorMap[k] - //The state change category with the highest value wins, meaning + //The state change category with the highest value wins, meaning //that it came in last, time-wise. - if ((start > restart) && (start > swarn) && (start > serr)) { - bsiStart.ComponentIDs = append(bsiStart.ComponentIDs,k) - } else if ((restart > start) && (restart > swarn) && (restart > serr)) { - bsiRestart.ComponentIDs = append(bsiRestart.ComponentIDs,k) - } else if ((swarn > start) && (swarn > restart) && (swarn > serr)) { - bsiStopWarn.ComponentIDs = append(bsiStopWarn.ComponentIDs,k) - } else if ((serr > start) && (serr > restart) && (serr > swarn)) { - bsiStopError.ComponentIDs = append(bsiStopError.ComponentIDs,k) + if (start > restart) && (start > swarn) && (start > serr) { + bsiStart.ComponentIDs = append(bsiStart.ComponentIDs, k) + } else if (restart > start) && (restart > swarn) && (restart > serr) { + bsiRestart.ComponentIDs = append(bsiRestart.ComponentIDs, k) + } else if (swarn > start) && (swarn > restart) && (swarn > serr) { + bsiStopWarn.ComponentIDs = append(bsiStopWarn.ComponentIDs, k) + } else if (serr > start) && (serr > restart) && (serr > swarn) { + bsiStopError.ComponentIDs = append(bsiStopError.ComponentIDs, k) } } - //If HSM is not ready, bail until next scan. + //If HSM is not ready, bail until next scan. - if !hsmReady { + if !hsmReady { hbtdPrintf("HSM Not ready, waiting until next scan.") - continue //wait until next scan. + continue //wait until next scan. } - //Check each bulk operation and add a wait count, then send the SM + //Check each bulk operation and add a wait count, then send the SM //patches, in parallel, one for each HB state change type. - if (len(bsiStart.ComponentIDs) > 0) { + if len(bsiStart.ComponentIDs) > 0 { hsmWG.Add(1) bsiStart.needSend = true go send_sm_patch(&bsiStart) } - if (len(bsiRestart.ComponentIDs) > 0) { + if len(bsiRestart.ComponentIDs) > 0 { hsmWG.Add(1) bsiRestart.needSend = true go send_sm_patch(&bsiRestart) } - if (len(bsiStopWarn.ComponentIDs) > 0) { + if len(bsiStopWarn.ComponentIDs) > 0 { hsmWG.Add(1) bsiStopWarn.needSend = true go send_sm_patch(&bsiStopWarn) } - if (len(bsiStopError.ComponentIDs) > 0) { + if len(bsiStopError.ComponentIDs) > 0 { hsmWG.Add(1) bsiStopError.needSend = true go send_sm_patch(&bsiStopError) } - if (!bsiStart.needSend && !bsiRestart.needSend && - !bsiStopWarn.needSend && !bsiStopError.needSend) { - if (app_params.debug_level.int_param > 1) { + if !bsiStart.needSend && !bsiRestart.needSend && + !bsiStopWarn.needSend && !bsiStopError.needSend { + if app_params.debug_level.int_param > 1 { hbtdPrintf("Nothing to send to HSM.") } continue } //Wait until they are all complete. - if (app_params.debug_level.int_param > 1) { + if app_params.debug_level.int_param > 1 { hbtdPrintf("Waiting for HSM PATCHs to complete...") } hsmWG.Wait() - if (app_params.debug_level.int_param > 1) { + if app_params.debug_level.int_param > 1 { hbtdPrintf("PATCHs complete: %t %t %t %t", - bsiStart.sentOK, bsiRestart.sentOK,bsiStopWarn.sentOK, + bsiStart.sentOK, bsiRestart.sentOK, bsiStopWarn.sentOK, bsiStopError.sentOK) } @@ -469,24 +468,24 @@ func send_sm_req() { //scan. hbMapLock.Lock() - if (bsiStart.needSend && bsiStart.sentOK) { - groomCompLocalMapsPOST(cpStartMap,cpRestartMap,cpStopWarnMap,cpStopErrorMap) + if bsiStart.needSend && bsiStart.sentOK { + groomCompLocalMapsPOST(cpStartMap, cpRestartMap, cpStopWarnMap, cpStopErrorMap) cpStartMap = make(map[string]uint64) } - if (bsiRestart.needSend && bsiRestart.sentOK) { - groomCompLocalMapsPOST(cpRestartMap,cpStartMap,cpStopWarnMap,cpStopErrorMap) + if bsiRestart.needSend && bsiRestart.sentOK { + groomCompLocalMapsPOST(cpRestartMap, cpStartMap, cpStopWarnMap, cpStopErrorMap) cpRestartMap = make(map[string]uint64) } - if (bsiStopWarn.needSend && bsiStopWarn.sentOK) { - groomCompLocalMapsPOST(cpStopWarnMap,cpStartMap,cpRestartMap,cpStopErrorMap) + if bsiStopWarn.needSend && bsiStopWarn.sentOK { + groomCompLocalMapsPOST(cpStopWarnMap, cpStartMap, cpRestartMap, cpStopErrorMap) cpStopWarnMap = make(map[string]uint64) } - if (bsiStopError.needSend && bsiStopError.sentOK) { - groomCompLocalMapsPOST(cpStopErrorMap,cpStartMap,cpRestartMap,cpStopWarnMap) + if bsiStopError.needSend && bsiStopError.sentOK { + groomCompLocalMapsPOST(cpStopErrorMap, cpStartMap, cpRestartMap, cpStopWarnMap) cpStopErrorMap = make(map[string]uint64) } hbMapLock.Unlock() - } + } } ///////////////////////////////////////////////////////////////////////////// @@ -497,22 +496,22 @@ func telemetry_handler() { var tmsg telemetry_json_v1 for { - if (app_params.use_telemetry.int_param == 0) { + if app_params.use_telemetry.int_param == 0 { time.Sleep(5 * time.Second) continue } tmsg = <-telemetryQ tbMutex.Lock() - if (msgbusHandle != nil) { - jdata,err := json.Marshal(tmsg) - if (err == nil) { + if msgbusHandle != nil { + jdata, err := json.Marshal(tmsg) + if err == nil { err = msgbusHandle.MessageWrite(string(jdata)) - if (err != nil) { - hbtdPrintln("ERROR injecting telemetry data:",err) + if err != nil { + hbtdPrintln("ERROR injecting telemetry data:", err) } } else { - hbtdPrintln("ERROR marshalling telemetry data:",err) + hbtdPrintln("ERROR marshalling telemetry data:", err) } } tbMutex.Unlock() @@ -521,7 +520,7 @@ func telemetry_handler() { ///////////////////////////////////////////////////////////////////////////// // Set values in the HB status change component maps based on this heartbeat's -// information. This is called by the heartbeat checker and when new +// information. This is called by the heartbeat checker and when new // heartbeats arrive. The maps are used when the heartbeat checker calls // send_sm_message() to update HSM. // @@ -534,45 +533,45 @@ func hb_update_notify(hb *hbinfo, to_state int) { telemsg.MessageID = TELEMETRY_MESSAGE_ID telemsg.Id = hb.Component telemsg.LastHBTimeStamp = hb.Last_hb_timestamp - hbSeq ++ - - switch(to_state) { - case HB_started: - hbMapLock.Lock() - StartMap[hb.Component] = hbSeq - hbMapLock.Unlock() - telemsg.NewState = base.StateReady.String() - telemsg.NewFlag = base.FlagOK.String() - telemsg.Info = "Heartbeat started." - case HB_restarted_warn: - hbMapLock.Lock() - RestartMap[hb.Component] = hbSeq - hbMapLock.Unlock() - telemsg.NewState = base.StateReady.String() - telemsg.NewFlag = base.FlagOK.String() - telemsg.Info = "Heartbeat re-started." - case HB_stopped_warn: - hbMapLock.Lock() - StopWarnMap[hb.Component] = hbSeq - hbMapLock.Unlock() - telemsg.NewState = base.StateReady.String() - telemsg.NewFlag = base.FlagWarning.String() - telemsg.Info = "Heartbeat stopped, node may be dead." - case HB_stopped_error: - hbMapLock.Lock() - StopErrorMap[hb.Component] = hbSeq - hbMapLock.Unlock() - telemsg.NewState = base.StateStandby.String() - telemsg.NewFlag = base.FlagAlert.String() - telemsg.Info = "Heartbeat stopped, node is dead." - default: - hbtdPrintf("INTERNAL ERROR: UNKNOWN STATE: %d",to_state) + hbSeq++ + + switch to_state { + case HB_started: + hbMapLock.Lock() + StartMap[hb.Component] = hbSeq + hbMapLock.Unlock() + telemsg.NewState = base.StateReady.String() + telemsg.NewFlag = base.FlagOK.String() + telemsg.Info = "Heartbeat started." + case HB_restarted_warn: + hbMapLock.Lock() + RestartMap[hb.Component] = hbSeq + hbMapLock.Unlock() + telemsg.NewState = base.StateReady.String() + telemsg.NewFlag = base.FlagOK.String() + telemsg.Info = "Heartbeat re-started." + case HB_stopped_warn: + hbMapLock.Lock() + StopWarnMap[hb.Component] = hbSeq + hbMapLock.Unlock() + telemsg.NewState = base.StateReady.String() + telemsg.NewFlag = base.FlagWarning.String() + telemsg.Info = "Heartbeat stopped, node may be dead." + case HB_stopped_error: + hbMapLock.Lock() + StopErrorMap[hb.Component] = hbSeq + hbMapLock.Unlock() + telemsg.NewState = base.StateStandby.String() + telemsg.NewFlag = base.FlagAlert.String() + telemsg.Info = "Heartbeat stopped, node is dead." + default: + hbtdPrintf("INTERNAL ERROR: UNKNOWN STATE: %d", to_state) } select { - case telemetryQ <-telemsg: - default: - hbtdPrintf("INFO: Telemetry bus not accepting messages, heartbeat event not sent.") + case telemetryQ <- telemsg: + default: + hbtdPrintf("INFO: Telemetry bus not accepting messages, heartbeat event not sent.") } } @@ -583,230 +582,230 @@ func hb_update_notify(hb *hbinfo, to_state int) { ///////////////////////////////////////////////////////////////////////////// func rearm_hbcheck_timer() { - if (app_params.check_interval.int_param > 0) { - time.AfterFunc((time.Duration(app_params.check_interval.int_param) * time.Second), - hb_checker) - } + if app_params.check_interval.int_param > 0 { + time.AfterFunc((time.Duration(app_params.check_interval.int_param) * time.Second), + hb_checker) + } } ///////////////////////////////////////////////////////////////////////////// // This function is called periodically by a timer. It will run through the -// list of currently tracked components and send notifications of any +// list of currently tracked components and send notifications of any // delinquencies. Note that new HB startup notifications are sent in the // hb_rcv() function. // // Args, return: None ///////////////////////////////////////////////////////////////////////////// -func hb_checker () { - var nhb hbinfo - var storeit bool - var verr error - var now,tdiff,lhbtime int64 - var deleteKeys []string - var updateKeys []hmetcd.Kvi_KV - - if (app_params.debug_level.int_param > 1) { - hbtdPrintf("HB CHECKER entry.") - } - - ncomp := 0 - - // Grab the inter-process lock and get all keys/vals. - - if (app_params.check_interval.int_param > 0) { - if (app_params.debug_level.int_param > 1) { - hbtdPrintf("Locking...") - } - tstart := time.Now() - lckerr := kvHandle.DistTimedLock(app_params.check_interval.int_param*2) - tfin := time.Now() - elapsed := tfin.Sub(tstart) / time.Second - if (int(elapsed) > (app_params.check_interval.int_param*3)) { - hbtdPrintf("WARNING: Distributed lock acquisition attempt took %d seconds.", - elapsed) - } - if (lckerr != nil) { - //Lock is already held. This means someone else is doing the check, - //so we don't have to. - - if (app_params.debug_level.int_param > 1) { - hbtdPrintf("HB checker being done elsewhere, skipping.\n") - hbtdPrintln(" (returned:",lckerr,")") - } - rearm_hbcheck_timer() - return - } - if (app_params.debug_level.int_param > 1) { - hbtdPrintf("HB Checker lock acquired.") - } - } - - //Test code, activated by environment variable. Causes the HB checker - //to sleep whilst holding the lock, simulating cases where the HB checker - //takes a long time, to verify that multi-instances will take over for - //each other. - - envstr := os.Getenv("HBTD_RSLEEP") - if (envstr != "") { - slp,_ := strconv.Atoi(envstr) - hbtdPrintf("Sleeping for %d seconds..\n",slp) - time.Sleep(time.Duration(slp) * time.Second) - } - - kvlist, err := kvHandle.GetRange(HB_KEYRANGE_START,HB_KEYRANGE_END) - if (err != nil) { - hbtdPrintln("ERROR fetching all hbtd keys from KV store: ",err) - kvHandle.DistUnlock() //ignore errors - rearm_hbcheck_timer() - return - } - - for _,kv := range kvlist { - //Skip special keys - if (kv.Key == KV_PARAM_KEY) { - continue - } - - storeit = false - ncomp ++ - - if (app_params.debug_level.int_param > 1) { - hbtdPrintf("Checking component: '%s'\n",kv.Key); - } - - verr = json.Unmarshal([]byte(kv.Value),&nhb) - if (verr != nil) { - hbtdPrintln("ERROR unmarshalling '",kv.Value,"': ",verr) - continue - } - - //Get the current time. We will get it here rather than once at the - //beginning of this function since there can be delays in getting - //the key/value store, and we need to be able to accurately calculate - //the time elapsed since the HB was received. - - //TODO: maybe should compare the component with the key? Seems like - //that's just extra work. - - //TODO: this has 1 second resolution. Should be enough. - - now = time.Now().Unix() - - lhbtime,_ = strconv.ParseInt(nhb.Last_hb_rcv_time,16,64) - tdiff = now - lhbtime - - if (tdiff >= int64(app_params.errtime.int_param)) { - if (staleKeys) { - //This means there was a time when there was no HBTD instance - //running. We'll treat these the same as warnings. - hbtdPrintf("WARNING: Heartbeat overdue %d seconds for '%s' due to HB monitoring gap; might be dead, last status: '%s'", - tdiff,nhb.Component,nhb.Last_hb_status) - - //Update the HB's last received time. This will freshen the - //stale key so if it's really still heartbeating, it will - //succeed next time. If the node is truly dead, it will still - //be reported dead once the freshened time expires. - nhb.Last_hb_rcv_time = strconv.FormatUint(uint64(time.Now().Unix()),16) - - //Send a warning to SM - nhb.Had_warning = HB_WARN_GAP - hb_update_notify(&nhb,HB_stopped_warn) - storeit = true - } else { - hbtdPrintf("ERROR: Heartbeat overdue %d seconds for '%s' (declared dead), last status: '%s'\n", - tdiff,nhb.Component,nhb.Last_hb_status) - - //Send an error to SM - hb_update_notify(&nhb,HB_stopped_error) - - //Since it's dead, take it out of the list. - deleteKeys = append(deleteKeys,kv.Key) - ncomp -- - continue - } - } else if (tdiff >= int64(app_params.warntime.int_param)) { - if (nhb.Had_warning == HB_WARN_NONE) { - hbtdPrintf("WARNING: Heartbeat overdue %d seconds for '%s' (might be dead), last status: '%s'\n", - tdiff,nhb.Component,nhb.Last_hb_status) - - //Send a warning to SM - nhb.Had_warning = HB_WARN_NORMAL - hb_update_notify(&nhb,HB_stopped_warn) - storeit = true - } - } else { - //HB arrived in time. Check if there was a prior warning, and if - //so, send a HB re-started message - if (nhb.Had_warning == HB_WARN_NORMAL) { - nhb.Had_warning = HB_WARN_NONE - storeit = true - hbtdPrintf("INFO: Heartbeat restarted for '%s'\n",nhb.Component) - hb_update_notify(&nhb,HB_restarted_warn) - } - } - - if (storeit) { - jstr,err := json.Marshal(nhb) - if (err != nil) { - hbtdPrintln("INTERNAL ERROR marshaling JSON for ",nhb.Component,": ",err) - } else { - updateKeys = append(updateKeys, hmetcd.Kvi_KV{Key: nhb.Component, - Value: string(jstr),}) - } - } - } - - //Delete keys of dead HBs - - if (app_params.debug_level.int_param > 1) { - hbtdPrintf("Deleting %d keys...",len(deleteKeys)) - } - for _,dkey := range(deleteKeys) { - verr = kvHandle.Delete(dkey) - if (verr != nil) { - hbtdPrintln("ERROR deleting key '",dkey,"' from KV store: ",verr) - } - } - - //Update keys that need updating - - if (app_params.debug_level.int_param > 1) { - hbtdPrintf("Updating %d keys...",len(updateKeys)) - } - for _,ukey := range(updateKeys) { - merr := kvHandle.Store(ukey.Key,ukey.Value) - if (merr != nil) { - hbtdPrintf("ERROR storing key '%s': %v",ukey.Key,merr) - } - } - - if (app_params.debug_level.int_param > 1) { - hbtdPrintf("Unlocking...") - } - if (app_params.check_interval.int_param > 0) { - err := kvHandle.DistUnlock() - if (err != nil) { - hbtdPrintln("ERROR unlocking distributed lock:",err) - } - } - - hsmUpdateQ <-HSMQ_NEW - - if (ncomp != sg_ncomp) { - sg_ncomp = ncomp - hbtdPrintf("Number of components heartbeating: %d\n",ncomp) - } - - staleKeys = false - - //Re-arm timer -- it is not periodic. If the check interval <= 0, don't - //re-arm (used for testing) - - rearm_hbcheck_timer() +func hb_checker() { + var nhb hbinfo + var storeit bool + var verr error + var now, tdiff, lhbtime int64 + var deleteKeys []string + var updateKeys []hmetcd.Kvi_KV + + if app_params.debug_level.int_param > 1 { + hbtdPrintf("HB CHECKER entry.") + } + + ncomp := 0 + + // Grab the inter-process lock and get all keys/vals. + + if app_params.check_interval.int_param > 0 { + if app_params.debug_level.int_param > 1 { + hbtdPrintf("Locking...") + } + tstart := time.Now() + lckerr := kvHandle.DistTimedLock(app_params.check_interval.int_param * 2) + tfin := time.Now() + elapsed := tfin.Sub(tstart) / time.Second + if int(elapsed) > (app_params.check_interval.int_param * 3) { + hbtdPrintf("WARNING: Distributed lock acquisition attempt took %d seconds.", + elapsed) + } + if lckerr != nil { + //Lock is already held. This means someone else is doing the check, + //so we don't have to. + + if app_params.debug_level.int_param > 1 { + hbtdPrintf("HB checker being done elsewhere, skipping.\n") + hbtdPrintln(" (returned:", lckerr, ")") + } + rearm_hbcheck_timer() + return + } + if app_params.debug_level.int_param > 1 { + hbtdPrintf("HB Checker lock acquired.") + } + } + + //Test code, activated by environment variable. Causes the HB checker + //to sleep whilst holding the lock, simulating cases where the HB checker + //takes a long time, to verify that multi-instances will take over for + //each other. + + envstr := os.Getenv("HBTD_RSLEEP") + if envstr != "" { + slp, _ := strconv.Atoi(envstr) + hbtdPrintf("Sleeping for %d seconds..\n", slp) + time.Sleep(time.Duration(slp) * time.Second) + } + + kvlist, err := kvHandle.GetRange(HB_KEYRANGE_START, HB_KEYRANGE_END) + if err != nil { + hbtdPrintln("ERROR fetching all hbtd keys from KV store: ", err) + kvHandle.DistUnlock() //ignore errors + rearm_hbcheck_timer() + return + } + + for _, kv := range kvlist { + //Skip special keys + if kv.Key == KV_PARAM_KEY { + continue + } + + storeit = false + ncomp++ + + if app_params.debug_level.int_param > 1 { + hbtdPrintf("Checking component: '%s'\n", kv.Key) + } + + verr = json.Unmarshal([]byte(kv.Value), &nhb) + if verr != nil { + hbtdPrintln("ERROR unmarshalling '", kv.Value, "': ", verr) + continue + } + + //Get the current time. We will get it here rather than once at the + //beginning of this function since there can be delays in getting + //the key/value store, and we need to be able to accurately calculate + //the time elapsed since the HB was received. + + //TODO: maybe should compare the component with the key? Seems like + //that's just extra work. + + //TODO: this has 1 second resolution. Should be enough. + + now = time.Now().Unix() + + lhbtime, _ = strconv.ParseInt(nhb.Last_hb_rcv_time, 16, 64) + tdiff = now - lhbtime + + if tdiff >= int64(app_params.errtime.int_param) { + if staleKeys { + //This means there was a time when there was no HBTD instance + //running. We'll treat these the same as warnings. + hbtdPrintf("WARNING: Heartbeat overdue %d seconds for '%s' due to HB monitoring gap; might be dead, last status: '%s'", + tdiff, nhb.Component, nhb.Last_hb_status) + + //Update the HB's last received time. This will freshen the + //stale key so if it's really still heartbeating, it will + //succeed next time. If the node is truly dead, it will still + //be reported dead once the freshened time expires. + nhb.Last_hb_rcv_time = strconv.FormatUint(uint64(time.Now().Unix()), 16) + + //Send a warning to SM + nhb.Had_warning = HB_WARN_GAP + hb_update_notify(&nhb, HB_stopped_warn) + storeit = true + } else { + hbtdPrintf("ERROR: Heartbeat overdue %d seconds for '%s' (declared dead), last status: '%s'\n", + tdiff, nhb.Component, nhb.Last_hb_status) + + //Send an error to SM + hb_update_notify(&nhb, HB_stopped_error) + + //Since it's dead, take it out of the list. + deleteKeys = append(deleteKeys, kv.Key) + ncomp-- + continue + } + } else if tdiff >= int64(app_params.warntime.int_param) { + if nhb.Had_warning == HB_WARN_NONE { + hbtdPrintf("WARNING: Heartbeat overdue %d seconds for '%s' (might be dead), last status: '%s'\n", + tdiff, nhb.Component, nhb.Last_hb_status) + + //Send a warning to SM + nhb.Had_warning = HB_WARN_NORMAL + hb_update_notify(&nhb, HB_stopped_warn) + storeit = true + } + } else { + //HB arrived in time. Check if there was a prior warning, and if + //so, send a HB re-started message + if nhb.Had_warning == HB_WARN_NORMAL { + nhb.Had_warning = HB_WARN_NONE + storeit = true + hbtdPrintf("INFO: Heartbeat restarted for '%s'\n", nhb.Component) + hb_update_notify(&nhb, HB_restarted_warn) + } + } + + if storeit { + jstr, err := json.Marshal(nhb) + if err != nil { + hbtdPrintln("INTERNAL ERROR marshaling JSON for ", nhb.Component, ": ", err) + } else { + updateKeys = append(updateKeys, hmetcd.Kvi_KV{Key: nhb.Component, + Value: string(jstr)}) + } + } + } + + //Delete keys of dead HBs + + if app_params.debug_level.int_param > 1 { + hbtdPrintf("Deleting %d keys...", len(deleteKeys)) + } + for _, dkey := range deleteKeys { + verr = kvHandle.Delete(dkey) + if verr != nil { + hbtdPrintln("ERROR deleting key '", dkey, "' from KV store: ", verr) + } + } + + //Update keys that need updating + + if app_params.debug_level.int_param > 1 { + hbtdPrintf("Updating %d keys...", len(updateKeys)) + } + for _, ukey := range updateKeys { + merr := kvHandle.Store(ukey.Key, ukey.Value) + if merr != nil { + hbtdPrintf("ERROR storing key '%s': %v", ukey.Key, merr) + } + } + + if app_params.debug_level.int_param > 1 { + hbtdPrintf("Unlocking...") + } + if app_params.check_interval.int_param > 0 { + err := kvHandle.DistUnlock() + if err != nil { + hbtdPrintln("ERROR unlocking distributed lock:", err) + } + } + + hsmUpdateQ <- HSMQ_NEW + + if ncomp != sg_ncomp { + sg_ncomp = ncomp + hbtdPrintf("Number of components heartbeating: %d\n", ncomp) + } + + staleKeys = false + + //Re-arm timer -- it is not periodic. If the check interval <= 0, don't + //re-arm (used for testing) + + rearm_hbcheck_timer() } -// Convenience function. Update the time stamp and associated info for this +// Convenience function. Update the time stamp and associated info for this // component. // // TODO: maybe we don't mess with unmarshalling the KV HB data -- we pretty @@ -814,74 +813,73 @@ func hb_checker () { // to do any data compares from the previous HB if we want to. func updateHB(errinst, xname, timestamp, status string, w http.ResponseWriter) { - var hbb hbinfo - - newkey := 0 - kval,kok,kerr := kvHandle.Get(xname) - if (kerr != nil) { - hbtdPrintf("Error reading KV key for: '%s', '%v'",xname,kerr) - } - - if ((kok == false) || (kerr != nil)) { - //Key does not exist. Create it. - newkey = 1 - - hbb.Component = xname - } else { - //Key exists, just update the time stamp and status. - - umerr := json.Unmarshal([]byte(kval),&hbb) - if (umerr != nil) { - hbtdPrintln("INTERNAL ERROR unmarshalling '",kval,"': ",umerr) - pdet := base.NewProblemDetails("about:blank", - "Internal Server Error", - "Error unmarshalling JSON string", - errinst,http.StatusInternalServerError) - base.SendProblemDetails(w,pdet,0) - return - } - } - - hbb.Last_hb_rcv_time = strconv.FormatUint(uint64(time.Now().Unix()),16) - hbb.Last_hb_timestamp = timestamp - hbb.Last_hb_status = status - - //Special case: if this heartbeat record Had_warning flag shows a coverage - //gap, set it to a normal warning so the checker handles is correctly. - - if (hbb.Had_warning == HB_WARN_GAP) { - hbb.Had_warning = HB_WARN_NORMAL - } - - jstr, jerr := json.Marshal(hbb) - if (jerr != nil) { - hbtdPrintln("INTERNAL ERROR marshaling JSON: ",jerr); - pdet := base.NewProblemDetails("about:blank", - "Internal Server Error", - "Error marshalling JSON data", - errinst,http.StatusInternalServerError) - base.SendProblemDetails(w,pdet,0) - return - } - - merr := kvHandle.Store(xname,string(jstr)) - if (merr != nil) { - hbtdPrintln("INTERNAL ERROR storing key ",string(jstr),": ",merr); - pdet := base.NewProblemDetails("about:blank", - "Internal Server Error", - "Key/Value service store operation failed", - errinst,http.StatusInternalServerError) - base.SendProblemDetails(w,pdet,0) - return - } - - if (newkey != 0) { - //Send notification of a new HB startup - hbtdPrintf("INFO: Heartbeat started for '%s'\n",hbb.Component) - hb_update_notify(&hbb,HB_started) - } -} + var hbb hbinfo + + newkey := 0 + kval, kok, kerr := kvHandle.Get(xname) + if kerr != nil { + hbtdPrintf("Error reading KV key for: '%s', '%v'", xname, kerr) + } + + if (kok == false) || (kerr != nil) { + //Key does not exist. Create it. + newkey = 1 + + hbb.Component = xname + } else { + //Key exists, just update the time stamp and status. + + umerr := json.Unmarshal([]byte(kval), &hbb) + if umerr != nil { + hbtdPrintln("INTERNAL ERROR unmarshalling '", kval, "': ", umerr) + pdet := base.NewProblemDetails("about:blank", + "Internal Server Error", + "Error unmarshalling JSON string", + errinst, http.StatusInternalServerError) + base.SendProblemDetails(w, pdet, 0) + return + } + } + + hbb.Last_hb_rcv_time = strconv.FormatUint(uint64(time.Now().Unix()), 16) + hbb.Last_hb_timestamp = timestamp + hbb.Last_hb_status = status + //Special case: if this heartbeat record Had_warning flag shows a coverage + //gap, set it to a normal warning so the checker handles is correctly. + + if hbb.Had_warning == HB_WARN_GAP { + hbb.Had_warning = HB_WARN_NORMAL + } + + jstr, jerr := json.Marshal(hbb) + if jerr != nil { + hbtdPrintln("INTERNAL ERROR marshaling JSON: ", jerr) + pdet := base.NewProblemDetails("about:blank", + "Internal Server Error", + "Error marshalling JSON data", + errinst, http.StatusInternalServerError) + base.SendProblemDetails(w, pdet, 0) + return + } + + merr := kvHandle.Store(xname, string(jstr)) + if merr != nil { + hbtdPrintln("INTERNAL ERROR storing key ", string(jstr), ": ", merr) + pdet := base.NewProblemDetails("about:blank", + "Internal Server Error", + "Key/Value service store operation failed", + errinst, http.StatusInternalServerError) + base.SendProblemDetails(w, pdet, 0) + return + } + + if newkey != 0 { + //Send notification of a new HB startup + hbtdPrintf("INFO: Heartbeat started for '%s'\n", hbb.Component) + hb_update_notify(&hbb, HB_started) + } +} ///////////////////////////////////////////////////////////////////////////// // Callback from the server loop when a HB request comes in. @@ -889,235 +887,235 @@ func updateHB(errinst, xname, timestamp, status string, w http.ResponseWriter) { ///////////////////////////////////////////////////////////////////////////// func hbRcv(w http.ResponseWriter, r *http.Request) { - errinst := URL_HEARTBEAT - - if (r.Method != "POST") { - hbtdPrintf("ERROR: request is not a POST.\n") - pdet := base.NewProblemDetails("about:blank", - "Invalid Request", - "Only POST operations supported", - errinst,http.StatusMethodNotAllowed) - - //It is required to have an "Allow:" header with this error - w.Header().Add("Allow","POST") - base.SendProblemDetails(w,pdet,0) - return - } - - var jdata hbjson_full_v1 - body,err := ioutil.ReadAll(r.Body) - err = json.Unmarshal(body,&jdata) - - if (err != nil) { - var v map[string]interface{} - - //The Unmarshal failed, find out which field(s) specifically failed. - //There's no quick-n-dirty way to do this so we'll just bulldoze - //through each field and verify it. - - errstr := "Invalid JSON data type" - errb := json.Unmarshal(body,&v) - if (errb != nil) { - hbtdPrintln("Unmarshal into map[string]interface{} didn't work:",errb) - } else { - //Figure out what field(s) == bad and report them. For now, they're - //all strings. - - mtype := reflect.TypeOf(jdata) - for i := 0; i < mtype.NumField(); i ++ { - nm := mtype.Field(i).Name - if (v[nm] == nil) { - continue - } - _,ok := v[nm].(string) //for now everything is strings. - if (!ok) { - errstr = fmt.Sprintf("Invalid data type in %s field",nm) - break - } - } - } - hbtdPrintln("Bad heartbeat JSON decode:",err); - pdet := base.NewProblemDetails("about:blank", - "Invalid Request", - errstr, - errinst,http.StatusBadRequest) - base.SendProblemDetails(w,pdet,0) - return - } - - //Check all the fields to be sure they are valid. TODO: we could - //check the Component to be sure it's a valid XName, but some - //customer might want to use their own node names and track things - //anyway; thus, for now at least, we won't limit tracking to just - //valid XNames. Note that this makes it possible for typos to be - //acceptable component names! - - ferrstr := "" - if (jdata.Component == "") { - ferrstr = "Missing Component field" - } else if (jdata.Hostname == "") { - ferrstr = "Missing Hostname field" - } else if (jdata.NID == "") { - ferrstr = "Missing NID field" - } else if (jdata.Status == "") { - ferrstr = "Missing Status field" - } else if (jdata.Timestamp == "") { - ferrstr = "Missing Timestamp field" - } - - if (ferrstr != "") { - hbtdPrintf("Incomplete heartbeat JSON: %s\n",ferrstr); - pdet := base.NewProblemDetails("about:blank", - "Invalid Request", - ferrstr, - errinst,http.StatusBadRequest) - base.SendProblemDetails(w,pdet,0) - return - } - - //Check to be sure that certain fields' values are valid. - - if (base.GetHMSType(jdata.Component) == base.HMSTypeInvalid) { - hbtdPrintf("Invalid XName in heartbeat JSON: %s\n",jdata.Component); - pdet := base.NewProblemDetails("about:blank", - "Invalid Request", - "Invalid Component Name", - errinst,http.StatusBadRequest) - base.SendProblemDetails(w,pdet,0) - return - } - - _,cerr := strconv.ParseInt(jdata.NID,0,64) - if (cerr != nil) { - hbtdPrintf("Invalid NID in heartbeat JSON: %s\n",jdata.NID); - pdet := base.NewProblemDetails("about:blank", - "Invalid Request", - "Invalid NID", - errinst,http.StatusBadRequest) - base.SendProblemDetails(w,pdet,0) - return - } - - if (app_params.debug_level.int_param > 0) { - hbtdPrintf("Heartbeat: Component: %s, Host: %s, NID: %s, Status: %s, time: %s\n", - jdata.Component,jdata.Hostname,jdata.NID,jdata.Status, - jdata.Timestamp) - } - - if (app_params.debug_level.int_param > 1) { - hbtdPrintf("HB received for: '%s'",jdata.Component) - } - - //Update the time stamp and info for this component. - - updateHB(errinst,jdata.Component,jdata.Timestamp,jdata.Status,w) + errinst := URL_HEARTBEAT + + if r.Method != "POST" { + hbtdPrintf("ERROR: request is not a POST.\n") + pdet := base.NewProblemDetails("about:blank", + "Invalid Request", + "Only POST operations supported", + errinst, http.StatusMethodNotAllowed) + + //It is required to have an "Allow:" header with this error + w.Header().Add("Allow", "POST") + base.SendProblemDetails(w, pdet, 0) + return + } + + var jdata hbjson_full_v1 + body, err := ioutil.ReadAll(r.Body) + err = json.Unmarshal(body, &jdata) + + if err != nil { + var v map[string]interface{} + + //The Unmarshal failed, find out which field(s) specifically failed. + //There's no quick-n-dirty way to do this so we'll just bulldoze + //through each field and verify it. + + errstr := "Invalid JSON data type" + errb := json.Unmarshal(body, &v) + if errb != nil { + hbtdPrintln("Unmarshal into map[string]interface{} didn't work:", errb) + } else { + //Figure out what field(s) == bad and report them. For now, they're + //all strings. + + mtype := reflect.TypeOf(jdata) + for i := 0; i < mtype.NumField(); i++ { + nm := mtype.Field(i).Name + if v[nm] == nil { + continue + } + _, ok := v[nm].(string) //for now everything is strings. + if !ok { + errstr = fmt.Sprintf("Invalid data type in %s field", nm) + break + } + } + } + hbtdPrintln("Bad heartbeat JSON decode:", err) + pdet := base.NewProblemDetails("about:blank", + "Invalid Request", + errstr, + errinst, http.StatusBadRequest) + base.SendProblemDetails(w, pdet, 0) + return + } + + //Check all the fields to be sure they are valid. TODO: we could + //check the Component to be sure it's a valid XName, but some + //customer might want to use their own node names and track things + //anyway; thus, for now at least, we won't limit tracking to just + //valid XNames. Note that this makes it possible for typos to be + //acceptable component names! + + ferrstr := "" + if jdata.Component == "" { + ferrstr = "Missing Component field" + } else if jdata.Hostname == "" { + ferrstr = "Missing Hostname field" + } else if jdata.NID == "" { + ferrstr = "Missing NID field" + } else if jdata.Status == "" { + ferrstr = "Missing Status field" + } else if jdata.Timestamp == "" { + ferrstr = "Missing Timestamp field" + } + + if ferrstr != "" { + hbtdPrintf("Incomplete heartbeat JSON: %s\n", ferrstr) + pdet := base.NewProblemDetails("about:blank", + "Invalid Request", + ferrstr, + errinst, http.StatusBadRequest) + base.SendProblemDetails(w, pdet, 0) + return + } + + //Check to be sure that certain fields' values are valid. + + if xnametypes.GetHMSType(jdata.Component) == xnametypes.HMSTypeInvalid { + hbtdPrintf("Invalid XName in heartbeat JSON: %s\n", jdata.Component) + pdet := base.NewProblemDetails("about:blank", + "Invalid Request", + "Invalid Component Name", + errinst, http.StatusBadRequest) + base.SendProblemDetails(w, pdet, 0) + return + } + + _, cerr := strconv.ParseInt(jdata.NID, 0, 64) + if cerr != nil { + hbtdPrintf("Invalid NID in heartbeat JSON: %s\n", jdata.NID) + pdet := base.NewProblemDetails("about:blank", + "Invalid Request", + "Invalid NID", + errinst, http.StatusBadRequest) + base.SendProblemDetails(w, pdet, 0) + return + } + + if app_params.debug_level.int_param > 0 { + hbtdPrintf("Heartbeat: Component: %s, Host: %s, NID: %s, Status: %s, time: %s\n", + jdata.Component, jdata.Hostname, jdata.NID, jdata.Status, + jdata.Timestamp) + } + + if app_params.debug_level.int_param > 1 { + hbtdPrintf("HB received for: '%s'", jdata.Component) + } + + //Update the time stamp and info for this component. + + updateHB(errinst, jdata.Component, jdata.Timestamp, jdata.Status, w) } func hbRcvXName(w http.ResponseWriter, r *http.Request) { - errinst := URL_HEARTBEAT - xname := mux.Vars(r)["xname"] - if (xname == "") { - //Should NOT happen, but if so, grab the end of the URL - toks := strings.Split(r.URL.Path,"/") - xn := base.NormalizeHMSCompID(toks[len(toks)-1]) - if (xn == "") { - //Enforce valid XName - hbtdPrintf("ERROR: request is not a POST.\n") - pdet := base.NewProblemDetails("about:blank", - "Invalid Request", - "Only POST operations supported", - errinst,http.StatusMethodNotAllowed) - base.SendProblemDetails(w,pdet,0) - return - } + errinst := URL_HEARTBEAT + xname := mux.Vars(r)["xname"] + if xname == "" { + //Should NOT happen, but if so, grab the end of the URL + toks := strings.Split(r.URL.Path, "/") + xn := xnametypes.NormalizeHMSCompID(toks[len(toks)-1]) + if xn == "" { + //Enforce valid XName + hbtdPrintf("ERROR: request is not a POST.\n") + pdet := base.NewProblemDetails("about:blank", + "Invalid Request", + "Only POST operations supported", + errinst, http.StatusMethodNotAllowed) + base.SendProblemDetails(w, pdet, 0) + return + } xname = xn } - if (r.Method != "POST") { - hbtdPrintf("ERROR: request is not a POST.\n") - pdet := base.NewProblemDetails("about:blank", - "Invalid Request", - "Only POST operations supported", - errinst,http.StatusMethodNotAllowed) - - //It is required to have an "Allow:" header with this error - w.Header().Add("Allow","POST") - base.SendProblemDetails(w,pdet,0) - return - } - - var jdata hbjson_v1 - body,err := ioutil.ReadAll(r.Body) - err = json.Unmarshal(body,&jdata) - - if (err != nil) { - var v map[string]interface{} - - //The Unmarshal failed, find out which field(s) specifically failed. - //There's no quick-n-dirty way to do this so we'll just bulldoze - //through each field and verify it. - - errstr := "Invalid JSON data type" - errb := json.Unmarshal(body,&v) - if (errb != nil) { - hbtdPrintln("Unmarshal into map[string]interface{} didn't work:",errb) - } else { - //Figure out what field(s) == bad and report them. For now, they're - //all strings. - - mtype := reflect.TypeOf(jdata) - for i := 0; i < mtype.NumField(); i ++ { - nm := mtype.Field(i).Name - if (v[nm] == nil) { - continue - } - _,ok := v[nm].(string) //for now everything is strings. - if (!ok) { - errstr = fmt.Sprintf("Invalid data type in %s field",nm) - break - } - } - } - hbtdPrintln("Bad heartbeat JSON decode:",err); - pdet := base.NewProblemDetails("about:blank", - "Invalid Request", - errstr, - errinst,http.StatusBadRequest) - base.SendProblemDetails(w,pdet,0) - return - } - - //Check all the fields to be sure they are valid. - - ferrstr := "" - if (jdata.Status == "") { - ferrstr = "Missing Status field" - } else if (jdata.Timestamp == "") { - ferrstr = "Missing Timestamp field" - } - - if (ferrstr != "") { - hbtdPrintf("Incomplete heartbeat JSON: %s\n",ferrstr); - pdet := base.NewProblemDetails("about:blank", - "Invalid Request", - ferrstr, - errinst,http.StatusBadRequest) - base.SendProblemDetails(w,pdet,0) - return - } - - if (app_params.debug_level.int_param > 0) { - hbtdPrintf("Heartbeat: Status: %s, time: %s\n", - jdata.Status,jdata.Timestamp) - } - - //Update the time stamp and info for this component. - - if (app_params.debug_level.int_param > 1) { - hbtdPrintf("HB received for: '%s'",xname) - } - - updateHB(errinst,xname,jdata.Timestamp,jdata.Status,w) + if r.Method != "POST" { + hbtdPrintf("ERROR: request is not a POST.\n") + pdet := base.NewProblemDetails("about:blank", + "Invalid Request", + "Only POST operations supported", + errinst, http.StatusMethodNotAllowed) + + //It is required to have an "Allow:" header with this error + w.Header().Add("Allow", "POST") + base.SendProblemDetails(w, pdet, 0) + return + } + + var jdata hbjson_v1 + body, err := ioutil.ReadAll(r.Body) + err = json.Unmarshal(body, &jdata) + + if err != nil { + var v map[string]interface{} + + //The Unmarshal failed, find out which field(s) specifically failed. + //There's no quick-n-dirty way to do this so we'll just bulldoze + //through each field and verify it. + + errstr := "Invalid JSON data type" + errb := json.Unmarshal(body, &v) + if errb != nil { + hbtdPrintln("Unmarshal into map[string]interface{} didn't work:", errb) + } else { + //Figure out what field(s) == bad and report them. For now, they're + //all strings. + + mtype := reflect.TypeOf(jdata) + for i := 0; i < mtype.NumField(); i++ { + nm := mtype.Field(i).Name + if v[nm] == nil { + continue + } + _, ok := v[nm].(string) //for now everything is strings. + if !ok { + errstr = fmt.Sprintf("Invalid data type in %s field", nm) + break + } + } + } + hbtdPrintln("Bad heartbeat JSON decode:", err) + pdet := base.NewProblemDetails("about:blank", + "Invalid Request", + errstr, + errinst, http.StatusBadRequest) + base.SendProblemDetails(w, pdet, 0) + return + } + + //Check all the fields to be sure they are valid. + + ferrstr := "" + if jdata.Status == "" { + ferrstr = "Missing Status field" + } else if jdata.Timestamp == "" { + ferrstr = "Missing Timestamp field" + } + + if ferrstr != "" { + hbtdPrintf("Incomplete heartbeat JSON: %s\n", ferrstr) + pdet := base.NewProblemDetails("about:blank", + "Invalid Request", + ferrstr, + errinst, http.StatusBadRequest) + base.SendProblemDetails(w, pdet, 0) + return + } + + if app_params.debug_level.int_param > 0 { + hbtdPrintf("Heartbeat: Status: %s, time: %s\n", + jdata.Status, jdata.Timestamp) + } + + //Update the time stamp and info for this component. + + if app_params.debug_level.int_param > 1 { + hbtdPrintf("HB received for: '%s'", xname) + } + + updateHB(errinst, xname, jdata.Timestamp, jdata.Status, w) } ///////////////////////////////////////////////////////////////////////////// @@ -1125,89 +1123,89 @@ func hbRcvXName(w http.ResponseWriter, r *http.Request) { ///////////////////////////////////////////////////////////////////////////// func paramsIO(w http.ResponseWriter, r *http.Request) { - var rparams []byte - errinst := URL_PARAMS - - if (r.Method == "PATCH") { - body,err := ioutil.ReadAll(r.Body) - - if (err != nil) { - hbtdPrintln("Error on message read:",err); - pdet := base.NewProblemDetails("about:blank", - "Invalid Request", - "Error reading inbound request", - errinst,http.StatusBadRequest) - base.SendProblemDetails(w,pdet,0) - return - } - - //OK, payload is OK. Set the param values found in it. - - var errstrs string - - if (parse_parm_json(body,PARAM_PATCH,&errstrs) != 0) { - hbtdPrintf("Error parsing parameter JSON: '%s'\n",errstrs) - pdet := base.NewProblemDetails("about:blank", - "Invalid Request", - errstrs, - errinst,http.StatusBadRequest) - base.SendProblemDetails(w,pdet,0) - return - } - - //OK, if we got here, things applied correctly. Generate a JSON - //response with the current values of the parameters. - - if (gen_cur_param_json(&rparams) != 0) { - pdet := base.NewProblemDetails("about:blank", - "Internal Server Error", - "Failed JSON marshall", - errinst,http.StatusInternalServerError) - base.SendProblemDetails(w,pdet,0) - return - } - - //Set this JSON blob as a key for the KV store so that all - //instances of this service see it and use the same values of - //parameters. - - merr := kvHandle.Store(KV_PARAM_KEY,string(rparams)) - if (merr != nil) { - hbtdPrintln("INTERNAL ERROR storing KV params value ", - string(rparams),": ",merr); - pdet := base.NewProblemDetails("about:blank", - "Internal Server Error", - "Failed KV service STORE operation", - errinst,http.StatusInternalServerError) - base.SendProblemDetails(w,pdet,0) - return - } - - w.WriteHeader(http.StatusOK) - w.Write(rparams) - } else if (r.Method == "GET") { - if (gen_cur_param_json(&rparams) != 0) { - pdet := base.NewProblemDetails("about:blank", - "Internal Server Error", - "Failed JSON marshall", - errinst,http.StatusInternalServerError) - base.SendProblemDetails(w,pdet,0) - return - } - w.Header().Set("Content-Type","application/json") - w.WriteHeader(http.StatusOK) - w.Write(rparams) - } else { - hbtdPrintf("ERROR: request is not a PATCH or a GET.\n") - pdet := base.NewProblemDetails("about:blank", - "Invalid Request", - "Only PATCH and GET operations supported", - errinst,http.StatusMethodNotAllowed) - //It is required to have an "Allow:" header with this error - w.Header().Add("Allow","GET,PATCH") - base.SendProblemDetails(w,pdet,0) - return - } + var rparams []byte + errinst := URL_PARAMS + + if r.Method == "PATCH" { + body, err := ioutil.ReadAll(r.Body) + + if err != nil { + hbtdPrintln("Error on message read:", err) + pdet := base.NewProblemDetails("about:blank", + "Invalid Request", + "Error reading inbound request", + errinst, http.StatusBadRequest) + base.SendProblemDetails(w, pdet, 0) + return + } + + //OK, payload is OK. Set the param values found in it. + + var errstrs string + + if parse_parm_json(body, PARAM_PATCH, &errstrs) != 0 { + hbtdPrintf("Error parsing parameter JSON: '%s'\n", errstrs) + pdet := base.NewProblemDetails("about:blank", + "Invalid Request", + errstrs, + errinst, http.StatusBadRequest) + base.SendProblemDetails(w, pdet, 0) + return + } + + //OK, if we got here, things applied correctly. Generate a JSON + //response with the current values of the parameters. + + if gen_cur_param_json(&rparams) != 0 { + pdet := base.NewProblemDetails("about:blank", + "Internal Server Error", + "Failed JSON marshall", + errinst, http.StatusInternalServerError) + base.SendProblemDetails(w, pdet, 0) + return + } + + //Set this JSON blob as a key for the KV store so that all + //instances of this service see it and use the same values of + //parameters. + + merr := kvHandle.Store(KV_PARAM_KEY, string(rparams)) + if merr != nil { + hbtdPrintln("INTERNAL ERROR storing KV params value ", + string(rparams), ": ", merr) + pdet := base.NewProblemDetails("about:blank", + "Internal Server Error", + "Failed KV service STORE operation", + errinst, http.StatusInternalServerError) + base.SendProblemDetails(w, pdet, 0) + return + } + + w.WriteHeader(http.StatusOK) + w.Write(rparams) + } else if r.Method == "GET" { + if gen_cur_param_json(&rparams) != 0 { + pdet := base.NewProblemDetails("about:blank", + "Internal Server Error", + "Failed JSON marshall", + errinst, http.StatusInternalServerError) + base.SendProblemDetails(w, pdet, 0) + return + } + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + w.Write(rparams) + } else { + hbtdPrintf("ERROR: request is not a PATCH or a GET.\n") + pdet := base.NewProblemDetails("about:blank", + "Invalid Request", + "Only PATCH and GET operations supported", + errinst, http.StatusMethodNotAllowed) + //It is required to have an "Allow:" header with this error + w.Header().Add("Allow", "GET,PATCH") + base.SendProblemDetails(w, pdet, 0) + return + } } // Convenience function, given a component name and time reference, determine @@ -1222,26 +1220,26 @@ func paramsIO(w http.ResponseWriter, r *http.Request) { func isHeartbeating(xname string, now int64, errinst string) (bool, *base.ProblemDetails) { var hbb hbinfo - kval,kok,kerr := kvHandle.Get(xname) - if (kerr != nil) { + kval, kok, kerr := kvHandle.Get(xname) + if kerr != nil { pdet := base.NewProblemDetails("about:blank", - "Invalid Request", - fmt.Sprintf("Error retrieving key '%s'",xname), - errinst,http.StatusInternalServerError) - return false,pdet + "Invalid Request", + fmt.Sprintf("Error retrieving key '%s'", xname), + errinst, http.StatusInternalServerError) + return false, pdet } - if (kok == false) { - return false,nil + if kok == false { + return false, nil } - umerr := json.Unmarshal([]byte(kval),&hbb) - if (umerr != nil) { - hbtdPrintln("INTERNAL ERROR unmarshalling '",kval,"': ",umerr) + umerr := json.Unmarshal([]byte(kval), &hbb) + if umerr != nil { + hbtdPrintln("INTERNAL ERROR unmarshalling '", kval, "': ", umerr) pdet := base.NewProblemDetails("about:blank", - "Internal Server Error", - fmt.Sprintf("Error unmarshalling JSON for key '%s'",xname), - errinst,http.StatusInternalServerError) - return false,pdet + "Internal Server Error", + fmt.Sprintf("Error unmarshalling JSON for key '%s'", xname), + errinst, http.StatusInternalServerError) + return false, pdet } //Get the HB record's Last_hb_rcv_time timestamp and decode it. @@ -1249,13 +1247,13 @@ func isHeartbeating(xname string, now int64, errinst string) (bool, *base.Proble //If the time lapse is >= error time, node is not heartbeating; else, //it is. "Might be dead" is assumed to be still functioning. - lhbtime,_ := strconv.ParseInt(hbb.Last_hb_rcv_time,16,64) + lhbtime, _ := strconv.ParseInt(hbb.Last_hb_rcv_time, 16, 64) tdiff := now - lhbtime - if (tdiff >= int64(app_params.errtime.int_param)) { - return false,nil + if tdiff >= int64(app_params.errtime.int_param) { + return false, nil } - return true,nil + return true, nil } // Entry point for /hmi/v1/hbstates @@ -1266,55 +1264,55 @@ func hbStates(w http.ResponseWriter, r *http.Request) { var rspSingle hbSingleStateRsp errinst := URL_HB_STATES - body,err := ioutil.ReadAll(r.Body) + body, err := ioutil.ReadAll(r.Body) - if (err != nil) { - hbtdPrintln("Error on message read:",err); + if err != nil { + hbtdPrintln("Error on message read:", err) pdet := base.NewProblemDetails("about:blank", - "Invalid Request", - "Error reading inbound request", - errinst,http.StatusBadRequest) - base.SendProblemDetails(w,pdet,0) + "Invalid Request", + "Error reading inbound request", + errinst, http.StatusBadRequest) + base.SendProblemDetails(w, pdet, 0) return } - err = json.Unmarshal(body,&jdata) - if (err != nil) { - hbtdPrintf("Error unmarshalling HB state req data: %v",err) + err = json.Unmarshal(body, &jdata) + if err != nil { + hbtdPrintf("Error unmarshalling HB state req data: %v", err) pdet := base.NewProblemDetails("about:blank", - "Invalid Request", - "Error unmarshalling inbound request", - errinst,http.StatusBadRequest) - base.SendProblemDetails(w,pdet,0) + "Invalid Request", + "Error unmarshalling inbound request", + errinst, http.StatusBadRequest) + base.SendProblemDetails(w, pdet, 0) return } now := time.Now().Unix() - for _,comp := range(jdata.XNames) { - isHB,pdet := isHeartbeating(comp,now,errinst) + for _, comp := range jdata.XNames { + isHB, pdet := isHeartbeating(comp, now, errinst) - if (pdet != nil) { - base.SendProblemDetails(w,pdet,0) + if pdet != nil { + base.SendProblemDetails(w, pdet, 0) return } rspSingle.XName = comp rspSingle.Heartbeating = isHB - rspData.HBStates = append(rspData.HBStates,rspSingle) + rspData.HBStates = append(rspData.HBStates, rspSingle) } - ba,baerr := json.Marshal(&rspData) - if (baerr != nil) { - hbtdPrintf("INTERNAL ERROR marshalling rsp data: %v",baerr) + ba, baerr := json.Marshal(&rspData) + if baerr != nil { + hbtdPrintf("INTERNAL ERROR marshalling rsp data: %v", baerr) pdet := base.NewProblemDetails("about:blank", - "Internal Server Error", - "Error marshalling JSON return data", - errinst,http.StatusInternalServerError) - base.SendProblemDetails(w,pdet,0) + "Internal Server Error", + "Error marshalling JSON return data", + errinst, http.StatusInternalServerError) + base.SendProblemDetails(w, pdet, 0) return } - w.Header().Set("Content-Type","application/json") + w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) w.Write(ba) } @@ -1325,32 +1323,31 @@ func hbStateSingle(w http.ResponseWriter, r *http.Request) { var rspSingle hbSingleStateRsp vars := mux.Vars(r) - targ := base.NormalizeHMSCompID(vars["xname"]) - errinst := URL_HB_STATE+"/"+targ + targ := xnametypes.NormalizeHMSCompID(vars["xname"]) + errinst := URL_HB_STATE + "/" + targ now := time.Now().Unix() - isHB,pdet := isHeartbeating(targ,now,errinst) + isHB, pdet := isHeartbeating(targ, now, errinst) - if (pdet != nil) { - base.SendProblemDetails(w,pdet,0) + if pdet != nil { + base.SendProblemDetails(w, pdet, 0) return } rspSingle.XName = targ rspSingle.Heartbeating = isHB - ba,baerr := json.Marshal(&rspSingle) - if (baerr != nil) { - hbtdPrintf("INTERNAL ERROR marshalling rsp data: %v",baerr) + ba, baerr := json.Marshal(&rspSingle) + if baerr != nil { + hbtdPrintf("INTERNAL ERROR marshalling rsp data: %v", baerr) pdet := base.NewProblemDetails("about:blank", - "Internal Server Error", - "Error marshalling JSON return data", - errinst,http.StatusInternalServerError) - base.SendProblemDetails(w,pdet,0) + "Internal Server Error", + "Error marshalling JSON return data", + errinst, http.StatusInternalServerError) + base.SendProblemDetails(w, pdet, 0) return } - w.Header().Set("Content-Type","application/json") + w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) w.Write(ba) } - diff --git a/cmd/hbtd/track_test.go b/cmd/hbtd/track_test.go index e08bb8f..5d0df1d 100644 --- a/cmd/hbtd/track_test.go +++ b/cmd/hbtd/track_test.go @@ -1,6 +1,6 @@ // MIT License // -// (C) Copyright [2018-2021] Hewlett Packard Enterprise Development LP +// (C) Copyright [2018-2021,2023] Hewlett Packard Enterprise Development LP // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), @@ -23,26 +23,26 @@ package main import ( - "testing" - "fmt" - "net/http" - "net/http/httptest" - "time" - "bytes" - "sort" - "strings" - "strconv" - "sync" - "io/ioutil" - "encoding/json" - "crypto/tls" - "regexp" - "github.com/Cray-HPE/hms-base" - "github.com/Cray-HPE/hms-hmetcd" - "github.com/gorilla/mux" + "bytes" + "crypto/tls" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/http/httptest" + "regexp" + "sort" + "strconv" + "strings" + "sync" + "testing" + "time" + + base "github.com/Cray-HPE/hms-base/v2" + hmetcd "github.com/Cray-HPE/hms-hmetcd" + "github.com/gorilla/mux" ) - var SMRMutex sync.Mutex var SMRval int = http.StatusOK var router *mux.Router @@ -51,8 +51,8 @@ var router *mux.Router var testStdout string var tdMutex sync.Mutex -// Home-brewed replacements for fmt.Printf and fmt.Println. These will -// capture the stdout and use it to be able to compare output from hbtd +// Home-brewed replacements for fmt.Printf and fmt.Println. These will +// capture the stdout and use it to be able to compare output from hbtd // functions. // // fmtstr(in): Printf format string @@ -60,54 +60,53 @@ var tdMutex sync.Mutex // Return: int: 0 (assume success), nil: assume no error func testPrintf(fmtstr string, a ...interface{}) { - tdMutex.Lock() - //Extract data as needed? - tdata := fmt.Sprintf(fmtstr,a...) - //Insure newline - td := strings.TrimSpace(tdata) - td += "\n" - //Append to an array - testStdout = testStdout + td - tdMutex.Unlock() + tdMutex.Lock() + //Extract data as needed? + tdata := fmt.Sprintf(fmtstr, a...) + //Insure newline + td := strings.TrimSpace(tdata) + td += "\n" + //Append to an array + testStdout = testStdout + td + tdMutex.Unlock() } func testPrintln(a ...interface{}) { - tdMutex.Lock() - //Extract data as needed? - tdata := fmt.Sprintln(a...) - //Append to an array - //Insure newline - td := strings.TrimSpace(tdata) - td += "\n" - testStdout = testStdout + td - tdMutex.Unlock() + tdMutex.Lock() + //Extract data as needed? + tdata := fmt.Sprintln(a...) + //Append to an array + //Insure newline + td := strings.TrimSpace(tdata) + td += "\n" + testStdout = testStdout + td + tdMutex.Unlock() } func testPrintData() string { - var rstr string - tdMutex.Lock() - rstr = testStdout - tdMutex.Unlock() - return rstr + var rstr string + tdMutex.Lock() + rstr = testStdout + tdMutex.Unlock() + return rstr } func testPrintClear() { - tdMutex.Lock() - testStdout = "" - tdMutex.Unlock() + tdMutex.Lock() + testStdout = "" + tdMutex.Unlock() } var is_setup int = 0 func one_time_setup() error { - var err error = nil - - if (is_setup == 0) { - kvHandle,err = hmetcd.Open("mem:","") - } + var err error = nil + if is_setup == 0 { + kvHandle, err = hmetcd.Open("mem:", "") + } - return err + return err } // Compares expected hb_checker() stdout with actual. Actual stdout @@ -121,49 +120,49 @@ func one_time_setup() error { // Return: 0 if things compare OK, -1 if not func hb_compare(exps []string, acts string) int { - var loc_acts []string - - //Sort the exps with the same also as sorting the actuals - - loc_exps := exps - sort.Strings(loc_exps) - - //Split the actuals into strings at the newlines and filter out SM patch - //errors, which are irrelevant. - - la := strings.Split(strings.TrimSuffix(acts,"\n"),"\n") - for ix,_ := range(la) { - if (strings.Contains(la[ix],"ERROR sending PATCH")) { - continue - } - loc_acts = append(loc_acts,la[ix]) - } - sort.Strings(loc_acts) - - if (len(loc_exps) != len(loc_acts)) { - return -1 - } - - rx := regexp.MustCompile("overdue [0-9]+ seconds") - ntimeStr := "overdue xx seconds" - var estr,astr string - - for ix := 0; ix < len(loc_exps); ix++ { - //Remove all specific mention of seconds due to overly quantized - //time delay measurement. - - estr = rx.ReplaceAllString(loc_exps[ix],ntimeStr) - astr = rx.ReplaceAllString(loc_acts[ix],ntimeStr) - if (estr != astr) { - return -1 - } - } - return 0 + var loc_acts []string + + //Sort the exps with the same also as sorting the actuals + + loc_exps := exps + sort.Strings(loc_exps) + + //Split the actuals into strings at the newlines and filter out SM patch + //errors, which are irrelevant. + + la := strings.Split(strings.TrimSuffix(acts, "\n"), "\n") + for ix, _ := range la { + if strings.Contains(la[ix], "ERROR sending PATCH") { + continue + } + loc_acts = append(loc_acts, la[ix]) + } + sort.Strings(loc_acts) + + if len(loc_exps) != len(loc_acts) { + return -1 + } + + rx := regexp.MustCompile("overdue [0-9]+ seconds") + ntimeStr := "overdue xx seconds" + var estr, astr string + + for ix := 0; ix < len(loc_exps); ix++ { + //Remove all specific mention of seconds due to overly quantized + //time delay measurement. + + estr = rx.ReplaceAllString(loc_exps[ix], ntimeStr) + astr = rx.ReplaceAllString(loc_acts[ix], ntimeStr) + if estr != astr { + return -1 + } + } + return 0 } func make_key(val *string, key string, ts int64) { - *val = fmt.Sprintf("{\"Component\":\"%s\",\"Last_hb_rcv_time\":\"%x\",\"Last_hb_timestamp\":\"\",\"Last_hb_status\":\"OK\",\"Had_warning\":\"%s\"}", - key,ts,HB_WARN_NONE) + *val = fmt.Sprintf("{\"Component\":\"%s\",\"Last_hb_rcv_time\":\"%x\",\"Last_hb_timestamp\":\"\",\"Last_hb_status\":\"OK\",\"Had_warning\":\"%s\"}", + key, ts, HB_WARN_NONE) } // Test entry point for hb_checker(). @@ -175,734 +174,735 @@ func make_key(val *string, key string, ts int64) { // Return: None. func TestHb_checker(t *testing.T) { - var err error - var kval string - var tpd string - var exp_strs_1 = []string { //after 4 sec - `Number of components heartbeating: 3`, - } - - var exp_strs_2 = []string { //after 7 sec - `WARNING: Heartbeat overdue 7 seconds for 'x0c1s2b0n3' (might be dead), last status: 'OK'`, - //`WARNING: Heartbeat overdue 5 seconds for 'x1c2s3b0n4' (might be dead), last status: 'OK'`, - } - - var exp_strs_3 = []string { //after 15 sec - //`ERROR: Heartbeat overdue 15 seconds for 'x0c1s2b0n3' (declared dead), last status: 'OK'`, - `WARNING: Heartbeat overdue 10 seconds for 'x1c2s3b0n4' (might be dead), last status: 'OK'`, - //`WARNING: Heartbeat overdue 9 seconds for 'x2c3s4b0n5' (might be dead), last status: 'OK'`, - //`WARNING: Heartbeat overdue 8 seconds for 'x3c4s5b0n6' (might be dead), last status: 'OK'`, - //`Number of components heartbeating: 1`, - } - - var exp_strs_4 = []string { //after 30 sec - `ERROR: Heartbeat overdue 30 seconds for 'x0c1s2b0n3' (declared dead), last status: 'OK'`, - `ERROR: Heartbeat overdue 25 seconds for 'x1c2s3b0n4' (declared dead), last status: 'OK'`, - `ERROR: Heartbeat overdue 20 seconds for 'x2c3s4b0n5' (declared dead), last status: 'OK'`, - `Number of components heartbeating: 0`, - } - - // Set up the app_params to specify warning and error HB timeouts - - t.Logf("** RUNNING hb_checker TEST **") - - ots_err := one_time_setup() - if (ots_err != nil) { - t.Error("ERROR setting up KV store:",ots_err) - return - } - staleKeys = false - app_params.debug_level.int_param = 0 - app_params.check_interval.int_param = 0 - app_params.warntime.int_param = 5 - app_params.errtime.int_param = 20 - - // Create KV entries for test components. Note that this would also - // be tested if we mock up an HTTP request and call hb_rcv() - - basetime := time.Now().Unix() - - keys := []string{"x0c1s2b0n3","x1c2s3b0n4","x2c3s4b0n5",} - dlys := []int{0,5,12} - - for ix,key := range keys { - make_key(&kval,key,(basetime+int64(dlys[ix]))) - err = kvHandle.Store(key,kval) - if (err != nil) { - t.Error("ERROR creating KV record for '",key,"': ",err) - } - } - - t.Logf("** Testing HB checker timer loop **\n") - - // Sleep for various amounts of time, calling hb_checker() after each - // sleep cycle. Heartbeat stops will be printed. - - // Test 1: 4 seconds elapsed. Should be no HB stops - - testPrintClear() - t.Logf("Sleeping 4 secs.\n"); - time.Sleep(4*time.Second) - t.Logf("Invoking HB checker.\n"); - hb_checker() + var err error + var kval string + var tpd string + var exp_strs_1 = []string{ //after 4 sec + `Number of components heartbeating: 3`, + } + + var exp_strs_2 = []string{ //after 7 sec + `WARNING: Heartbeat overdue 7 seconds for 'x0c1s2b0n3' (might be dead), last status: 'OK'`, + //`WARNING: Heartbeat overdue 5 seconds for 'x1c2s3b0n4' (might be dead), last status: 'OK'`, + } + + var exp_strs_3 = []string{ //after 15 sec + //`ERROR: Heartbeat overdue 15 seconds for 'x0c1s2b0n3' (declared dead), last status: 'OK'`, + `WARNING: Heartbeat overdue 10 seconds for 'x1c2s3b0n4' (might be dead), last status: 'OK'`, + //`WARNING: Heartbeat overdue 9 seconds for 'x2c3s4b0n5' (might be dead), last status: 'OK'`, + //`WARNING: Heartbeat overdue 8 seconds for 'x3c4s5b0n6' (might be dead), last status: 'OK'`, + //`Number of components heartbeating: 1`, + } + + var exp_strs_4 = []string{ //after 30 sec + `ERROR: Heartbeat overdue 30 seconds for 'x0c1s2b0n3' (declared dead), last status: 'OK'`, + `ERROR: Heartbeat overdue 25 seconds for 'x1c2s3b0n4' (declared dead), last status: 'OK'`, + `ERROR: Heartbeat overdue 20 seconds for 'x2c3s4b0n5' (declared dead), last status: 'OK'`, + `Number of components heartbeating: 0`, + } + + // Set up the app_params to specify warning and error HB timeouts + + t.Logf("** RUNNING hb_checker TEST **") + + ots_err := one_time_setup() + if ots_err != nil { + t.Error("ERROR setting up KV store:", ots_err) + return + } + staleKeys = false + app_params.debug_level.int_param = 0 + app_params.check_interval.int_param = 0 + app_params.warntime.int_param = 5 + app_params.errtime.int_param = 20 + + // Create KV entries for test components. Note that this would also + // be tested if we mock up an HTTP request and call hb_rcv() + + basetime := time.Now().Unix() + + keys := []string{"x0c1s2b0n3", "x1c2s3b0n4", "x2c3s4b0n5"} + dlys := []int{0, 5, 12} + + for ix, key := range keys { + make_key(&kval, key, (basetime + int64(dlys[ix]))) + err = kvHandle.Store(key, kval) + if err != nil { + t.Error("ERROR creating KV record for '", key, "': ", err) + } + } + + t.Logf("** Testing HB checker timer loop **\n") + + // Sleep for various amounts of time, calling hb_checker() after each + // sleep cycle. Heartbeat stops will be printed. + + // Test 1: 4 seconds elapsed. Should be no HB stops + + testPrintClear() + t.Logf("Sleeping 4 secs.\n") + time.Sleep(4 * time.Second) + t.Logf("Invoking HB checker.\n") + hb_checker() time.Sleep(100 * time.Millisecond) - tpd = testPrintData() - if (hb_compare(exp_strs_1,tpd) != 0) { - t.Errorf("ERROR, mismatch\nExp: '%s'\nAct: '%s'\n\n", - exp_strs_1,tpd) - } - - //Test 2: 7 seconds elapsed. The first 1 should show a HB stop warning - - testPrintClear() - t.Logf("Sleeping 3 secs.\n"); - time.Sleep(3 * time.Second) - t.Logf("Invoking HB checker.\n"); - hb_checker() + tpd = testPrintData() + if hb_compare(exp_strs_1, tpd) != 0 { + t.Errorf("ERROR, mismatch\nExp: '%s'\nAct: '%s'\n\n", + exp_strs_1, tpd) + } + + //Test 2: 7 seconds elapsed. The first 1 should show a HB stop warning + + testPrintClear() + t.Logf("Sleeping 3 secs.\n") + time.Sleep(3 * time.Second) + t.Logf("Invoking HB checker.\n") + hb_checker() time.Sleep(100 * time.Millisecond) - tpd = testPrintData() - if (hb_compare(exp_strs_2,tpd) != 0) { - t.Errorf("ERROR, mismatch\nExp: '%s'\nAct: '%s'\n\n", - exp_strs_2,tpd) - } - - //Test 3: 15 seconds elapsed. - //The second one should show a HB stop warning - - testPrintClear() - t.Logf("Sleeping 8 secs.\n"); - time.Sleep(8 * time.Second) - t.Logf("Invoking HB checker.\n"); - hb_checker() + tpd = testPrintData() + if hb_compare(exp_strs_2, tpd) != 0 { + t.Errorf("ERROR, mismatch\nExp: '%s'\nAct: '%s'\n\n", + exp_strs_2, tpd) + } + + //Test 3: 15 seconds elapsed. + //The second one should show a HB stop warning + + testPrintClear() + t.Logf("Sleeping 8 secs.\n") + time.Sleep(8 * time.Second) + t.Logf("Invoking HB checker.\n") + hb_checker() time.Sleep(100 * time.Millisecond) - tpd = testPrintData() - if (hb_compare(exp_strs_3,tpd) != 0) { - t.Errorf("ERROR, mismatch\nExp: '%s'\nAct: '%s'\n\n", - exp_strs_3,tpd) - } - - //Test 4: 30 seconds elapsed - //Remaining ones should show HB stop errors - - testPrintClear() - t.Logf("Sleeping 22 secs.\n"); - time.Sleep(22 * time.Second) - t.Logf("Invoking HB checker.\n"); - hb_checker() + tpd = testPrintData() + if hb_compare(exp_strs_3, tpd) != 0 { + t.Errorf("ERROR, mismatch\nExp: '%s'\nAct: '%s'\n\n", + exp_strs_3, tpd) + } + + //Test 4: 30 seconds elapsed + //Remaining ones should show HB stop errors + + testPrintClear() + t.Logf("Sleeping 22 secs.\n") + time.Sleep(22 * time.Second) + t.Logf("Invoking HB checker.\n") + hb_checker() time.Sleep(100 * time.Millisecond) - tpd = testPrintData() - if (hb_compare(exp_strs_4,tpd) != 0) { - t.Errorf("ERROR, mismatch\nExp: '%s'\nAct: '%s'\n\n", - exp_strs_4,tpd) - } - - time.Sleep(2 * time.Second) - t.Logf(" ==> FINISHED hb_checker TEST.") + tpd = testPrintData() + if hb_compare(exp_strs_4, tpd) != 0 { + t.Errorf("ERROR, mismatch\nExp: '%s'\nAct: '%s'\n\n", + exp_strs_4, tpd) + } + + time.Sleep(2 * time.Second) + t.Logf(" ==> FINISHED hb_checker TEST.") } func heartbeat(comp string) error { - kval,ok,err := kvHandle.Get(comp) - if (err != nil) { - return err - } - if (!ok) { - return fmt.Errorf("Key '%s' does not exist.",comp) - } - var hbb hbinfo - umerr := json.Unmarshal([]byte(kval),&hbb) - if (umerr != nil) { - return umerr - } - - hbb.Last_hb_rcv_time = strconv.FormatUint(uint64(time.Now().Unix()),16) - if (hbb.Had_warning == HB_WARN_GAP) { - hbb.Had_warning = HB_WARN_NORMAL - } - jstr,jerr := json.Marshal(hbb) - if (jerr != nil) { - return jerr - } - - err = kvHandle.Store(comp,string(jstr)) - if (err != nil) { - return err - } - return nil + kval, ok, err := kvHandle.Get(comp) + if err != nil { + return err + } + if !ok { + return fmt.Errorf("Key '%s' does not exist.", comp) + } + var hbb hbinfo + umerr := json.Unmarshal([]byte(kval), &hbb) + if umerr != nil { + return umerr + } + + hbb.Last_hb_rcv_time = strconv.FormatUint(uint64(time.Now().Unix()), 16) + if hbb.Had_warning == HB_WARN_GAP { + hbb.Had_warning = HB_WARN_NORMAL + } + jstr, jerr := json.Marshal(hbb) + if jerr != nil { + return jerr + } + + err = kvHandle.Store(comp, string(jstr)) + if err != nil { + return err + } + return nil } func kill_sm_goroutines() { for { - if (len(hsmUpdateQ) == 0) { + if len(hsmUpdateQ) == 0 { break } _ = <-hsmUpdateQ } - hsmUpdateQ <-HSMQ_DIE + hsmUpdateQ <- HSMQ_DIE time.Sleep(100 * time.Millisecond) for { - if (len(hsmUpdateQ) == 0) { + if len(hsmUpdateQ) == 0 { break } _ = <-hsmUpdateQ } - time.Sleep(2 * time.Second) + time.Sleep(2 * time.Second) } // Similar to TestHb_checker(), but this version checks for stale keys func TestHb_checker2(t *testing.T) { - var err error - var kval string - var tpd string - - var exp_strs_1 = []string{ - `WARNING: Heartbeat overdue 60 seconds for 'x0c1s2b0n3' due to HB monitoring gap; might be dead, last status: 'OK'`, - `WARNING: Heartbeat overdue 60 seconds for 'x0c1s2b0n4' due to HB monitoring gap; might be dead, last status: 'OK'`, - `Number of components heartbeating: 2`, - } - - var exp_strs_2 = []string{`INFO: Heartbeat restarted for 'x0c1s2b0n3'`, } - - var exp_strs_3 = []string{} - - var exp_strs_4 = []string{ - `ERROR: Heartbeat overdue 24 seconds for 'x0c1s2b0n4' (declared dead), last status: 'OK'`, - `Number of components heartbeating: 1`, - } - - //TODO: do we need a time second edge detect? - - hbtdPrintf = testPrintf - hbtdPrintln = testPrintln - - t.Logf("** RUNNING hb_checker GAP TEST **") - - ots_err := one_time_setup() - if (ots_err != nil) { - t.Error("ERROR setting up KV store:",ots_err) - return - } - hsmReady = true - staleKeys = true - app_params.debug_level.int_param = 0 - app_params.check_interval.int_param = 0 - app_params.warntime.int_param = 15 - app_params.errtime.int_param = 20 - - for (len(hsmUpdateQ) > 0) { - <-hsmUpdateQ - } - - go send_sm_req() - time.Sleep(500 * time.Millisecond) - - // Create KV entries for test components. Note that this would also - // be tested if we mock up an HTTP request and call hb_rcv() - - basetime := time.Now().Unix() - - key1 := "x0c1s2b0n3" - make_key(&kval,key1,(basetime-int64(60))) - err = kvHandle.Store(key1,kval) - if (err != nil) { - t.Error("ERROR creating KV record for '",key1,"': ",err) - } - key2 := "x0c1s2b0n4" - make_key(&kval,key2,(basetime-int64(60))) - err = kvHandle.Store(key2,kval) - if (err != nil) { - t.Error("ERROR creating KV record for '",key2,"': ",err) - } - - testPrintClear() - hb_checker() + var err error + var kval string + var tpd string + + var exp_strs_1 = []string{ + `WARNING: Heartbeat overdue 60 seconds for 'x0c1s2b0n3' due to HB monitoring gap; might be dead, last status: 'OK'`, + `WARNING: Heartbeat overdue 60 seconds for 'x0c1s2b0n4' due to HB monitoring gap; might be dead, last status: 'OK'`, + `Number of components heartbeating: 2`, + } + + var exp_strs_2 = []string{`INFO: Heartbeat restarted for 'x0c1s2b0n3'`} + + var exp_strs_3 = []string{} + + var exp_strs_4 = []string{ + `ERROR: Heartbeat overdue 24 seconds for 'x0c1s2b0n4' (declared dead), last status: 'OK'`, + `Number of components heartbeating: 1`, + } + + //TODO: do we need a time second edge detect? + + hbtdPrintf = testPrintf + hbtdPrintln = testPrintln + + t.Logf("** RUNNING hb_checker GAP TEST **") + + ots_err := one_time_setup() + if ots_err != nil { + t.Error("ERROR setting up KV store:", ots_err) + return + } + hsmReady = true + staleKeys = true + app_params.debug_level.int_param = 0 + app_params.check_interval.int_param = 0 + app_params.warntime.int_param = 15 + app_params.errtime.int_param = 20 + + for len(hsmUpdateQ) > 0 { + <-hsmUpdateQ + } + + go send_sm_req() + time.Sleep(500 * time.Millisecond) + + // Create KV entries for test components. Note that this would also + // be tested if we mock up an HTTP request and call hb_rcv() + + basetime := time.Now().Unix() + + key1 := "x0c1s2b0n3" + make_key(&kval, key1, (basetime - int64(60))) + err = kvHandle.Store(key1, kval) + if err != nil { + t.Error("ERROR creating KV record for '", key1, "': ", err) + } + key2 := "x0c1s2b0n4" + make_key(&kval, key2, (basetime - int64(60))) + err = kvHandle.Store(key2, kval) + if err != nil { + t.Error("ERROR creating KV record for '", key2, "': ", err) + } + + testPrintClear() + hb_checker() time.Sleep(100 * time.Millisecond) - tpd = testPrintData() - if (hb_compare(exp_strs_1,tpd) != 0) { - t.Errorf("ERROR, mismatch\nExp: '%s'\nAct: '%s'\n\n", - exp_strs_1,tpd) - } - // At this time, both keys' times are set to base - - time.Sleep(2* time.Second) - - //"heartbeat" by updating a node's time stamp - err = heartbeat(key1) - if (err != nil) { - t.Errorf("ERROR performing fake heartbeat: %v",err) - } - - //comp key1 HB is set to base+2. - testPrintClear() - time.Sleep(10 * time.Second) - - //Should see restarted warning on key1 - hb_checker() - time.Sleep(100 * time.Millisecond) - tpd = testPrintData() - if (hb_compare(exp_strs_2,tpd) != 0) { - t.Errorf("ERROR, mismatch\nExp: '%s'\nAct: '%s'\n\n", - exp_strs_2,tpd) - } - - heartbeat(key1) - - testPrintClear() - time.Sleep(4 * time.Second) - //This is base + 17 for key2. Should not show any warnings. - - hb_checker() - time.Sleep(100 * time.Millisecond) - tpd = testPrintData() - if (hb_compare(exp_strs_3,tpd) != 0) { - t.Errorf("ERROR, mismatch\nExp: '%s'\nAct: '%s'\n\n", - exp_strs_3,tpd) - } - - testPrintClear() - time.Sleep(6 * time.Second) - //This is base + 24. Should be an error for key2 - - heartbeat(key1) - hb_checker() - time.Sleep(100 * time.Millisecond) - tpd = testPrintData() - if (hb_compare(exp_strs_4,tpd) != 0) { - t.Errorf("ERROR, mismatch\nExp: '%s'\nAct: '%s'\n\n", - exp_strs_4,tpd) - } - - //Kill the send goroutine - - kill_sm_goroutines() -} + tpd = testPrintData() + if hb_compare(exp_strs_1, tpd) != 0 { + t.Errorf("ERROR, mismatch\nExp: '%s'\nAct: '%s'\n\n", + exp_strs_1, tpd) + } + // At this time, both keys' times are set to base + + time.Sleep(2 * time.Second) + //"heartbeat" by updating a node's time stamp + err = heartbeat(key1) + if err != nil { + t.Errorf("ERROR performing fake heartbeat: %v", err) + } + + //comp key1 HB is set to base+2. + testPrintClear() + time.Sleep(10 * time.Second) + + //Should see restarted warning on key1 + hb_checker() + time.Sleep(100 * time.Millisecond) + tpd = testPrintData() + if hb_compare(exp_strs_2, tpd) != 0 { + t.Errorf("ERROR, mismatch\nExp: '%s'\nAct: '%s'\n\n", + exp_strs_2, tpd) + } + + heartbeat(key1) + + testPrintClear() + time.Sleep(4 * time.Second) + //This is base + 17 for key2. Should not show any warnings. + + hb_checker() + time.Sleep(100 * time.Millisecond) + tpd = testPrintData() + if hb_compare(exp_strs_3, tpd) != 0 { + t.Errorf("ERROR, mismatch\nExp: '%s'\nAct: '%s'\n\n", + exp_strs_3, tpd) + } + + testPrintClear() + time.Sleep(6 * time.Second) + //This is base + 24. Should be an error for key2 + + heartbeat(key1) + hb_checker() + time.Sleep(100 * time.Millisecond) + tpd = testPrintData() + if hb_compare(exp_strs_4, tpd) != 0 { + t.Errorf("ERROR, mismatch\nExp: '%s'\nAct: '%s'\n\n", + exp_strs_4, tpd) + } + + //Kill the send goroutine + + kill_sm_goroutines() +} func hb_cmp(t *testing.T, cmp string, ts string, status string) { - var kval string - var kok bool - var err error - var kjson hbinfo - - //Fetch key from KV store - - kval,kok,err = kvHandle.Get(cmp) - if (err != nil) { - t.Error("ERROR looking up key '",cmp,"': ",err) - return - } - if (!kok) { - t.Errorf("ERROR, key not found: '%s'\n",cmp) - return - } - - err = json.Unmarshal([]byte(kval),&kjson) - if (err != nil) { - t.Error("ERROR unmarshalling '",kval,"': ",err) - return - } - - - if (kjson.Component != cmp) { - t.Errorf("ERROR, mismatch component name in '%s', got '%s'.\n", - cmp, kjson.Component) - return - } - if (kjson.Last_hb_timestamp != ts) { - t.Errorf("ERROR, mismatch timestamp in '%s', got '%s'.\n", - ts, kjson.Last_hb_timestamp) - return - } - if (kjson.Last_hb_status != status) { - t.Errorf("ERROR, mismatch status in '%s', got '%s'.\n", - status, kjson.Last_hb_status) - return - } + var kval string + var kok bool + var err error + var kjson hbinfo + + //Fetch key from KV store + + kval, kok, err = kvHandle.Get(cmp) + if err != nil { + t.Error("ERROR looking up key '", cmp, "': ", err) + return + } + if !kok { + t.Errorf("ERROR, key not found: '%s'\n", cmp) + return + } + + err = json.Unmarshal([]byte(kval), &kjson) + if err != nil { + t.Error("ERROR unmarshalling '", kval, "': ", err) + return + } + + if kjson.Component != cmp { + t.Errorf("ERROR, mismatch component name in '%s', got '%s'.\n", + cmp, kjson.Component) + return + } + if kjson.Last_hb_timestamp != ts { + t.Errorf("ERROR, mismatch timestamp in '%s', got '%s'.\n", + ts, kjson.Last_hb_timestamp) + return + } + if kjson.Last_hb_status != status { + t.Errorf("ERROR, mismatch status in '%s', got '%s'.\n", + status, kjson.Last_hb_status) + return + } +} + +func heartbeatBody(xname, status, timestamp string) *bytes.Buffer { + body := `{"Component":"` + xname + + `","Hostname":"` + "nid0001.us.cray.com" + + `","NID":"` + "0001" + + `","Status":"` + status + + `","Timestamp":"` + timestamp + `"}` + return bytes.NewBufferString(body) +} + +func postHeartbeat(t *testing.T, requestBody *bytes.Buffer, expectedReturnCode int) { + url := "http://localhost:8080/hmi/v1/heartbeat" + req, err := http.NewRequest("POST", url, requestBody) + if err != nil { + t.Fatal(err) + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(hbRcv) + handler.ServeHTTP(rr, req) + + // Check the return code + if rr.Code != expectedReturnCode { + t.Errorf("Wrong http code: expected: %v, actual: %v, url: %s, requestBody: %v", expectedReturnCode, rr.Code, url, requestBody) + } +} + +func heartbeatToXnameBody(status, timestamp string) *bytes.Buffer { + body := `{"Status":"` + status + `","Timestamp":"` + timestamp + `"}` + return bytes.NewBufferString(body) +} + +func postHeartbeatToXname(t *testing.T, xname string, requestBody *bytes.Buffer, expectedReturnCode int) { + url := "http://localhost:8080/hmi/v1/heartbeat/" + xname + req, err := http.NewRequest("POST", url, requestBody) + + if err != nil { + t.Fatal(err) + } + + // Set up to grab the "responses" + rr := httptest.NewRecorder() + handlerXName := http.HandlerFunc(hbRcvXName) + + // Mock up the second operation + handlerXName.ServeHTTP(rr, req) + + // Check the return code + if rr.Code != expectedReturnCode { + t.Errorf("Wrong http code: expected: %v, actual: %v, url: %s, requestBody: %v", expectedReturnCode, rr.Code, url, requestBody) + } } // Test entry point for hb_rcv(), which is the HB HTTP request handler. // We will fake out an HTTP request object and record the response. // We will also examine the newly-created components in the HB tracking // map to be sure the right ones were created and with the right info. -// +// // t(in) Test framework. // Return: None. func TestHb_rcv(t *testing.T) { - t.Logf("** RUNNING HEARTBEAT HTTP OPERATIONS TEST\n") + t.Logf("** RUNNING HEARTBEAT HTTP OPERATIONS TEST\n") - ots_err := one_time_setup() - if (ots_err != nil) { - t.Error("ERROR setting up KV store:",ots_err) - return - } + ots_err := one_time_setup() + if ots_err != nil { + t.Error("ERROR setting up KV store:", ots_err) + return + } - //Slop together JSON payloads for 2 heartbeating nodes + //Slop together JSON payloads for 2 heartbeating nodes + xnames := []string{"x1c2s2b0n3", "x1c2s2b0n3v4"} + timestamps := []string{"Jan 1, 0000", "Jan 2, 0000"} + for i := range xnames { + postHeartbeat(t, + heartbeatBody(xnames[i], "OK", timestamps[i]), + http.StatusOK) + } - req1_hb := bytes.NewBufferString(`{"Component":"x1c2s2b0n3","Hostname":"nid0001.us.cray.com","NID":"0001","Status":"OK","Timestamp":"Jan 1, 0000"}`) - req2_hb := bytes.NewBufferString(`{"Status":"OK","Timestamp":"Jan 2, 1000"}`) + xnames2 := []string{"x1c2s2b0n4", "x1c2s2b0n4v5"} + timestamps2 := []string{"Jan 3, 0000", "Jan 4, 0000"} + for i := range xnames2 { + postHeartbeatToXname(t, + xnames2[i], + heartbeatToXnameBody("OK", timestamps2[i]), + http.StatusOK) + } - // Create 2 fake HTTP POSTs with the HB data in it - req1, err1 := http.NewRequest("POST","http://localhost:8080/hmi/v1/heartbeat",req1_hb) + for i := range xnames { + hb_cmp(t, xnames[i], timestamps[i], "OK") + } + for i := range xnames2 { + hb_cmp(t, xnames2[i], timestamps2[i], "OK") + } - if (err1 != nil) { - t.Fatal(err1) - } + //Now check some error conditions. First, a non-POST request - req2, err2 := http.NewRequest("POST","http://localhost:8080/hmi/v1/heartbeat/x2c3s4b0n5",req2_hb) + req_e1, err_e1 := http.NewRequest("GET", "http://localhost:8080/hmi/v1/heartbeat", nil) - if (err2 != nil) { - t.Fatal(err2) - } + if err_e1 != nil { + t.Fatal(err_e1) + } - // Set up to grab the "responses" - rr := httptest.NewRecorder() - handler := http.HandlerFunc(hbRcv) - handlerXName := http.HandlerFunc(hbRcvXName) + rr_e1 := httptest.NewRecorder() + handler_e1 := http.HandlerFunc(hbRcv) + handler_e1.ServeHTTP(rr_e1, req_e1) - // Mock up the first operation - handler.ServeHTTP(rr,req1) + // Check the return code + if rr_e1.Code != http.StatusMethodNotAllowed { + t.Errorf("HTTP handler returned bad error code, got %v, want %v", + rr_e1.Code, http.StatusMethodNotAllowed) + } - // Check the return code - if (rr.Code != http.StatusOK) { - t.Errorf("HTTP handler returned bad error code, got %v, want %v", - rr.Code,http.StatusOK) - } + //Next we'll give it JSON with an invalid data type + // the Component is an int instead of a string + req_e1_data := bytes.NewBufferString(`{"Component":1234,"Hostname":"nid0001.us.cray.com","NID":"0001","Status":"OK","Timestamp":"Jan 1, 0000"}`) + req_e1, err_e1 = http.NewRequest("POST", "http://localhost:8080/hmi/v1/heartbeat", req_e1_data) - // Mock up the second operation - handlerXName.ServeHTTP(rr,req2) + if err_e1 != nil { + t.Fatal(err_e1) + } + rr_e1 = httptest.NewRecorder() + handler_e1 = http.HandlerFunc(hbRcv) + handler_e1.ServeHTTP(rr_e1, req_e1) - // Check the return code - if (rr.Code != http.StatusOK) { - t.Errorf("HTTP handler returned bad error code, got %v, want %v", - rr.Code,http.StatusOK) - } + // Check the return code + if rr_e1.Code != http.StatusBadRequest { + t.Errorf("HTTP handler returned bad error code, got %v, want %v", + rr_e1.Code, http.StatusBadRequest) + } + + //Send HB with a missing field. - //Now check the hbmap to see if it has entries for both nodes + req_e1_data = bytes.NewBufferString(`{"Hostname":"nid0001.us.cray.com","NID":"0001","Status":"OK","Timestamp":"Jan 1, 0000"}`) + req_e1, err_e1 = http.NewRequest("POST", "http://localhost:8080/hmi/v1/heartbeat", req_e1_data) - hb_cmp(t,"x1c2s2b0n3", "Jan 1, 0000", "OK") - hb_cmp(t,"x2c3s4b0n5", "Jan 2, 1000", "OK") + if err_e1 != nil { + t.Fatal(err_e1) + } + rr_e1 = httptest.NewRecorder() + handler_e1 = http.HandlerFunc(hbRcv) + handler_e1.ServeHTTP(rr_e1, req_e1) - //Now check some error conditions. First, a non-POST request + // Check the return code + if rr_e1.Code != http.StatusBadRequest { + t.Errorf("HTTP handler returned bad error code, got %v, want %v", + rr_e1.Code, http.StatusBadRequest) + } - req_e1, err_e1 := http.NewRequest("GET","http://localhost:8080/hmi/v1/heartbeat",nil) + //Send a HB with an invalid component XName - if (err_e1 != nil) { - t.Fatal(err_e1) - } + postHeartbeat(t, heartbeatBody("xxyyzz", "OK", "Jan 1, 0000"), http.StatusBadRequest) - rr_e1 := httptest.NewRecorder() - handler_e1 := http.HandlerFunc(hbRcv) - handler_e1.ServeHTTP(rr_e1,req_e1) - - // Check the return code - if (rr_e1.Code != http.StatusMethodNotAllowed) { - t.Errorf("HTTP handler returned bad error code, got %v, want %v", - rr_e1.Code,http.StatusMethodNotAllowed) - } - - //Next we'll give it JSON with an invalid data type - - req_e1_data := bytes.NewBufferString(`{"Component":1234,"Hostname":"nid0001.us.cray.com","NID":"0001","Status":"OK","Timestamp":"Jan 1, 0000"}`) - req_e1, err_e1 = http.NewRequest("POST","http://localhost:8080/hmi/v1/heartbeat",req_e1_data) - - if (err_e1 != nil) { - t.Fatal(err_e1) - } - rr_e1 = httptest.NewRecorder() - handler_e1 = http.HandlerFunc(hbRcv) - handler_e1.ServeHTTP(rr_e1,req_e1) - - // Check the return code - if (rr_e1.Code != http.StatusBadRequest) { - t.Errorf("HTTP handler returned bad error code, got %v, want %v", - rr_e1.Code,http.StatusBadRequest) - } - - //Send HB with a missing field. - - req_e1_data = bytes.NewBufferString(`{"Hostname":"nid0001.us.cray.com","NID":"0001","Status":"OK","Timestamp":"Jan 1, 0000"}`) - req_e1, err_e1 = http.NewRequest("POST","http://localhost:8080/hmi/v1/heartbeat",req_e1_data) - - if (err_e1 != nil) { - t.Fatal(err_e1) - } - rr_e1 = httptest.NewRecorder() - handler_e1 = http.HandlerFunc(hbRcv) - handler_e1.ServeHTTP(rr_e1,req_e1) + //Send a HB with a NID that's numerically invalid - // Check the return code - if (rr_e1.Code != http.StatusBadRequest) { - t.Errorf("HTTP handler returned bad error code, got %v, want %v", - rr_e1.Code,http.StatusBadRequest) - } + req_e1_data = bytes.NewBufferString(`{"Component":"x0c0s0b0n0","Hostname":"nid0001.us.cray.com","NID":"123456789123456789123456789123456789","Status":"OK","Timestamp":"Jan 1, 0000"}`) + req_e1, err_e1 = http.NewRequest("POST", "http://localhost:8080/hmi/v1/heartbeat", req_e1_data) - //Send a HB with an invalid component XName + if err_e1 != nil { + t.Fatal(err_e1) + } + rr_e1 = httptest.NewRecorder() + handler_e1 = http.HandlerFunc(hbRcv) + handler_e1.ServeHTTP(rr_e1, req_e1) - req_e1_data = bytes.NewBufferString(`{"Component":"xxyyzz","Hostname":"nid0001.us.cray.com","NID":"0001","Status":"OK","Timestamp":"Jan 1, 0000"}`) - req_e1, err_e1 = http.NewRequest("POST","http://localhost:8080/hmi/v1/heartbeat",req_e1_data) - - if (err_e1 != nil) { - t.Fatal(err_e1) - } - rr_e1 = httptest.NewRecorder() - handler_e1 = http.HandlerFunc(hbRcv) - handler_e1.ServeHTTP(rr_e1,req_e1) - - // Check the return code - if (rr_e1.Code != http.StatusBadRequest) { - t.Errorf("HTTP handler returned bad error code, got %v, want %v", - rr_e1.Code,http.StatusBadRequest) - } - - //Send a HB with a NID that's numerically invalid - - req_e1_data = bytes.NewBufferString(`{"Component":"x0c0s0b0n0","Hostname":"nid0001.us.cray.com","NID":"123456789123456789123456789123456789","Status":"OK","Timestamp":"Jan 1, 0000"}`) - req_e1, err_e1 = http.NewRequest("POST","http://localhost:8080/hmi/v1/heartbeat",req_e1_data) - - if (err_e1 != nil) { - t.Fatal(err_e1) - } - rr_e1 = httptest.NewRecorder() - handler_e1 = http.HandlerFunc(hbRcv) - handler_e1.ServeHTTP(rr_e1,req_e1) - - // Check the return code - if (rr_e1.Code != http.StatusBadRequest) { - t.Errorf("HTTP handler returned bad error code, got %v, want %v", - rr_e1.Code,http.StatusBadRequest) - } - - //Send a HB to an existing key - - req_e1_data = bytes.NewBufferString(`{"Component":"x1c2s2b0n3","Hostname":"nid0001.us.cray.com","NID":"0001","Status":"OK","Timestamp":"Jan 1, 0000"}`) - req_e1, err_e1 = http.NewRequest("POST","http://localhost:8080/hmi/v1/heartbeat",req_e1_data) - - if (err_e1 != nil) { - t.Fatal(err_e1) - } - rr_e1 = httptest.NewRecorder() - handler_e1 = http.HandlerFunc(hbRcv) - handler_e1.ServeHTTP(rr_e1,req_e1) - - // Check the return code - if (rr_e1.Code != http.StatusOK) { - t.Errorf("HTTP handler returned bad error code, got %v, want %v", - rr_e1.Code,http.StatusOK) - } - - time.Sleep(1 * time.Second) - t.Logf(" ==> FINISHED HEARTBEAT HTTP OPERATIONS TEST\n") + // Check the return code + if rr_e1.Code != http.StatusBadRequest { + t.Errorf("HTTP handler returned bad error code, got %v, want %v", + rr_e1.Code, http.StatusBadRequest) + } + + //Send a HB to an existing key + + postHeartbeat(t, heartbeatBody(xnames[0], "OK", "Jan 1, 0000"), http.StatusOK) + + time.Sleep(1 * time.Second) + t.Logf(" ==> FINISHED HEARTBEAT HTTP OPERATIONS TEST\n") } // Test entry point for params_io(), which is the params HTTP request handler. // We will fake out an HTTP request object and record the response. // We will also examine the current parameters to see if the PATCH operations // work. -// +// // t(in) Test framework. // Return: None. func TestParams_io(t *testing.T) { - t.Logf("** RUNNING PARAMETER HTTP OPERATIONS TEST\n") - - //Set parameters to initial values - - app_params.debug_level.int_param = 0 - app_params.warntime.int_param = 1 - app_params.errtime.int_param = 2 - - //Slop together JSON payloads for 2 heartbeating nodes - - req1_hb := bytes.NewBufferString(`{"debug":"3","warntime":"5","errtime":"10"}`) - - // Create a fake HTTP PATCH with the param data in it - req1, err1 := http.NewRequest("PATCH","http://localhost:8080/hmi/v1/params",req1_hb) - - if (err1 != nil) { - t.Fatal(err1) - } - - // Set up to grab the "responses" - rr := httptest.NewRecorder() - handler := http.HandlerFunc(paramsIO) - - // Mock up the operation - handler.ServeHTTP(rr,req1) - - // Check the return code - if (rr.Code != http.StatusOK) { - t.Errorf("HTTP handler returned bad error code, bot %v, want %v", - rr.Code,http.StatusOK) - } - - //Now check the current parameters to make sure they match - - if (app_params.debug_level.int_param != 3) { - t.Errorf("PATCH test failed: debug level is incorrect (exp: %d, got %d)", - 3,app_params.debug_level.int_param) - } - if (app_params.warntime.int_param != 5) { - t.Errorf("PATCH test failed: warntime is incorrect (exp: %d, got %d)", - 5,app_params.warntime.int_param) - } - if (app_params.errtime.int_param != 10) { - t.Errorf("PATCH test failed: errtime is incorrect (exp: %d, got %d)", - 10,app_params.errtime.int_param) - } - - //Now do a GET operation and insure we get the same thing. - - req2, err2 := http.NewRequest("GET","http://localhost:8080/hmi/v1/params",nil) - - if (err2 != nil) { - t.Fatal(err2) - } - - // Set up to grab the "responses" - rr2 := httptest.NewRecorder() - handler2 := http.HandlerFunc(paramsIO) - - // Mock up the operation - handler2.ServeHTTP(rr2,req2) - - // Check the return code - if (rr2.Code != http.StatusOK) { - t.Errorf("HTTP handler returned bad error code, bot %v, want %v", - rr2.Code,http.StatusOK) - } - - // Read the response payload - var pdata inidata - body,err := ioutil.ReadAll(rr2.Body) - if (err != nil) { - t.Error("Error reading GET response data:",err) - } else { - errj := json.Unmarshal(body,&pdata) - if (errj != nil) { - t.Error("Error unmarshalling GET response data:",errj) - } else { - // Do the compares - if (pdata.Debug != "3") { - t.Errorf("GET test failed: debug level is incorrect (exp: %d, got %s)", - 3,pdata.Debug) - } - if (pdata.Warntime != "5") { - t.Errorf("GET test failed: warntime is incorrect (exp: %d, got %s)", - 5,pdata.Warntime) - } - if (pdata.Errtime != "10") { - t.Errorf("GET test failed: errtime is incorrect (exp: %d, got %s)", - 10,pdata.Errtime) - } - } - } - - //Now check some error conditions. First, an invalid request (POST) - - req_e1_data := bytes.NewBufferString(`{"Debug":"3"}`) - req_e1, err_e1 := http.NewRequest("POST","http://localhost:8080/hmi/v1/params",nil) - - if (err_e1 != nil) { - t.Fatal(err_e1) - } - rr_e1 := httptest.NewRecorder() - handler_e1 := http.HandlerFunc(paramsIO) - handler_e1.ServeHTTP(rr_e1,req_e1) - - // Check the return code - if (rr_e1.Code != http.StatusMethodNotAllowed) { - t.Errorf("HTTP handler returned bad error code, got %v, want %v", - rr_e1.Code,http.StatusMethodNotAllowed) - } - - //Check PATCH with bad data - - req_e1_data = bytes.NewBufferString(`{"Debug":3}`) - req_e1, err_e1 = http.NewRequest("PATCH","http://localhost:8080/hmi/v1/params",req_e1_data) - - if (err_e1 != nil) { - t.Fatal(err_e1) - } - rr_e1 = httptest.NewRecorder() - handler_e1 = http.HandlerFunc(paramsIO) - handler_e1.ServeHTTP(rr_e1,req_e1) - - // Check the return code - if (rr_e1.Code != http.StatusBadRequest) { - t.Errorf("HTTP handler returned bad error code, got %v, want %v", - rr_e1.Code,http.StatusBadRequest) - } - - t.Logf(" ==> FINISHED PARAMETER HTTP OPERATIONS TEST\n") + t.Logf("** RUNNING PARAMETER HTTP OPERATIONS TEST\n") + + //Set parameters to initial values + + app_params.debug_level.int_param = 0 + app_params.warntime.int_param = 1 + app_params.errtime.int_param = 2 + + //Slop together JSON payloads for 2 heartbeating nodes + + req1_hb := bytes.NewBufferString(`{"debug":"3","warntime":"5","errtime":"10"}`) + + // Create a fake HTTP PATCH with the param data in it + req1, err1 := http.NewRequest("PATCH", "http://localhost:8080/hmi/v1/params", req1_hb) + + if err1 != nil { + t.Fatal(err1) + } + + // Set up to grab the "responses" + rr := httptest.NewRecorder() + handler := http.HandlerFunc(paramsIO) + + // Mock up the operation + handler.ServeHTTP(rr, req1) + + // Check the return code + if rr.Code != http.StatusOK { + t.Errorf("HTTP handler returned bad error code, bot %v, want %v", + rr.Code, http.StatusOK) + } + + //Now check the current parameters to make sure they match + + if app_params.debug_level.int_param != 3 { + t.Errorf("PATCH test failed: debug level is incorrect (exp: %d, got %d)", + 3, app_params.debug_level.int_param) + } + if app_params.warntime.int_param != 5 { + t.Errorf("PATCH test failed: warntime is incorrect (exp: %d, got %d)", + 5, app_params.warntime.int_param) + } + if app_params.errtime.int_param != 10 { + t.Errorf("PATCH test failed: errtime is incorrect (exp: %d, got %d)", + 10, app_params.errtime.int_param) + } + + //Now do a GET operation and insure we get the same thing. + + req2, err2 := http.NewRequest("GET", "http://localhost:8080/hmi/v1/params", nil) + + if err2 != nil { + t.Fatal(err2) + } + + // Set up to grab the "responses" + rr2 := httptest.NewRecorder() + handler2 := http.HandlerFunc(paramsIO) + + // Mock up the operation + handler2.ServeHTTP(rr2, req2) + + // Check the return code + if rr2.Code != http.StatusOK { + t.Errorf("HTTP handler returned bad error code, bot %v, want %v", + rr2.Code, http.StatusOK) + } + + // Read the response payload + var pdata inidata + body, err := ioutil.ReadAll(rr2.Body) + if err != nil { + t.Error("Error reading GET response data:", err) + } else { + errj := json.Unmarshal(body, &pdata) + if errj != nil { + t.Error("Error unmarshalling GET response data:", errj) + } else { + // Do the compares + if pdata.Debug != "3" { + t.Errorf("GET test failed: debug level is incorrect (exp: %d, got %s)", + 3, pdata.Debug) + } + if pdata.Warntime != "5" { + t.Errorf("GET test failed: warntime is incorrect (exp: %d, got %s)", + 5, pdata.Warntime) + } + if pdata.Errtime != "10" { + t.Errorf("GET test failed: errtime is incorrect (exp: %d, got %s)", + 10, pdata.Errtime) + } + } + } + + //Now check some error conditions. First, an invalid request (POST) + + req_e1_data := bytes.NewBufferString(`{"Debug":"3"}`) + req_e1, err_e1 := http.NewRequest("POST", "http://localhost:8080/hmi/v1/params", nil) + + if err_e1 != nil { + t.Fatal(err_e1) + } + rr_e1 := httptest.NewRecorder() + handler_e1 := http.HandlerFunc(paramsIO) + handler_e1.ServeHTTP(rr_e1, req_e1) + + // Check the return code + if rr_e1.Code != http.StatusMethodNotAllowed { + t.Errorf("HTTP handler returned bad error code, got %v, want %v", + rr_e1.Code, http.StatusMethodNotAllowed) + } + + //Check PATCH with bad data + + req_e1_data = bytes.NewBufferString(`{"Debug":3}`) + req_e1, err_e1 = http.NewRequest("PATCH", "http://localhost:8080/hmi/v1/params", req_e1_data) + + if err_e1 != nil { + t.Fatal(err_e1) + } + rr_e1 = httptest.NewRecorder() + handler_e1 = http.HandlerFunc(paramsIO) + handler_e1.ServeHTTP(rr_e1, req_e1) + + // Check the return code + if rr_e1.Code != http.StatusBadRequest { + t.Errorf("HTTP handler returned bad error code, got %v, want %v", + rr_e1.Code, http.StatusBadRequest) + } + + t.Logf(" ==> FINISHED PARAMETER HTTP OPERATIONS TEST\n") } func setSMRVal(val int) { - SMRMutex.Lock() - SMRval = val - SMRMutex.Unlock() + SMRMutex.Lock() + SMRval = val + SMRMutex.Unlock() } func getSMRVal() int { - var val int - SMRMutex.Lock() - val = SMRval - SMRMutex.Unlock() - return val + var val int + SMRMutex.Lock() + val = SMRval + SMRMutex.Unlock() + return val } var gotUAHdr bool func hasUserAgentHeader(r *http.Request) bool { - if (len(r.Header) == 0) { - return false - } - - _,ok := r.Header["User-Agent"] - if (!ok) { - return false - } - return true + if len(r.Header) == 0 { + return false + } + + _, ok := r.Header["User-Agent"] + if !ok { + return false + } + return true } func fakeHSMHandler(w http.ResponseWriter, req *http.Request) { - gotUAHdr = hasUserAgentHeader(req) - w.WriteHeader(getSMRVal()) + gotUAHdr = hasUserAgentHeader(req) + w.WriteHeader(getSMRVal()) } -var startComps,restartComps,stopWarnComps,stopErrorComps []string +var startComps, restartComps, stopWarnComps, stopErrorComps []string var compLock sync.Mutex func fakeHSMPatchHandler(w http.ResponseWriter, req *http.Request) { var sinfo smjbulk_v1 - if (getSMRVal() != http.StatusOK) { + if getSMRVal() != http.StatusOK { w.WriteHeader(getSMRVal()) return } - body,err := ioutil.ReadAll(req.Body) - if (err != nil) { + body, err := ioutil.ReadAll(req.Body) + if err != nil { w.WriteHeader(http.StatusInternalServerError) return } - err = json.Unmarshal(body,&sinfo) - if (err != nil) { + err = json.Unmarshal(body, &sinfo) + if err != nil { w.WriteHeader(http.StatusBadRequest) return } compLock.Lock() - if ((sinfo.State == base.StateReady.String()) && (sinfo.Flag == base.FlagOK.String())) { - if (strings.Contains(sinfo.ExtendedInfo.Message,"beat restarted")) { - restartComps = append(restartComps,sinfo.ComponentIDs...) + if (sinfo.State == base.StateReady.String()) && (sinfo.Flag == base.FlagOK.String()) { + if strings.Contains(sinfo.ExtendedInfo.Message, "beat restarted") { + restartComps = append(restartComps, sinfo.ComponentIDs...) } else { - startComps = append(startComps,sinfo.ComponentIDs...) + startComps = append(startComps, sinfo.ComponentIDs...) } - } else if ((sinfo.State == base.StateReady.String()) && (sinfo.Flag == base.FlagWarning.String())) { - stopWarnComps = append(stopWarnComps,sinfo.ComponentIDs...) - } else if ((sinfo.State == base.StateStandby.String()) && (sinfo.Flag == base.FlagAlert.String())) { - stopErrorComps = append(stopErrorComps,sinfo.ComponentIDs...) + } else if (sinfo.State == base.StateReady.String()) && (sinfo.Flag == base.FlagWarning.String()) { + stopWarnComps = append(stopWarnComps, sinfo.ComponentIDs...) + } else if (sinfo.State == base.StateStandby.String()) && (sinfo.Flag == base.FlagAlert.String()) { + stopErrorComps = append(stopErrorComps, sinfo.ComponentIDs...) } compLock.Unlock() @@ -919,7 +919,7 @@ func TestSMPatch1(t *testing.T) { time.Second), } smjinfo := smjbulk_v1{ComponentIDs: []string{"x0c0s0b0n0"}, State: "Ready", Flag: "OK", - ExtendedInfo: smjson_einfo{Message: "Test",},} + ExtendedInfo: smjson_einfo{Message: "Test"}} srv := httptest.NewServer(http.HandlerFunc(fakeHSMHandler)) serviceName = "HBTDTest" @@ -938,10 +938,10 @@ func TestSMPatch1(t *testing.T) { smjinfo.sentOK = false go send_sm_patch(&smjinfo) hsmWG.Wait() - if (!smjinfo.sentOK) { + if !smjinfo.sentOK { t.Errorf("ERROR: send_sm_patch() didn't set sentOK flag.") } - if (!gotUAHdr) { + if !gotUAHdr { t.Errorf("ERROR, never saw User-Agent header.") } @@ -953,105 +953,105 @@ func TestSMPatch1(t *testing.T) { smjinfo.sentOK = false go send_sm_patch(&smjinfo) hsmWG.Wait() - if (smjinfo.sentOK) { + if smjinfo.sentOK { t.Errorf("ERROR: send_sm_patch() incorrectly set sentOK flag.") } } func Test_HbStates(t *testing.T) { - var kval,xname string + var kval, xname string var jdata hbStatesReq var rdata hbStatesRsp var err error - t.Logf("** RUNNING hbStates TEST **") + t.Logf("** RUNNING hbStates TEST **") - ots_err := one_time_setup() - if (ots_err != nil) { - t.Error("ERROR setting up KV store:",ots_err) - return - } - staleKeys = false - app_params.check_interval.int_param = 30 - app_params.warntime.int_param = 5 - app_params.errtime.int_param = 20 + ots_err := one_time_setup() + if ots_err != nil { + t.Error("ERROR setting up KV store:", ots_err) + return + } + staleKeys = false + app_params.check_interval.int_param = 30 + app_params.warntime.int_param = 5 + app_params.errtime.int_param = 20 //Make HB entries in KV store. Need 3 -- one that's up to date, one that's //in warn state, one in err state. Will query 4 nodes -- the above 3, //plus one that is not in the KV store. basetime := time.Now().Unix() - xnameArr := []string{"x0c1s2b0n0","x0c1s2b1n1","x0c1s2b2n2","x0c1s2b3n3",} - times := []int64{0,10,30,0,} - - for ix := 0; ix < 3; ix ++ { - make_key(&kval,xnameArr[ix],(basetime-times[ix])) - err = kvHandle.Store(xnameArr[ix],kval) - if (err != nil) { - t.Errorf("ERROR storing key data for '%s': %v",xnameArr[ix],err) + xnameArr := []string{"x0c1s2b0n0", "x0c1s2b1n1", "x0c1s2b2n2", "x0c1s2b3n3"} + times := []int64{0, 10, 30, 0} + + for ix := 0; ix < 3; ix++ { + make_key(&kval, xnameArr[ix], (basetime - times[ix])) + err = kvHandle.Store(xnameArr[ix], kval) + if err != nil { + t.Errorf("ERROR storing key data for '%s': %v", xnameArr[ix], err) } } //Do query for 4 nodes jdata.XNames = xnameArr - ba,baerr := json.Marshal(&jdata) - if (baerr != nil) { - t.Error("ERROR marshalling POST data:",baerr) + ba, baerr := json.Marshal(&jdata) + if baerr != nil { + t.Error("ERROR marshalling POST data:", baerr) } rr := httptest.NewRecorder() handler := http.HandlerFunc(hbStates) - req,_ := http.NewRequest("POST","http://localhost:8080/hmi/v1/hbstates",bytes.NewBuffer(ba)) + req, _ := http.NewRequest("POST", "http://localhost:8080/hmi/v1/hbstates", bytes.NewBuffer(ba)) - handler.ServeHTTP(rr,req) + handler.ServeHTTP(rr, req) - if (rr.Code != http.StatusOK) { - t.Errorf("HTTP handler returned bad status code: %v",rr.Code) + if rr.Code != http.StatusOK { + t.Errorf("HTTP handler returned bad status code: %v", rr.Code) } //Check results. - body,bodyErr := ioutil.ReadAll(rr.Body) - if (bodyErr != nil) { - t.Errorf("ERROR reading hbstates return body: %v",bodyErr) + body, bodyErr := ioutil.ReadAll(rr.Body) + if bodyErr != nil { + t.Errorf("ERROR reading hbstates return body: %v", bodyErr) } - baerr = json.Unmarshal(body,&rdata) - if (baerr != nil) { - t.Errorf("ERROR umnarshalling hbstates return body: %v",baerr) + baerr = json.Unmarshal(body, &rdata) + if baerr != nil { + t.Errorf("ERROR umnarshalling hbstates return body: %v", baerr) } - if (len(rdata.HBStates) != 4) { - t.Errorf("Invalid HB states length, expected 4, got %d",len(rdata.HBStates)) + if len(rdata.HBStates) != 4 { + t.Errorf("Invalid HB states length, expected 4, got %d", len(rdata.HBStates)) } //Check results smap := make(map[string]*hbSingleStateRsp) - for ix := 0; ix < len(rdata.HBStates); ix ++ { + for ix := 0; ix < len(rdata.HBStates); ix++ { smap[rdata.HBStates[ix].XName] = &rdata.HBStates[ix] } - xname = xnameArr[0] //should be valid and heartbeating - if (smap[xname].Heartbeating == false) { - t.Errorf("ERROR, HB state for '%s' is not heartbeating, should be.",xname) + xname = xnameArr[0] //should be valid and heartbeating + if smap[xname].Heartbeating == false { + t.Errorf("ERROR, HB state for '%s' is not heartbeating, should be.", xname) } - xname = xnameArr[1] //should be valid and heartbeating - if (smap[xname].Heartbeating == false) { - t.Errorf("ERROR, HB state for '%s' is not heartbeating, should be.",xname) + xname = xnameArr[1] //should be valid and heartbeating + if smap[xname].Heartbeating == false { + t.Errorf("ERROR, HB state for '%s' is not heartbeating, should be.", xname) } - xname = xnameArr[2] //should be valid and not heartbeating - if (smap[xname].Heartbeating == true) { - t.Errorf("ERROR, HB state for '%s' is heartbeating, should not be.",xname) + xname = xnameArr[2] //should be valid and not heartbeating + if smap[xname].Heartbeating == true { + t.Errorf("ERROR, HB state for '%s' is heartbeating, should not be.", xname) } - xname = xnameArr[3] //should be not valid or heartbeating - if (smap[xname].Heartbeating == true) { - t.Errorf("ERROR, HB state for '%s' is heartbeating, should not be.",xname) + xname = xnameArr[3] //should be not valid or heartbeating + if smap[xname].Heartbeating == true { + t.Errorf("ERROR, HB state for '%s' is heartbeating, should not be.", xname) } } @@ -1060,31 +1060,31 @@ func Test_HbStateSingle(t *testing.T) { var rdata hbSingleStateRsp var err error - t.Logf("** RUNNING hbStates TEST **") + t.Logf("** RUNNING hbStates TEST **") - ots_err := one_time_setup() - if (ots_err != nil) { - t.Error("ERROR setting up KV store:",ots_err) - return - } - staleKeys = false - app_params.check_interval.int_param = 30 - app_params.warntime.int_param = 5 - app_params.errtime.int_param = 20 + ots_err := one_time_setup() + if ots_err != nil { + t.Error("ERROR setting up KV store:", ots_err) + return + } + staleKeys = false + app_params.check_interval.int_param = 30 + app_params.warntime.int_param = 5 + app_params.errtime.int_param = 20 //Make HB entries in KV store. Need 3 -- one that's up to date, one that's //in warn state, one in err state. Will query 4 nodes -- the above 3, //plus one that is not in the KV store. basetime := time.Now().Unix() - xnameArr := []string{"x1c1s2b0n0","x1c1s2b1n1","x1c1s2b2n2","x1c1s2b3n3",} - times := []int64{0,10,30,0,} - - for ix := 0; ix < 3; ix ++ { - make_key(&kval,xnameArr[ix],(basetime-times[ix])) - err = kvHandle.Store(xnameArr[ix],kval) - if (err != nil) { - t.Errorf("ERROR storing key data for '%s': %v",xnameArr[ix],err) + xnameArr := []string{"x1c1s2b0n0", "x1c1s2b1n1", "x1c1s2b2n2", "x1c1s2b3n3"} + times := []int64{0, 10, 30, 0} + + for ix := 0; ix < 3; ix++ { + make_key(&kval, xnameArr[ix], (basetime - times[ix])) + err = kvHandle.Store(xnameArr[ix], kval) + if err != nil { + t.Errorf("ERROR storing key data for '%s': %v", xnameArr[ix], err) } } @@ -1097,33 +1097,33 @@ func Test_HbStateSingle(t *testing.T) { //First: valid and heartbeating rr := httptest.NewRecorder() - url := fmt.Sprintf("http://localhost/hmi/v1/hbstate/%s",xnameArr[0]) - req,_ := http.NewRequest("GET",url,nil) + url := fmt.Sprintf("http://localhost/hmi/v1/hbstate/%s", xnameArr[0]) + req, _ := http.NewRequest("GET", url, nil) - router.ServeHTTP(rr,req) + router.ServeHTTP(rr, req) - if (rr.Code != http.StatusOK) { - t.Errorf("HTTP handler returned bad status code: %v",rr.Code) + if rr.Code != http.StatusOK { + t.Errorf("HTTP handler returned bad status code: %v", rr.Code) } //Check results. - body,bodyErr := ioutil.ReadAll(rr.Body) - if (bodyErr != nil) { - t.Errorf("ERROR reading hbstates return body: %v",bodyErr) + body, bodyErr := ioutil.ReadAll(rr.Body) + if bodyErr != nil { + t.Errorf("ERROR reading hbstates return body: %v", bodyErr) } - err = json.Unmarshal(body,&rdata) - if (err != nil) { - t.Errorf("ERROR umnarshalling hbstates return body: %v",err) + err = json.Unmarshal(body, &rdata) + if err != nil { + t.Errorf("ERROR umnarshalling hbstates return body: %v", err) } //Should be valid and heartbeating - if (rdata.XName != xnameArr[0]) { + if rdata.XName != xnameArr[0] { t.Errorf("ERROR, HB state for '%s' has invalid xname: '%s'", - xnameArr[0],rdata.XName) + xnameArr[0], rdata.XName) } - if (rdata.Heartbeating == false) { + if rdata.Heartbeating == false { t.Errorf("ERROR, HB state for '%s' is not heartbeating, should be.", rdata.XName) } @@ -1131,33 +1131,33 @@ func Test_HbStateSingle(t *testing.T) { //Second: Should be valid and heartbeating rr = httptest.NewRecorder() - url = fmt.Sprintf("http://localhost/hmi/v1/hbstate/%s",xnameArr[1]) - req,_ = http.NewRequest("GET",url,nil) + url = fmt.Sprintf("http://localhost/hmi/v1/hbstate/%s", xnameArr[1]) + req, _ = http.NewRequest("GET", url, nil) - router.ServeHTTP(rr,req) + router.ServeHTTP(rr, req) - if (rr.Code != http.StatusOK) { - t.Errorf("HTTP handler returned bad status code: %v",rr.Code) + if rr.Code != http.StatusOK { + t.Errorf("HTTP handler returned bad status code: %v", rr.Code) } //Check results. - body,bodyErr = ioutil.ReadAll(rr.Body) - if (bodyErr != nil) { - t.Errorf("ERROR reading hbstates return body: %v",bodyErr) + body, bodyErr = ioutil.ReadAll(rr.Body) + if bodyErr != nil { + t.Errorf("ERROR reading hbstates return body: %v", bodyErr) } - err = json.Unmarshal(body,&rdata) - if (err != nil) { - t.Errorf("ERROR umnarshalling hbstates return body: %v",err) + err = json.Unmarshal(body, &rdata) + if err != nil { + t.Errorf("ERROR umnarshalling hbstates return body: %v", err) } //Should be valid and heartbeating - if (rdata.XName != xnameArr[1]) { + if rdata.XName != xnameArr[1] { t.Errorf("ERROR, HB state for '%s' has invalid xname: '%s'", - xnameArr[1],rdata.XName) + xnameArr[1], rdata.XName) } - if (rdata.Heartbeating == false) { + if rdata.Heartbeating == false { t.Errorf("ERROR, HB state for '%s' is not heartbeating, should be.", rdata.XName) } @@ -1165,33 +1165,33 @@ func Test_HbStateSingle(t *testing.T) { //Third: should be valid, not heartbeating rr = httptest.NewRecorder() - url = fmt.Sprintf("http://localhost/hmi/v1/hbstate/%s",xnameArr[2]) - req,_ = http.NewRequest("GET",url,nil) + url = fmt.Sprintf("http://localhost/hmi/v1/hbstate/%s", xnameArr[2]) + req, _ = http.NewRequest("GET", url, nil) - router.ServeHTTP(rr,req) + router.ServeHTTP(rr, req) - if (rr.Code != http.StatusOK) { - t.Errorf("HTTP handler returned bad status code: %v",rr.Code) + if rr.Code != http.StatusOK { + t.Errorf("HTTP handler returned bad status code: %v", rr.Code) } //Check results. - body,bodyErr = ioutil.ReadAll(rr.Body) - if (bodyErr != nil) { - t.Errorf("ERROR reading hbstates return body: %v",bodyErr) + body, bodyErr = ioutil.ReadAll(rr.Body) + if bodyErr != nil { + t.Errorf("ERROR reading hbstates return body: %v", bodyErr) } - err = json.Unmarshal(body,&rdata) - if (err != nil) { - t.Errorf("ERROR umnarshalling hbstates return body: %v",err) + err = json.Unmarshal(body, &rdata) + if err != nil { + t.Errorf("ERROR umnarshalling hbstates return body: %v", err) } //Should be valid and NOT heartbeating - if (rdata.XName != xnameArr[2]) { + if rdata.XName != xnameArr[2] { t.Errorf("ERROR, HB state for '%s' has invalid xname: '%s'", - xnameArr[2],rdata.XName) + xnameArr[2], rdata.XName) } - if (rdata.Heartbeating == true) { + if rdata.Heartbeating == true { t.Errorf("ERROR, HB state for '%s' is heartbeating, should not be.", rdata.XName) } @@ -1199,33 +1199,33 @@ func Test_HbStateSingle(t *testing.T) { //Fourth: not valid rr = httptest.NewRecorder() - url = fmt.Sprintf("http://localhost/hmi/v1/hbstate/%s",xnameArr[3]) - req,_ = http.NewRequest("GET",url,nil) + url = fmt.Sprintf("http://localhost/hmi/v1/hbstate/%s", xnameArr[3]) + req, _ = http.NewRequest("GET", url, nil) - router.ServeHTTP(rr,req) + router.ServeHTTP(rr, req) - if (rr.Code != http.StatusOK) { - t.Errorf("HTTP handler returned bad status code: %v",rr.Code) + if rr.Code != http.StatusOK { + t.Errorf("HTTP handler returned bad status code: %v", rr.Code) } //Check results. - body,bodyErr = ioutil.ReadAll(rr.Body) - if (bodyErr != nil) { - t.Errorf("ERROR reading hbstates return body: %v",bodyErr) + body, bodyErr = ioutil.ReadAll(rr.Body) + if bodyErr != nil { + t.Errorf("ERROR reading hbstates return body: %v", bodyErr) } - err = json.Unmarshal(body,&rdata) - if (err != nil) { - t.Errorf("ERROR umnarshalling hbstates return body: %v",err) + err = json.Unmarshal(body, &rdata) + if err != nil { + t.Errorf("ERROR umnarshalling hbstates return body: %v", err) } //Should be invalid, not heartbeating - if (rdata.XName != xnameArr[3]) { + if rdata.XName != xnameArr[3] { t.Errorf("ERROR, HB state for '%s' has invalid xname: '%s'", - xnameArr[3],rdata.XName) + xnameArr[3], rdata.XName) } - if (rdata.Heartbeating == true) { + if rdata.Heartbeating == true { t.Errorf("ERROR, HB state for '%s' is heartbeating, should not be.", rdata.XName) } @@ -1233,13 +1233,13 @@ func Test_HbStateSingle(t *testing.T) { func TestHBMapping(t *testing.T) { hbi1 := hbinfo{Component: "x0c0s0b0n1", Last_hb_rcv_time: "00000001", - Last_hb_timestamp: "00000001", Last_hb_status: "OK",} + Last_hb_timestamp: "00000001", Last_hb_status: "OK"} hbi2 := hbinfo{Component: "x0c0s0b0n2", Last_hb_rcv_time: "00000002", - Last_hb_timestamp: "00000002", Last_hb_status: "OK",} + Last_hb_timestamp: "00000002", Last_hb_status: "OK"} hbi3 := hbinfo{Component: "x0c0s0b0n3", Last_hb_rcv_time: "00000003", - Last_hb_timestamp: "00000003", Last_hb_status: "OK",} + Last_hb_timestamp: "00000003", Last_hb_status: "OK"} hbi4 := hbinfo{Component: "x0c0s0b0n4", Last_hb_rcv_time: "00000004", - Last_hb_timestamp: "00000004", Last_hb_status: "OK",} + Last_hb_timestamp: "00000004", Last_hb_status: "OK"} kill_sm_goroutines() go send_sm_req() @@ -1265,16 +1265,16 @@ func TestHBMapping(t *testing.T) { restartComps = []string{} stopWarnComps = []string{} stopErrorComps = []string{} - for k,_ := range(StartMap) { + for k, _ := range StartMap { StartMap[k] = 0 } - for k,_ := range(RestartMap) { + for k, _ := range RestartMap { RestartMap[k] = 0 } - for k,_ := range(StopWarnMap) { + for k, _ := range StopWarnMap { StopWarnMap[k] = 0 } - for k,_ := range(StopErrorMap) { + for k, _ := range StopErrorMap { StopErrorMap[k] = 0 } compLock.Unlock() @@ -1285,113 +1285,113 @@ func TestHBMapping(t *testing.T) { testMode = true - hb_update_notify(&hbi1,HB_started) - hb_update_notify(&hbi2,HB_restarted_warn) - hb_update_notify(&hbi3,HB_stopped_warn) - hb_update_notify(&hbi4,HB_stopped_error) + hb_update_notify(&hbi1, HB_started) + hb_update_notify(&hbi2, HB_restarted_warn) + hb_update_notify(&hbi3, HB_stopped_warn) + hb_update_notify(&hbi4, HB_stopped_error) //Conflicts - hb_update_notify(&hbi1,HB_restarted_warn) - hb_update_notify(&hbi2,HB_stopped_warn) - hb_update_notify(&hbi3,HB_stopped_error) - hb_update_notify(&hbi4,HB_started) + hb_update_notify(&hbi1, HB_restarted_warn) + hb_update_notify(&hbi2, HB_stopped_warn) + hb_update_notify(&hbi3, HB_stopped_error) + hb_update_notify(&hbi4, HB_started) setSMRVal(http.StatusOK) - hsmUpdateQ <-HSMQ_NEW + hsmUpdateQ <- HSMQ_NEW time.Sleep(2 * time.Second) //Check if we got the results we wanted. - if (len(startComps) == 0) { + if len(startComps) == 0 { t.Fatalf("No 'start' components found.") } - if (len(restartComps) == 0) { + if len(restartComps) == 0 { t.Fatalf("No 'restart' components found.") } - if (len(stopWarnComps) == 0) { + if len(stopWarnComps) == 0 { t.Fatalf("No 'stop-warn' components found.") } - if (len(stopErrorComps) == 0) { + if len(stopErrorComps) == 0 { t.Fatalf("No 'stop-error' components found.") } - if (len(startComps) > 1) { - t.Errorf("Too many 'start' components (%d), expected 1.",len(startComps)) + if len(startComps) > 1 { + t.Errorf("Too many 'start' components (%d), expected 1.", len(startComps)) } - if (len(restartComps) > 1) { - t.Errorf("Too many 'restart' components (%d), expected 1.",len(restartComps)) + if len(restartComps) > 1 { + t.Errorf("Too many 'restart' components (%d), expected 1.", len(restartComps)) } - if (len(stopWarnComps) > 1) { - t.Errorf("Too many 'stopWarnComps' components (%d), expected 1.",len(stopWarnComps)) + if len(stopWarnComps) > 1 { + t.Errorf("Too many 'stopWarnComps' components (%d), expected 1.", len(stopWarnComps)) } - if (len(stopErrorComps) > 1) { - t.Errorf("Too many 'stopErrorComps' components (%d), expected 1.",len(stopErrorComps)) + if len(stopErrorComps) > 1 { + t.Errorf("Too many 'stopErrorComps' components (%d), expected 1.", len(stopErrorComps)) } - if (startComps[0] != hbi4.Component) { - t.Errorf("HB start not found on '%s'",hbi4.Component) + if startComps[0] != hbi4.Component { + t.Errorf("HB start not found on '%s'", hbi4.Component) } - if (restartComps[0] != hbi1.Component) { - t.Errorf("HB restart not found on '%s'",hbi1.Component) + if restartComps[0] != hbi1.Component { + t.Errorf("HB restart not found on '%s'", hbi1.Component) } - if (stopWarnComps[0] != hbi2.Component) { - t.Errorf("HB stop-warning not found on '%s'",hbi2.Component) + if stopWarnComps[0] != hbi2.Component { + t.Errorf("HB stop-warning not found on '%s'", hbi2.Component) } - if (stopErrorComps[0] != hbi3.Component) { - t.Errorf("HB stop-error not found on '%s'",hbi3.Component) + if stopErrorComps[0] != hbi3.Component { + t.Errorf("HB stop-error not found on '%s'", hbi3.Component) } //Make sure global maps are cleared nitems := 0 - for _,v := range(StartMap) { - if (v != 0) { - nitems ++ + for _, v := range StartMap { + if v != 0 { + nitems++ } } - if (nitems > 0) { + if nitems > 0 { t.Errorf("Global Start Map is not cleared.") } nitems = 0 - for _,v := range(RestartMap) { - if (v != 0) { - nitems ++ + for _, v := range RestartMap { + if v != 0 { + nitems++ } } - if (nitems > 0) { + if nitems > 0 { t.Errorf("Global Restart Map is not cleared.") } nitems = 0 - for _,v := range(StopWarnMap) { - if (v != 0) { - nitems ++ + for _, v := range StopWarnMap { + if v != 0 { + nitems++ } } - if (nitems > 0) { + if nitems > 0 { t.Errorf("Global Stop Warning Map is not cleared.") } nitems = 0 - for _,v := range(StopErrorMap) { - if (v != 0) { - nitems ++ + for _, v := range StopErrorMap { + if v != 0 { + nitems++ } } - if (nitems > 0) { + if nitems > 0 { t.Errorf("Global Stop Error Map is not cleared.") } } func TestHBMapping2(t *testing.T) { hbi1 := hbinfo{Component: "x0c0s0b0n0", Last_hb_rcv_time: "00000000", - Last_hb_timestamp: "00000000", Last_hb_status: "OK",} + Last_hb_timestamp: "00000000", Last_hb_status: "OK"} hbi2 := hbinfo{Component: "x0c0s0b0n1", Last_hb_rcv_time: "00000001", - Last_hb_timestamp: "00000001", Last_hb_status: "OK",} + Last_hb_timestamp: "00000001", Last_hb_status: "OK"} hbi3 := hbinfo{Component: "x0c0s0b0n2", Last_hb_rcv_time: "00000002", - Last_hb_timestamp: "00000002", Last_hb_status: "OK",} + Last_hb_timestamp: "00000002", Last_hb_status: "OK"} hbi4 := hbinfo{Component: "x0c0s0b0n3", Last_hb_rcv_time: "00000003", - Last_hb_timestamp: "00000003", Last_hb_status: "OK",} + Last_hb_timestamp: "00000003", Last_hb_status: "OK"} hbi5 := hbinfo{Component: "x0c0s0b0n4", Last_hb_rcv_time: "00000004", - Last_hb_timestamp: "00000004", Last_hb_status: "OK",} + Last_hb_timestamp: "00000004", Last_hb_status: "OK"} kill_sm_goroutines() go send_sm_req() @@ -1418,71 +1418,71 @@ func TestHBMapping2(t *testing.T) { stopErrorComps = []string{} compLock.Unlock() - hb_update_notify(&hbi1,HB_started) - hb_update_notify(&hbi2,HB_restarted_warn) - hb_update_notify(&hbi3,HB_stopped_warn) - hb_update_notify(&hbi4,HB_stopped_error) + hb_update_notify(&hbi1, HB_started) + hb_update_notify(&hbi2, HB_restarted_warn) + hb_update_notify(&hbi3, HB_stopped_warn) + hb_update_notify(&hbi4, HB_stopped_error) - hb_update_notify(&hbi1,HB_restarted_warn) - hb_update_notify(&hbi2,HB_stopped_warn) - hb_update_notify(&hbi3,HB_stopped_error) - hb_update_notify(&hbi4,HB_started) + hb_update_notify(&hbi1, HB_restarted_warn) + hb_update_notify(&hbi2, HB_stopped_warn) + hb_update_notify(&hbi3, HB_stopped_error) + hb_update_notify(&hbi4, HB_started) setSMRVal(http.StatusNotFound) - hsmUpdateQ <-HSMQ_NEW + hsmUpdateQ <- HSMQ_NEW time.Sleep(2 * time.Second) //Check if we got the results we wanted. - if (len(startComps) != 0) { + if len(startComps) != 0 { t.Fatalf("'start' components incorrectly found.") } - if (len(restartComps) != 0) { + if len(restartComps) != 0 { t.Fatalf("No 'restart' components incorrectly found.") } - if (len(stopWarnComps) != 0) { + if len(stopWarnComps) != 0 { t.Fatalf("No 'stop-warn' components incorrectly found.") } - if (len(stopErrorComps) != 0) { + if len(stopErrorComps) != 0 { t.Fatalf("No 'stop-error' components incorrectly found.") } //Make sure global maps are NOT cleared nitems := 0 - for _,v := range(StartMap) { - if (v != 0) { - nitems ++ + for _, v := range StartMap { + if v != 0 { + nitems++ } } - if (nitems != 0) { + if nitems != 0 { t.Errorf("Global Start Map is not cleared.") } nitems = 0 - for _,v := range(RestartMap) { - if (v != 0) { - nitems ++ + for _, v := range RestartMap { + if v != 0 { + nitems++ } } - if (nitems != 0) { + if nitems != 0 { t.Errorf("Global Restart Map is not cleared.") } nitems = 0 - for _,v := range(StopWarnMap) { - if (v != 0) { - nitems ++ + for _, v := range StopWarnMap { + if v != 0 { + nitems++ } } - if (nitems != 0) { + if nitems != 0 { t.Errorf("Global Stop Warning Map is not cleared.") } nitems = 0 - for _,v := range(StopErrorMap) { - if (v != 0) { - nitems ++ + for _, v := range StopErrorMap { + if v != 0 { + nitems++ } } - if (nitems != 0) { + if nitems != 0 { t.Errorf("Global Stop Error Map is not cleared.") } @@ -1495,56 +1495,55 @@ func TestHBMapping2(t *testing.T) { stopErrorComps = []string{} compLock.Unlock() - hb_update_notify(&hbi5,HB_started) + hb_update_notify(&hbi5, HB_started) setSMRVal(http.StatusOK) - hsmUpdateQ <-HSMQ_NEW + hsmUpdateQ <- HSMQ_NEW time.Sleep(2 * time.Second) //Check if we got the results we wanted. - if (len(startComps) == 0) { + if len(startComps) == 0 { t.Fatalf("No 'start' components found.") } - if (len(restartComps) == 0) { + if len(restartComps) == 0 { t.Fatalf("No 'restart' components found.") } - if (len(stopWarnComps) == 0) { + if len(stopWarnComps) == 0 { t.Fatalf("No 'stop-warn' components found.") } - if (len(stopErrorComps) == 0) { + if len(stopErrorComps) == 0 { t.Fatalf("No 'stop-error' components found.") } - if (len(startComps) > 2) { - t.Errorf("Too many 'start' components (%d), expected 2.",len(startComps)) + if len(startComps) > 2 { + t.Errorf("Too many 'start' components (%d), expected 2.", len(startComps)) } - if (len(restartComps) > 1) { - t.Errorf("Too many 'restart' components (%d), expected 1.",len(restartComps)) + if len(restartComps) > 1 { + t.Errorf("Too many 'restart' components (%d), expected 1.", len(restartComps)) } - if (len(stopWarnComps) > 1) { - t.Errorf("Too many 'stopWarnComps' components (%d), expected 1.",len(stopWarnComps)) + if len(stopWarnComps) > 1 { + t.Errorf("Too many 'stopWarnComps' components (%d), expected 1.", len(stopWarnComps)) } - if (len(stopErrorComps) > 1) { - t.Errorf("Too many 'stopErrorComps' components (%d), expected 1.",len(stopErrorComps)) + if len(stopErrorComps) > 1 { + t.Errorf("Too many 'stopErrorComps' components (%d), expected 1.", len(stopErrorComps)) } - if ((startComps[0] != hbi4.Component) && - (startComps[1] != hbi4.Component)) { - t.Errorf("HB start not found on '%s'",hbi4.Component) + if (startComps[0] != hbi4.Component) && + (startComps[1] != hbi4.Component) { + t.Errorf("HB start not found on '%s'", hbi4.Component) } - if ((startComps[0] != hbi5.Component) && - (startComps[1] != hbi5.Component)) { - t.Errorf("HB start not found on '%s'",hbi5.Component) + if (startComps[0] != hbi5.Component) && + (startComps[1] != hbi5.Component) { + t.Errorf("HB start not found on '%s'", hbi5.Component) } - if (restartComps[0] != hbi1.Component) { - t.Errorf("HB restart not found on '%s'",hbi1.Component) + if restartComps[0] != hbi1.Component { + t.Errorf("HB restart not found on '%s'", hbi1.Component) } - if (stopWarnComps[0] != hbi2.Component) { - t.Errorf("HB stop-warning not found on '%s'",hbi2.Component) + if stopWarnComps[0] != hbi2.Component { + t.Errorf("HB stop-warning not found on '%s'", hbi2.Component) } - if (stopErrorComps[0] != hbi3.Component) { - t.Errorf("HB stop-error not found on '%s'",hbi3.Component) + if stopErrorComps[0] != hbi3.Component { + t.Errorf("HB stop-error not found on '%s'", hbi3.Component) } } - diff --git a/go.mod b/go.mod index 47757e8..b3716d3 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,10 @@ module github.com/Cray-HPE/hms-hbtd go 1.16 require ( - github.com/Cray-HPE/hms-base v1.15.0 - github.com/Cray-HPE/hms-hmetcd v1.10.2 + github.com/Cray-HPE/hms-base/v2 v2.0.1 + github.com/Cray-HPE/hms-hmetcd v1.10.3 github.com/Cray-HPE/hms-msgbus v1.11.0 + github.com/Cray-HPE/hms-xname v1.3.0 github.com/gorilla/mux v1.8.0 github.com/gorilla/websocket v1.4.1 // indirect ) diff --git a/go.sum b/go.sum index a354234..4d6d199 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,13 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/Cray-HPE/hms-base v1.15.0 h1:VaQj6cY9OAs6rcD1miE133eqQnRTkEcX1lnwJA/SUuU= -github.com/Cray-HPE/hms-base v1.15.0/go.mod h1:+G8KFLPtanLC5lQ602hrf3MDfLTmIXedTavVCOdz5XA= -github.com/Cray-HPE/hms-hmetcd v1.10.2 h1:gtCi9r3IB9OasjtJ/WgjjeJzCcUsQTEv7yYphSb8tTI= -github.com/Cray-HPE/hms-hmetcd v1.10.2/go.mod h1:cL/imOEh++ErJ1x30HEeo1a38gxBAJgviA8V4j8Wrus= +github.com/Cray-HPE/hms-base/v2 v2.0.1 h1:xHQWmoOlmA/UU0zDWjHxCK/qOF9WMLr7MrSpFAxLXWY= +github.com/Cray-HPE/hms-base/v2 v2.0.1/go.mod h1:Mq+Ao3q4YtNZJZ1ly9wnEIKyvc3+QaA1B/xpiUpyhzQ= +github.com/Cray-HPE/hms-hmetcd v1.10.3 h1:bn+MzRcHp1CzLcm/VScouwiLB46EiLR6FddCEyanhqw= +github.com/Cray-HPE/hms-hmetcd v1.10.3/go.mod h1:cL/imOEh++ErJ1x30HEeo1a38gxBAJgviA8V4j8Wrus= github.com/Cray-HPE/hms-msgbus v1.11.0 h1:w5ThMJ8D5pmuvXhc817rJDG4TTixEfvtcDi3tR6eOUM= github.com/Cray-HPE/hms-msgbus v1.11.0/go.mod h1:cxn+lUOq3tpY3+KdFml6L56ZQo8sqN2VoZ6gGxds6o8= +github.com/Cray-HPE/hms-xname v1.3.0 h1:DQmetMniubqcaL6Cxarz9+7KFfWGSEizIhfPHIgC3Gw= +github.com/Cray-HPE/hms-xname v1.3.0/go.mod h1:XKdjQSzoTps5KDOE8yWojBTAWASGaS6LfRrVDxwTQO8= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= diff --git a/vendor/github.com/Cray-HPE/hms-base/.version b/vendor/github.com/Cray-HPE/hms-base/.version deleted file mode 100644 index 141f2e8..0000000 --- a/vendor/github.com/Cray-HPE/hms-base/.version +++ /dev/null @@ -1 +0,0 @@ -1.15.0 diff --git a/vendor/github.com/Cray-HPE/hms-base/Dockerfile b/vendor/github.com/Cray-HPE/hms-base/Dockerfile deleted file mode 100644 index 36752b9..0000000 --- a/vendor/github.com/Cray-HPE/hms-base/Dockerfile +++ /dev/null @@ -1,45 +0,0 @@ -# MIT License -# -# (C) Copyright [2019-2021] Hewlett Packard Enterprise Development LP -# -# Permission is hereby granted, free of charge, to any person obtaining a -# copy of this software and associated documentation files (the "Software"), -# to deal in the Software without restriction, including without limitation -# the rights to use, copy, modify, merge, publish, distribute, sublicense, -# and/or sell copies of the Software, and to permit persons to whom the -# Software is furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR -# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -# OTHER DEALINGS IN THE SOFTWARE. - -# Dockerfile for building HMS base. Note that this image -# can't be run as these are just packages in this repo. - -# Build base just has the packages installed we need. -FROM arti.dev.cray.com/baseos-docker-master-local/golang:1.16-alpine3.13 AS build-base - -RUN set -ex \ - && apk update \ - && apk add build-base - -# Copy the files in for the next stages to use. -FROM build-base AS base - -RUN go env -w GO111MODULE=auto - -COPY *.go $GOPATH/src/github.com/Cray-HPE/hms-base/ -COPY vendor $GOPATH/src/github.com/Cray-HPE/hms-base/vendor - -# Now we can build. -FROM base - -RUN set -ex \ - && go build -v github.com/Cray-HPE/hms-base/... diff --git a/vendor/github.com/Cray-HPE/hms-base/Dockerfile.build-base b/vendor/github.com/Cray-HPE/hms-base/Dockerfile.build-base deleted file mode 100644 index 0d82257..0000000 --- a/vendor/github.com/Cray-HPE/hms-base/Dockerfile.build-base +++ /dev/null @@ -1,39 +0,0 @@ -# MIT License -# -# (C) Copyright [2019-2021] Hewlett Packard Enterprise Development LP -# -# Permission is hereby granted, free of charge, to any person obtaining a -# copy of this software and associated documentation files (the "Software"), -# to deal in the Software without restriction, including without limitation -# the rights to use, copy, modify, merge, publish, distribute, sublicense, -# and/or sell copies of the Software, and to permit persons to whom the -# Software is furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR -# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -# OTHER DEALINGS IN THE SOFTWARE. - -# Dockerfile for creating a base image that can be used -# for other images to do testing, coverage, and building. - -# Build base just has the packages installed we need. -FROM arti.dev.cray.com/baseos-docker-master-local/golang:1.16-alpine3.13 AS build-base - -RUN set -ex \ - && apk update \ - && apk add build-base - -# Copy the files in for the next stages to use. -FROM build-base - -RUN go env -w GO111MODULE=auto - -COPY *.go $GOPATH/src/github.com/Cray-HPE/hms-base/ -COPY vendor $GOPATH/src/github.com/Cray-HPE/hms-base/vendor diff --git a/vendor/github.com/Cray-HPE/hms-base/Dockerfile.coverage b/vendor/github.com/Cray-HPE/hms-base/Dockerfile.coverage deleted file mode 100644 index 40a53c1..0000000 --- a/vendor/github.com/Cray-HPE/hms-base/Dockerfile.coverage +++ /dev/null @@ -1,29 +0,0 @@ -# MIT License -# -# (C) Copyright [2019-2021] Hewlett Packard Enterprise Development LP -# -# Permission is hereby granted, free of charge, to any person obtaining a -# copy of this software and associated documentation files (the "Software"), -# to deal in the Software without restriction, including without limitation -# the rights to use, copy, modify, merge, publish, distribute, sublicense, -# and/or sell copies of the Software, and to permit persons to whom the -# Software is furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR -# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -# OTHER DEALINGS IN THE SOFTWARE. - -# Dockerfile for running testing coverage on base HMS code. - -FROM cray/hms-base-build-base - -# Run coverage. -RUN set -ex \ - && go test -cover -v github.com/Cray-HPE/hms-base/... diff --git a/vendor/github.com/Cray-HPE/hms-base/Jenkinsfile.github b/vendor/github.com/Cray-HPE/hms-base/Jenkinsfile.github deleted file mode 100644 index c5c0c38..0000000 --- a/vendor/github.com/Cray-HPE/hms-base/Jenkinsfile.github +++ /dev/null @@ -1,34 +0,0 @@ -@Library('csm-shared-library') _ - -pipeline { - agent { - label "metal-gcp-builder" - } - - options { - buildDiscarder(logRotator(numToKeepStr: "10")) - timestamps() - } - - environment { - NAME = "hms-base" - DESCRIPTION = "Cray Hardware Management System Base library" - IS_STABLE = getBuildIsStable() - VERSION = getDockerBuildVersion(isStable: env.IS_STABLE) - DOCKER_ARGS = getDockerBuildArgs(name: env.NAME, description: env.DESCRIPTION) - } - - stages { - stage("Run Unit Tests") { - steps { - sh "make unittest" - } - } - - stage("Run Coverage Tests") { - steps { - sh "make coverage" - } - } - } -} diff --git a/vendor/github.com/Cray-HPE/hms-base/Makefile b/vendor/github.com/Cray-HPE/hms-base/Makefile deleted file mode 100644 index c7a63b1..0000000 --- a/vendor/github.com/Cray-HPE/hms-base/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -NAME ?= hms-base -VERSION ?= $(shell cat .version) - -all : unittest coverage - -unittest: - docker build --pull ${DOCKER_ARGS} --tag '${NAME}:${VERSION}' . - ./runUnitTest.sh - -coverage: - ./runCoverage.sh diff --git a/vendor/github.com/Cray-HPE/hms-base/hmslimits.go b/vendor/github.com/Cray-HPE/hms-base/hmslimits.go deleted file mode 100644 index db54c7a..0000000 --- a/vendor/github.com/Cray-HPE/hms-base/hmslimits.go +++ /dev/null @@ -1,29 +0,0 @@ -// MIT License -// -// (C) Copyright [2021] Hewlett Packard Enterprise Development LP -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR -// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. - -package base - -import () - -const ( - MaxNodesInEnclosure = 8 -) diff --git a/vendor/github.com/Cray-HPE/hms-base/runCoverage.sh b/vendor/github.com/Cray-HPE/hms-base/runCoverage.sh deleted file mode 100644 index f6504a1..0000000 --- a/vendor/github.com/Cray-HPE/hms-base/runCoverage.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash -# MIT License -# -# (C) Copyright [2021] Hewlett Packard Enterprise Development LP -# -# Permission is hereby granted, free of charge, to any person obtaining a -# copy of this software and associated documentation files (the "Software"), -# to deal in the Software without restriction, including without limitation -# the rights to use, copy, modify, merge, publish, distribute, sublicense, -# and/or sell copies of the Software, and to permit persons to whom the -# Software is furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR -# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -# OTHER DEALINGS IN THE SOFTWARE. - -# Build the build base image -docker build -t cray/hms-base-build-base -f Dockerfile.build-base . - -docker build -t cray/hms-base-coverage -f Dockerfile.coverage . diff --git a/vendor/github.com/Cray-HPE/hms-base/runUnitTest.sh b/vendor/github.com/Cray-HPE/hms-base/runUnitTest.sh deleted file mode 100644 index 40e708d..0000000 --- a/vendor/github.com/Cray-HPE/hms-base/runUnitTest.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash -# MIT License -# -# (C) Copyright [2021] Hewlett Packard Enterprise Development LP -# -# Permission is hereby granted, free of charge, to any person obtaining a -# copy of this software and associated documentation files (the "Software"), -# to deal in the Software without restriction, including without limitation -# the rights to use, copy, modify, merge, publish, distribute, sublicense, -# and/or sell copies of the Software, and to permit persons to whom the -# Software is furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR -# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -# OTHER DEALINGS IN THE SOFTWARE. - -# Build the build base image -docker build -t cray/hms-base-build-base -f Dockerfile.build-base . - -docker build -t cray/hms-base-testing -f Dockerfile.testing . diff --git a/vendor/github.com/Cray-HPE/hms-base/.gitignore b/vendor/github.com/Cray-HPE/hms-base/v2/.gitignore similarity index 100% rename from vendor/github.com/Cray-HPE/hms-base/.gitignore rename to vendor/github.com/Cray-HPE/hms-base/v2/.gitignore diff --git a/vendor/github.com/Cray-HPE/hms-base/v2/.version b/vendor/github.com/Cray-HPE/hms-base/v2/.version new file mode 100644 index 0000000..38f77a6 --- /dev/null +++ b/vendor/github.com/Cray-HPE/hms-base/v2/.version @@ -0,0 +1 @@ +2.0.1 diff --git a/vendor/github.com/Cray-HPE/hms-base/CHANGELOG.md b/vendor/github.com/Cray-HPE/hms-base/v2/CHANGELOG.md similarity index 92% rename from vendor/github.com/Cray-HPE/hms-base/CHANGELOG.md rename to vendor/github.com/Cray-HPE/hms-base/v2/CHANGELOG.md index 3b273ce..ca368c0 100644 --- a/vendor/github.com/Cray-HPE/hms-base/CHANGELOG.md +++ b/vendor/github.com/Cray-HPE/hms-base/v2/CHANGELOG.md @@ -5,6 +5,26 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [2.0.1] - 2022-01-21 + +### Changed + +- repatriated HMSError types from hms-xname +- updated arti link to artifactory.algol60.net + +## [2.0.0] - 2021-12-13 + +### Changed + +- CASMHMS-5180: Moved HMSTypes and related functions to the new hms-xname repo under the xnametypes package. + +## [1.15.1] - 2021-08-09 + +### Changed + +- Added GitHub configuration files and fixed snyk warning. + ## [1.15.0] - 2021-07-16 ### Changed diff --git a/vendor/github.com/Cray-HPE/hms-base/LICENSE b/vendor/github.com/Cray-HPE/hms-base/v2/LICENSE similarity index 100% rename from vendor/github.com/Cray-HPE/hms-base/LICENSE rename to vendor/github.com/Cray-HPE/hms-base/v2/LICENSE diff --git a/vendor/github.com/Cray-HPE/hms-base/Dockerfile.testing b/vendor/github.com/Cray-HPE/hms-base/v2/Makefile similarity index 82% rename from vendor/github.com/Cray-HPE/hms-base/Dockerfile.testing rename to vendor/github.com/Cray-HPE/hms-base/v2/Makefile index fccd2ea..437f7b6 100644 --- a/vendor/github.com/Cray-HPE/hms-base/Dockerfile.testing +++ b/vendor/github.com/Cray-HPE/hms-base/v2/Makefile @@ -1,6 +1,7 @@ +# # MIT License # -# (C) Copyright [2019-2021] Hewlett Packard Enterprise Development LP +# (C) Copyright [2018-2022] Hewlett Packard Enterprise Development LP # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), @@ -19,12 +20,10 @@ # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # OTHER DEALINGS IN THE SOFTWARE. +# +all: unittest +.PHONY: unittest -# Dockerfile for testing HMS base code. - -FROM cray/hms-base-build-base - -# Run any tests that might be present. -RUN set -ex \ - && go test -v github.com/Cray-HPE/hms-base +unittest: + go test ./... -cover diff --git a/vendor/github.com/Cray-HPE/hms-base/configWatch.go b/vendor/github.com/Cray-HPE/hms-base/v2/configWatch.go similarity index 100% rename from vendor/github.com/Cray-HPE/hms-base/configWatch.go rename to vendor/github.com/Cray-HPE/hms-base/v2/configWatch.go diff --git a/vendor/github.com/Cray-HPE/hms-base/doc.go b/vendor/github.com/Cray-HPE/hms-base/v2/doc.go similarity index 100% rename from vendor/github.com/Cray-HPE/hms-base/doc.go rename to vendor/github.com/Cray-HPE/hms-base/v2/doc.go diff --git a/vendor/github.com/Cray-HPE/hms-base/go.mod b/vendor/github.com/Cray-HPE/hms-base/v2/go.mod similarity index 80% rename from vendor/github.com/Cray-HPE/hms-base/go.mod rename to vendor/github.com/Cray-HPE/hms-base/v2/go.mod index 020ab34..5ec025f 100644 --- a/vendor/github.com/Cray-HPE/hms-base/go.mod +++ b/vendor/github.com/Cray-HPE/hms-base/v2/go.mod @@ -1,4 +1,4 @@ -module github.com/Cray-HPE/hms-base +module github.com/Cray-HPE/hms-base/v2 go 1.16 diff --git a/vendor/github.com/Cray-HPE/hms-base/go.sum b/vendor/github.com/Cray-HPE/hms-base/v2/go.sum similarity index 100% rename from vendor/github.com/Cray-HPE/hms-base/go.sum rename to vendor/github.com/Cray-HPE/hms-base/v2/go.sum diff --git a/vendor/github.com/Cray-HPE/hms-base/hmserr.go b/vendor/github.com/Cray-HPE/hms-base/v2/hmserr.go similarity index 100% rename from vendor/github.com/Cray-HPE/hms-base/hmserr.go rename to vendor/github.com/Cray-HPE/hms-base/v2/hmserr.go diff --git a/vendor/github.com/Cray-HPE/hms-base/v2/hmstypes.go b/vendor/github.com/Cray-HPE/hms-base/v2/hmstypes.go new file mode 100644 index 0000000..775d0e2 --- /dev/null +++ b/vendor/github.com/Cray-HPE/hms-base/v2/hmstypes.go @@ -0,0 +1,518 @@ +// MIT License +// +// (C) Copyright [2018-2022] Hewlett Packard Enterprise Development LP +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + +package base + +import ( + "encoding/json" + "strings" +) + +// Use HMS-wrapped errors. Subsequent errors will be children of this one. +var e = NewHMSError("hms", "GenericError") + +// +// State field used in component, set in response to events by state manager. +// 1.0.0 +// +type HMSState string + +// Valid state values for components - should refect hardware state +// Enabled/Disabled is a separate boolean field, as the component should +// still have it's actual physical state known and tracked at all times, so +// we know what it is when it is enabled. It also avoids the primary case +// where admins need to modify the state field manually. +// +// NOTE: there will be no state between on and ready. If the managed plane +// software does not have heartbeats, On is as high as it will ever get. +// So "active" is not useful. 'Paused' is not in scope now that the software +// status field exists. +const ( + StateUnknown HMSState = "Unknown" // The State is unknown. Appears missing but has not been confirmed as empty. + StateEmpty HMSState = "Empty" // The location is not populated with a component + StatePopulated HMSState = "Populated" // Present (not empty), but no further track can or is being done. + StateOff HMSState = "Off" // Present but powered off + StateOn HMSState = "On" // Powered on. If no heartbeat mechanism is available, it's software state may be unknown. + + StateStandby HMSState = "Standby" // No longer Ready and presumed dead. It typically means HB has been lost (w/alert). + StateHalt HMSState = "Halt" // No longer Ready and halted. OS has been gracefully shutdown or panicked (w/ alert). + StateReady HMSState = "Ready" // Both On and Ready to provide its expected services, i.e. used for jobs. + + // Retired (actually never used) states: + // StateActive HMSState = "Active" // If level-2 systems without hb monitoring can make a distinction between on and booting/booted. + // StatePaused HMSState = "Paused" // Was in a Ready state, but is temporarily unavailable due to admin action or a transient issue. +) + +var ErrHMSStateInvalid = e.NewChild("was not a valid HMS state") +var ErrHMSStateUnsupported = e.NewChild("HMSState value not supported for this operation") +var ErrHMSNeedForce = e.NewChild("operation not allowed and not forced.") +var ErrHMSTypeInvalid = e.NewChild("got HMSTypeInvalid instead of valid type") +var ErrHMSTypeUnsupported = e.NewChild("HMSType value not supported for this operation") // TODO should this be in base? + +// For case-insensitive verification and normalization of state strings +var hmsStateMap = map[string]HMSState{ + "unknown": StateUnknown, + "empty": StateEmpty, + "populated": StatePopulated, + "off": StateOff, + "on": StateOn, + "standby": StateStandby, + "halt": StateHalt, + "ready": StateReady, +} + +func GetHMSStateList() []string { + hmsStateList := []string{} + for _, state := range hmsStateMap { + hmsStateList = append(hmsStateList, state.String()) + } + return hmsStateList +} + +// Returns the given state string (adjusting any capitalization differences), +// if a valid state is given. Else, return the empty string. +func VerifyNormalizeState(stateStr string) string { + stateLower := strings.ToLower(stateStr) + value, ok := hmsStateMap[stateLower] + if ok != true { + return "" + } else { + return value.String() + } +} + +// Specifies valid STARTING states before changing to the indicated state, +// at least without forcing the change, which would normally be a bad idea. +// An empty array means "None without forcing. +var hmsValidStartStatesMap = map[string][]string{ + "unknown": []string{}, // Force/HSM only + "empty": []string{}, // Force/HSM only + "populated": []string{}, // Force/HSM only + "off": []string{string(StateOff), string(StateOn), string(StateStandby), string(StateHalt), string(StateReady)}, + "on": []string{string(StateOn), string(StateOff), string(StateStandby), string(StateHalt)}, + "standby": []string{string(StateStandby), string(StateReady)}, + "halt": []string{string(StateHalt), string(StateReady)}, + "ready": []string{string(StateReady), string(StateOn), string(StateOff), string(StateStandby), string(StateHalt)}, // Last three are needed (for now) if RF events break. +} + +// If ok == true, beforeStates contain valid current states a +// component can be in if it is being transitioned to afterState without +// being forced (either because it is a bad idea, or the state should +// only be set by HSM and not by other software). An empty array means 'None +// without force=true +// +// If ok == false, afterState matched no valid HMS State (case insensitive) +func GetValidStartStates(afterState string) (beforeStates []string, ok bool) { + stateLower := strings.ToLower(afterState) + beforeStates, ok = hmsValidStartStatesMap[stateLower] + return +} + +// Same as above, but with force flag. If not found, returns +// ErrHMSStateInvalid. If can only be forced, and force = false, +// error will be ErrHMSNeedForce. Otherwise list of starting states. +// If force = true and no errors, an empty array means no restrictions. +func GetValidStartStateWForce( + afterState string, + force bool, +) (beforeStates []string, err error) { + + beforeStates = []string{} + // See if transition is valid. + if force == false { + var ok bool + beforeStates, ok = GetValidStartStates(afterState) + if !ok { + err = ErrHMSStateInvalid + } else if len(beforeStates) == 0 { + err = ErrHMSNeedForce + } + } + return +} + +// Check to see if the state is above on (on is the highest we will get +// from Redfish, so these are state set by higher software layers) +func IsPostBootState(stateStr string) bool { + stateLower := strings.ToLower(stateStr) + value, ok := hmsStateMap[stateLower] + if ok != true { + return false + } else { + switch value { + //case StateActive: + // fallthrough + case StateStandby: + fallthrough + case StateHalt: + fallthrough + case StateReady: + return true + //case StatePaused: + // return true + default: + return false + } + } +} + +// Allow HMSState to be treated as a standard string type. +func (s HMSState) String() string { return string(s) } + +// +// Flag field used in component, set in response to events by state manager. +// 1.0.0 +// + +type HMSFlag string + +// Valid flag values. +const ( + FlagUnknown HMSFlag = "Unknown" + FlagOK HMSFlag = "OK" // Functioning properly + FlagWarning HMSFlag = "Warning" // Continues to operate, but has an issue that may require attention. + FlagAlert HMSFlag = "Alert" // No longer operating as expected. The state may also have changed due to error. + FlagLocked HMSFlag = "Locked" // Another service has reserved this component. +) + +// For case-insensitive verification and normalization of flag strings +var hmsFlagMap = map[string]HMSFlag{ + "unknown": FlagUnknown, + "ok": FlagOK, + "warning": FlagWarning, + "warn": FlagWarning, + "alert": FlagAlert, + "locked": FlagLocked, +} + +// Get a list of all valid HMS flags +func GetHMSFlagList() []string { + hmsFlagList := []string{} + for _, flag := range hmsFlagMap { + hmsFlagList = append(hmsFlagList, flag.String()) + } + return hmsFlagList +} + +// Returns the given flag string (adjusting any capitalization differences), +// if a valid flag was given. Else, return the empty string. +func VerifyNormalizeFlag(flagStr string) string { + flagLower := strings.ToLower(flagStr) + value, ok := hmsFlagMap[flagLower] + if ok != true { + return "" + } else { + return value.String() + } +} + +// As above, but if flag is the empty string, then return FlagOK. +// If non-empty and invalid, return the empty string. +func VerifyNormalizeFlagOK(flag string) string { + if flag == "" { + return FlagOK.String() + } + return VerifyNormalizeFlag(flag) +} + +// Allow HMSFlag to be treated as a standard string type. +func (f HMSFlag) String() string { return string(f) } + +// +// Role of component +// 1.0.0 +// + +type HMSRole string + +// Valid role values. +const ( + RoleCompute HMSRole = "Compute" + RoleService HMSRole = "Service" + RoleSystem HMSRole = "System" + RoleApplication HMSRole = "Application" + RoleStorage HMSRole = "Storage" + RoleManagement HMSRole = "Management" +) + +// For case-insensitive verification and normalization of role strings +var defaultHMSRoleMap = map[string]string{ + "compute": RoleCompute.String(), + "service": RoleService.String(), + "system": RoleSystem.String(), + "application": RoleApplication.String(), + "storage": RoleStorage.String(), + "management": RoleManagement.String(), +} + +var hmsRoleMap = defaultHMSRoleMap + +// Get a list of all valid HMS roles +func GetHMSRoleList() []string { + hmsRoleList := []string{} + for _, role := range hmsRoleMap { + hmsRoleList = append(hmsRoleList, role) + } + return hmsRoleList +} + +// Returns the given role string (adjusting any capitalization differences), +// if a valid role was given. Else, return the empty string. +func VerifyNormalizeRole(roleStr string) string { + roleLower := strings.ToLower(roleStr) + value, ok := hmsRoleMap[roleLower] + if ok != true { + return "" + } else { + return value + } +} + +// Allow HMSRole to be treated as a standard string type. +func (r HMSRole) String() string { return string(r) } + +// +// SubRole of component +// 1.0.0 +// + +type HMSSubRole string + +// Valid SubRole values. +const ( + SubRoleMaster HMSSubRole = "Master" + SubRoleWorker HMSSubRole = "Worker" + SubRoleStorage HMSSubRole = "Storage" +) + +// For case-insensitive verification and normalization of SubRole strings +var defaultHMSSubRoleMap = map[string]string{ + "master": SubRoleMaster.String(), + "worker": SubRoleWorker.String(), + "storage": SubRoleStorage.String(), +} + +var hmsSubRoleMap = defaultHMSSubRoleMap + +// Get a list of all valid HMS subroles +func GetHMSSubRoleList() []string { + hmsSubRoleList := []string{} + for _, subrole := range hmsSubRoleMap { + hmsSubRoleList = append(hmsSubRoleList, subrole) + } + return hmsSubRoleList +} + +// Returns the given SubRole string (adjusting any capitalization differences), +// if a valid SubRole was given. Else, return the empty string. +func VerifyNormalizeSubRole(subRoleStr string) string { + subRoleLower := strings.ToLower(subRoleStr) + value, ok := hmsSubRoleMap[subRoleLower] + if ok != true { + return "" + } else { + return value + } +} + +// Allow HMSSubRole to be treated as a standard string type. +func (r HMSSubRole) String() string { return string(r) } + +// +// HMSNetType - type of high speed network +// 1.0.0 +// + +type HMSNetType string + +const ( + NetSling HMSNetType = "Sling" + NetInfiniband HMSNetType = "Infiniband" + NetEthernet HMSNetType = "Ethernet" + NetOEM HMSNetType = "OEM" // Placeholder for non-slingshot + NetNone HMSNetType = "None" +) + +// For case-insensitive verification and normalization of HSN network types +var hmsNetTypeMap = map[string]HMSNetType{ + "sling": NetSling, + "infiniband": NetInfiniband, + "ethernet": NetEthernet, + "oem": NetOEM, + "none": NetNone, +} + +// Get a list of all valid HMS NetTypes +func GetHMSNetTypeList() []string { + hmsNetTypeList := []string{} + for _, netType := range hmsNetTypeMap { + hmsNetTypeList = append(hmsNetTypeList, netType.String()) + } + return hmsNetTypeList +} + +// Returns the given net type string (adjusting any capitalization differences), +// if a valid netType was given. Else, return the empty string. +func VerifyNormalizeNetType(netTypeStr string) string { + netTypeLower := strings.ToLower(netTypeStr) + value, ok := hmsNetTypeMap[netTypeLower] + if ok != true { + return "" + } else { + return value.String() + } +} + +// Allow HMSNetType to be treated as a standard string type. +func (r HMSNetType) String() string { return string(r) } + +// +// HMSArch - binary type needed for component +// 1.0.0 +// + +type HMSArch string + +const ( + ArchX86 HMSArch = "X86" + ArchARM HMSArch = "ARM" + ArchUnknown HMSArch = "UNKNOWN" + ArchOther HMSArch = "Other" +) + +// For case-insensitive verification and normalization of HSN network types +var hmsArchMap = map[string]HMSArch{ + "x86": ArchX86, + "arm": ArchARM, + "unknown": ArchUnknown, + "other": ArchOther, +} + +// Get a list of all valid HMS Arch +func GetHMSArchList() []string { + hmsArchList := []string{} + for _, arch := range hmsArchMap { + hmsArchList = append(hmsArchList, arch.String()) + } + return hmsArchList +} + +// Returns the given arch string (adjusting any capitalization differences), +// if a valid arch was given. Else, return the empty string. +func VerifyNormalizeArch(archStr string) string { + archLower := strings.ToLower(archStr) + value, ok := hmsArchMap[archLower] + if ok != true { + return "" + } else { + return value.String() + } +} + +// Allow HMSArch to be treated as a standard string type. +func (r HMSArch) String() string { return string(r) } + +// +// HMSClass - Physical hardware profile +// 1.0.0 +// + +type HMSClass string + +const ( + ClassRiver HMSClass = "River" + ClassMountain HMSClass = "Mountain" + ClassHill HMSClass = "Hill" +) + +// For case-insensitive verification and normalization of HMS Class +var hmsClassMap = map[string]HMSClass{ + "river": ClassRiver, + "mountain": ClassMountain, + "hill": ClassHill, +} + +// Get a list of all valid HMS Class +func GetHMSClassList() []string { + hmsClassList := []string{} + for _, class := range hmsClassMap { + hmsClassList = append(hmsClassList, class.String()) + } + return hmsClassList +} + +// Returns the given class string (adjusting any capitalization differences), +// if a valid class was given. Else, return the empty string. +func VerifyNormalizeClass(classStr string) string { + classLower := strings.ToLower(classStr) + value, ok := hmsClassMap[classLower] + if ok != true { + return "" + } else { + return value.String() + } +} + +// Allow HMSClass to be treated as a standard string type. +func (r HMSClass) String() string { return string(r) } + +// +// This is the equivalent to rs_node_t in Cascade. It is the minimal +// amount of of information for tracking component state and other vital +// info at an abstract level. The hwinv is for component-type specific +// fields and detailed HW attributes, i.e. just like XC. +// +// For most HMS operations (and non-inventory ones in the managed plane) +// this info should be sufficient. We want to keep it minimal for speed. +// Those fields that are not fixed at discovery should be those that can +// change outside of discovery in response to system activity, i.e. hwinv +// should contain only fields that are basically static between discoveries +// of the endpoint. Things like firmware versions might be an exception, +// but that would be a separate process SM would +// +// 1.0.0 +// +type Component struct { + ID string `json:"ID"` + Type string `json:"Type"` + State string `json:"State,omitempty"` + Flag string `json:"Flag,omitempty"` + Enabled *bool `json:"Enabled,omitempty"` + SwStatus string `json:"SoftwareStatus,omitempty"` + Role string `json:"Role,omitempty"` + SubRole string `json:"SubRole,omitempty"` + NID json.Number `json:"NID,omitempty"` + Subtype string `json:"Subtype,omitempty"` + NetType string `json:"NetType,omitempty"` + Arch string `json:"Arch,omitempty"` + Class string `json:"Class,omitempty"` + ReservationDisabled bool `json:"ReservationDisabled,omitempty"` + Locked bool `json:"Locked,omitempty"` +} + +// A collection of 0-n Components. It could just be an ordinary +// array but we want to save the option to have indentifying info, etc. +// packaged with it, e.g. the query parameters or options that produced it, +// especially if there are fewer fields than normal being included. +type ComponentArray struct { + Components []*Component `json:"Components"` +} diff --git a/vendor/github.com/Cray-HPE/hms-base/http.go b/vendor/github.com/Cray-HPE/hms-base/v2/http.go similarity index 97% rename from vendor/github.com/Cray-HPE/hms-base/http.go rename to vendor/github.com/Cray-HPE/hms-base/v2/http.go index 526e36a..29eb808 100644 --- a/vendor/github.com/Cray-HPE/hms-base/http.go +++ b/vendor/github.com/Cray-HPE/hms-base/v2/http.go @@ -31,8 +31,8 @@ import ( "github.com/hashicorp/go-retryablehttp" "io/ioutil" "net/http" - "time" "os" + "time" ) // Package to slightly abstract some of the most mundane of HTTP interactions. Primary intention is as a JSON @@ -53,30 +53,30 @@ type HTTPRequest struct { const USERAGENT = "User-Agent" -func GetServiceInstanceName() (string,error) { +func GetServiceInstanceName() (string, error) { return os.Hostname() } func SetHTTPUserAgent(req *http.Request, instName string) { - if (req == nil) { + if req == nil { return } //See if this User Agent is already in place found := false - _,ok := req.Header[USERAGENT] + _, ok := req.Header[USERAGENT] - if (ok) { - for _,v := range(req.Header[USERAGENT]) { - if (v == instName) { + if ok { + for _, v := range req.Header[USERAGENT] { + if v == instName { found = true break } } } - if (!found) { + if !found { req.Header.Add(USERAGENT, instName) } } diff --git a/vendor/github.com/Cray-HPE/hms-base/util.go b/vendor/github.com/Cray-HPE/hms-base/v2/util.go similarity index 100% rename from vendor/github.com/Cray-HPE/hms-base/util.go rename to vendor/github.com/Cray-HPE/hms-base/v2/util.go diff --git a/vendor/github.com/Cray-HPE/hms-base/worker-pool.go b/vendor/github.com/Cray-HPE/hms-base/v2/worker-pool.go similarity index 100% rename from vendor/github.com/Cray-HPE/hms-base/worker-pool.go rename to vendor/github.com/Cray-HPE/hms-base/v2/worker-pool.go diff --git a/vendor/github.com/Cray-HPE/hms-hmetcd/.version b/vendor/github.com/Cray-HPE/hms-hmetcd/.version index 5ad2491..587c5f0 100644 --- a/vendor/github.com/Cray-HPE/hms-hmetcd/.version +++ b/vendor/github.com/Cray-HPE/hms-hmetcd/.version @@ -1 +1 @@ -1.10.2 +1.10.3 diff --git a/vendor/github.com/Cray-HPE/hms-hmetcd/CHANGELOG.md b/vendor/github.com/Cray-HPE/hms-hmetcd/CHANGELOG.md index c7b1afb..faccf02 100644 --- a/vendor/github.com/Cray-HPE/hms-hmetcd/CHANGELOG.md +++ b/vendor/github.com/Cray-HPE/hms-hmetcd/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.10.3] - 2021-08-10 + +### Changed + +- Added GitHub configuration files and fixed snyk warning. + ## [1.10.2] - 2021-07-21 ### Changed diff --git a/vendor/github.com/Cray-HPE/hms-hmetcd/Dockerfile b/vendor/github.com/Cray-HPE/hms-hmetcd/Dockerfile index cd7672b..d363fc2 100644 --- a/vendor/github.com/Cray-HPE/hms-hmetcd/Dockerfile +++ b/vendor/github.com/Cray-HPE/hms-hmetcd/Dockerfile @@ -27,7 +27,7 @@ FROM arti.dev.cray.com/baseos-docker-master-local/golang:1.16-alpine3.13 AS build-base RUN set -ex \ - && apk update \ + && apk -U upgrade \ && apk add build-base # Copy the files in for the next stages to use. diff --git a/vendor/github.com/Cray-HPE/hms-hmetcd/Dockerfile.build-base b/vendor/github.com/Cray-HPE/hms-hmetcd/Dockerfile.build-base index 5ac9e21..08e202c 100644 --- a/vendor/github.com/Cray-HPE/hms-hmetcd/Dockerfile.build-base +++ b/vendor/github.com/Cray-HPE/hms-hmetcd/Dockerfile.build-base @@ -27,7 +27,7 @@ FROM arti.dev.cray.com/baseos-docker-master-local/golang:1.16-alpine3.13 AS build-base RUN set -ex \ - && apk update \ + && apk -U upgrade \ && apk add build-base # Copy the files in for the next stages to use. diff --git a/vendor/github.com/Cray-HPE/hms-hmetcd/Jenkinsfile b/vendor/github.com/Cray-HPE/hms-hmetcd/Jenkinsfile deleted file mode 100644 index 1ceabdc..0000000 --- a/vendor/github.com/Cray-HPE/hms-hmetcd/Jenkinsfile +++ /dev/null @@ -1,13 +0,0 @@ -@Library('dst-shared@master') _ - -dockerBuildPipeline { - githubPushRepo = "Cray-HPE/hms-hmetcd" - repository = "cray" - imagePrefix = "hms" - app = "hmetcd" - name = "hms-hmetcd" - description = "Cray HMS hmetcd code." - dockerfile = "Dockerfile" - slackNotification = ["", "", false, false, true, true] - product = "internal" -} diff --git a/vendor/github.com/Cray-HPE/hms-xname/LICENSE b/vendor/github.com/Cray-HPE/hms-xname/LICENSE new file mode 100644 index 0000000..eaf7da4 --- /dev/null +++ b/vendor/github.com/Cray-HPE/hms-xname/LICENSE @@ -0,0 +1,21 @@ +MIT License + +(C) Copyright 2021-2022 Hewlett Packard Enterprise Development LP + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/Cray-HPE/hms-base/hmstypes.go b/vendor/github.com/Cray-HPE/hms-xname/xnametypes/hmstypes.go similarity index 53% rename from vendor/github.com/Cray-HPE/hms-base/hmstypes.go rename to vendor/github.com/Cray-HPE/hms-xname/xnametypes/hmstypes.go index 23db8fe..f85d8fc 100644 --- a/vendor/github.com/Cray-HPE/hms-base/hmstypes.go +++ b/vendor/github.com/Cray-HPE/hms-xname/xnametypes/hmstypes.go @@ -1,6 +1,6 @@ // MIT License // -// (C) Copyright [2018-2021] Hewlett Packard Enterprise Development LP +// (C) Copyright 2018-2023 Hewlett Packard Enterprise Development LP // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), @@ -19,19 +19,15 @@ // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. - -package base +package xnametypes import ( - "encoding/json" + "fmt" "regexp" "strings" "unicode" ) -// Use HMS-wrapped errors. Subsequent errors will be children of this one. -var e = NewHMSError("hms", "GenericError") - // // HMS Component type. This is the top-level classification. There may be // subtypes as required by HMS. This is above the Redfish type and there is @@ -76,6 +72,7 @@ const ( NodeEnclosurePowerSupply HMSType = "NodeEnclosurePowerSupply" // xXcCsSeEtT NodePowerConnector HMSType = "NodePowerConnector" // xXcCsSjJ Node HMSType = "Node" // xXcCsSbBnN + VirtualNode HMSType = "VirtualNode" // xXcCsSbBnNvV Processor HMSType = "Processor" // xXcCsSbBnNpP StorageGroup HMSType = "StorageGroup" // xXcCsSbBnNgG Drive HMSType = "Drive" // xXcCsSbBnNgGkK @@ -87,18 +84,20 @@ const ( NodeFpga HMSType = "NodeFpga" // xXcCsSbBfF HSNAsic HMSType = "HSNAsic" // xXcCrRaA RouterFpga HMSType = "RouterFpga" // xXcCrRfF + RouterTOR HMSType = "RouterTOR" // xXcCrRtT RouterTORFpga HMSType = "RouterTORFpga" // xXcCrRtTfF RouterBMC HMSType = "RouterBMC" // xXcCrRbB RouterBMCNic HMSType = "RouterBMCNic" // xXcCrRbBiI RouterPowerConnector HMSType = "RouterPowerConnector" // xXcCrRvV - HSNBoard HMSType = "HSNBoard" // xXcCrReE - HSNLink HMSType = "HSNLink" // xXcCrRaAlL - HSNConnector HMSType = "HSNConnector" // xXcCrRjJ - HSNConnectorPort HMSType = "HSNConnectorPort" // xXcCrRjJpP - MgmtSwitch HMSType = "MgmtSwitch" // xXcCwW - MgmtHLSwitch HMSType = "MgmtHLSwitch" // xXcChHsS - MgmtSwitchConnector HMSType = "MgmtSwitchConnector" // xXcCwWjJ + HSNBoard HMSType = "HSNBoard" // xXcCrReE + HSNLink HMSType = "HSNLink" // xXcCrRaAlL + HSNConnector HMSType = "HSNConnector" // xXcCrRjJ + HSNConnectorPort HMSType = "HSNConnectorPort" // xXcCrRjJpP + MgmtSwitch HMSType = "MgmtSwitch" // xXcCwW + MgmtHLSwitchEnclosure HMSType = "MgmtHLSwitchEnclosure" // xXcChH + MgmtHLSwitch HMSType = "MgmtHLSwitch" // xXcChHsS + MgmtSwitchConnector HMSType = "MgmtSwitchConnector" // xXcCwWjJ // Special types and wildcards SMSBox HMSType = "SMSBox" // smsN @@ -110,23 +109,23 @@ const ( HMSTypeInvalid HMSType = "INVALID" // Not a valid type/xname ) -var ErrHMSTypeInvalid = e.NewChild("got HMSTypeInvalid instead of valid type") -var ErrHMSTypeUnsupported = e.NewChild("HMSType value not supported for this operation") - -type hmsCompRecognitionEntry struct { - Type HMSType - ParentType HMSType - Regex *regexp.Regexp - GenStr string - NumArgs int +type HMSCompRecognitionEntry struct { + Type HMSType + ParentType HMSType + ExampleString string + Regex *regexp.Regexp + GenStr string + NumArgs int } // Component recognition table keyed by normalized (i.e. all lowercase) // component name. -var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ +// WARNING: if you modify this map you MUST regenerate the xnames, see https://github.com/Cray-HPE/hms-xname#code-generation +var hmsCompRecognitionTable = map[string]HMSCompRecognitionEntry{ "invalid": { HMSTypeInvalid, HMSTypeInvalid, + "INVALID", regexp.MustCompile("INVALID"), "INVALID", 0, @@ -134,6 +133,7 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "hmstypeall": { HMSTypeAll, HMSTypeInvalid, + "all", regexp.MustCompile("^all$"), "all", 0, @@ -141,6 +141,7 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "hmstypeallsvc": { HMSTypeAllSvc, HMSTypeInvalid, + "all_svc", regexp.MustCompile("^all_svc$"), "all_svc", 0, @@ -148,6 +149,7 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "hmstypeallcomp": { HMSTypeAllComp, HMSTypeInvalid, + "all_comp", regexp.MustCompile("^all_comp$"), "all_comp", 0, @@ -155,6 +157,7 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "partition": { Partition, HMSTypeInvalid, + "pH.S", regexp.MustCompile("^p([0-9]+)(.([0-9]+))?$"), "p%d.%d", 2, @@ -162,6 +165,7 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "system": { System, HMSTypeInvalid, + "sS", regexp.MustCompile("^s0$"), "s0", 0, @@ -169,13 +173,15 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "smsbox": { SMSBox, HMSTypeInvalid, + "smsN", regexp.MustCompile("^sms([0-9]+)$"), "sms%d", 1, }, "cdu": { CDU, - HMSTypeInvalid, //TODO: what's the CDU's parent? System, right? + System, + "dD", regexp.MustCompile("^d([0-9]+)$"), "d%d", 1, @@ -183,6 +189,7 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "cdumgmtswitch": { CDUMgmtSwitch, CDU, + "dDwW", regexp.MustCompile("^d([0-9]+)w([0-9]+)$"), "d%dw%d", 2, @@ -190,6 +197,7 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "cabinetcdu": { CabinetCDU, Cabinet, + "xXdD", regexp.MustCompile("^x([0-9]{1,4})d([0-1])$"), "x%dd%d", 2, @@ -197,6 +205,7 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "cabinetpducontroller": { CabinetPDUController, Cabinet, + "xXmM", regexp.MustCompile("^x([0-9]{1,4})m([0-3])$"), "x%dm%d", 2, @@ -204,6 +213,7 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "cabinetpdu": { CabinetPDU, CabinetPDUController, + "xXmMpP", regexp.MustCompile("^x([0-9]{1,4})m([0-3])p([0-7])$"), "x%dm%dp%d", 3, @@ -211,13 +221,15 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "cabinetpdunic": { CabinetPDUNic, CabinetPDUController, + "xXmMpPiI", regexp.MustCompile("^x([0-9]{1,4})m([0-3])i([0-3])$"), - "x%dm%dp%di%d", + "x%dm%di%d", 3, }, "cabinetpduoutlet": { CabinetPDUOutlet, CabinetPDU, + "xXmMpPjJ", regexp.MustCompile("^x([0-9]{1,4})m([0-3])p([0-7])j([1-9][0-9]*)$"), "x%dm%dp%dj%d", 4, @@ -225,6 +237,7 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "cabinetpdupowerconnector": { CabinetPDUPowerConnector, CabinetPDU, + "xXmMpPvV", regexp.MustCompile("^x([0-9]{1,4})m([0-3])p([0-7])v([1-9][0-9]*)$"), "x%dm%dp%dv%d", 4, @@ -232,6 +245,7 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "cec": { CEC, Cabinet, + "xXeE", regexp.MustCompile("^x([0-9]{1,4})e([0-1])$"), "x%de%d", 2, @@ -239,6 +253,7 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "cabinet": { Cabinet, System, + "xX", regexp.MustCompile("^x([0-9]{1,4})$"), "x%d", 1, @@ -246,6 +261,7 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "cabinetbmc": { CabinetBMC, Cabinet, + "xXbB", regexp.MustCompile("^x([0-9]{1,4})b([0])$"), "x%db%d", 2, @@ -253,6 +269,7 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "chassis": { Chassis, Cabinet, + "xXcC", regexp.MustCompile("^x([0-9]{1,4})c([0-7])$"), "x%dc%d", 2, @@ -260,6 +277,7 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "chassisbmc": { ChassisBMC, Chassis, + "xXcCbB", regexp.MustCompile("^x([0-9]{1,4})c([0-7])b([0])$"), "x%dc%db%d", 3, @@ -267,13 +285,15 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "chassisbmcnic": { ChassisBMCNic, ChassisBMC, + "xXcCbBiI", regexp.MustCompile("^x([0-9]{1,4})c([0-7])b([0])i([0-3])$"), "x%dc%db%di%d", - 3, + 4, }, "cmmfpga": { CMMFpga, Chassis, + "xXcCfF", regexp.MustCompile("^x([0-9]{1,4})c([0-7])f([0])$"), "x%dc%df%d", 3, @@ -281,6 +301,7 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "cmmrectifier": { CMMRectifier, Chassis, + "xXcCtT", regexp.MustCompile("^x([0-9]{1,4})c([0-7])t([0-9])$"), "x%dc%dt%d", 3, @@ -288,6 +309,7 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "computemodule": { ComputeModule, Chassis, + "xXcCsS", regexp.MustCompile("^x([0-9]{1,4})c([0-7])s([0-9]+)$"), "x%dc%ds%d", 3, @@ -295,6 +317,7 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "storagegroup": { StorageGroup, Node, + "xXcCsSbBnNgG", regexp.MustCompile("^x([0-9]{1,4})c([0-7])s([0-9]+)b([0-9]+)n([0-9]+)g([0-9]+)$"), "x%dc%ds%db%dn%dg%d", 6, @@ -302,6 +325,7 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "drive": { Drive, StorageGroup, + "xXcCsSbBnNgGkK", regexp.MustCompile("^x([0-9]{1,4})c([0-7])s([0-9]+)b([0-9]+)n([0-9]+)g([0-9]+)k([0-9]+)$"), "x%dc%ds%db%dn%dg%dk%d", 7, @@ -309,6 +333,7 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "nodefpga": { NodeFpga, NodeEnclosure, + "xXcCsSbBfF", regexp.MustCompile("^x([0-9]{1,4})c([0-7])s([0-9]+)b([0-9]+)f([0])$"), "x%dc%ds%db%df%d", 5, @@ -316,6 +341,7 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "nodebmc": { NodeBMC, ComputeModule, + "xXcCsSbB", regexp.MustCompile("^x([0-9]{1,4})c([0-7])s([0-9]+)b([0-9]+)$"), "x%dc%ds%db%d", 4, @@ -323,6 +349,7 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "nodebmcnic": { NodeBMCNic, NodeBMC, + "xXcCsSbBiI", regexp.MustCompile("^x([0-9]{1,4})c([0-7])s([0-9]+)b([0-9]+)i([0-3])$"), "x%dc%ds%db%di%d", 5, @@ -330,6 +357,7 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "nodeenclosure": { NodeEnclosure, ComputeModule, + "xXcCsSbBeE", regexp.MustCompile("^x([0-9]{1,4})c([0-7])s([0-9]+)e([0-9]+)$"), "x%dc%ds%de%d", 4, @@ -337,20 +365,23 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "nodeenclosurepowersupply": { NodeEnclosurePowerSupply, NodeEnclosure, + "xXcCsSbBeEtT", regexp.MustCompile("^x([0-9]{1,4})c([0-7])s([0-9]+)e([0-9]+)t([0-9]+)$"), "x%dc%ds%de%dt%d", 5, }, - "nodepowerconnector": { //'j' is deprecated, should be 'v' + "nodepowerconnector": { // 'j' is deprecated, should be 'v' NodePowerConnector, ComputeModule, + "xXcCsSv", regexp.MustCompile("^x([0-9]{1,4})c([0-7])s([0-9]+)[jv]([1-2])$"), "x%dc%ds%dv%d", 4, }, "hsnboard": { HSNBoard, - RouterBMC, + RouterModule, + "xXcCrReE", regexp.MustCompile("^x([0-9]{1,4})c([0-7])r([0-9]+)e([0-9]+)$"), "x%dc%dr%de%d", 4, @@ -358,13 +389,23 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "node": { Node, NodeBMC, // Controlling entity is an nC or COTS BMC + "xXcCsSbBnN", regexp.MustCompile("^x([0-9]{1,4})c([0-7])s([0-9]+)b([0-9]+)n([0-9]+)$"), "x%dc%ds%db%dn%d", 5, }, + "virtualnode": { + VirtualNode, + Node, // The hypervisor + "xXcCsSbBnNvV", + regexp.MustCompile("^x([0-9]{1,4})c([0-7])s([0-9]+)b([0-9]+)n([0-9]+)v([0-9]+)$"), + "x%dc%ds%db%dn%dv%d", + 6, + }, "nodenic": { NodeNic, Node, + "xXcCsSbBnNiI", regexp.MustCompile("^x([0-9]{1,4})c([0-7])s([0-9]+)b([0-9]+)n([0-9]+)i([0-3])$"), "x%dc%ds%db%dn%di%d", 6, @@ -372,6 +413,7 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "nodehsnnic": { NodeHsnNic, Node, + "xXcCsSbBnNhH", regexp.MustCompile("^x([0-9]{1,4})c([0-7])s([0-9]+)b([0-9]+)n([0-9]+)h([0-3])$"), "x%dc%ds%db%dn%dh%d", 6, @@ -379,6 +421,7 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "nodeaccel": { NodeAccel, Node, + "xXcCsSbBnNaA", regexp.MustCompile("^x([0-9]{1,4})c([0-7])s([0-9]+)b([0-9]+)n([0-9]+)a([0-9]+)$"), "x%dc%ds%db%dn%da%d", 6, @@ -386,13 +429,15 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "nodeaccelriser": { NodeAccelRiser, Node, + "xXcCsSbBnNrR", regexp.MustCompile("^x([0-9]{1,4})c([0-7])s([0-9]+)b([0-9]+)n([0-9]+)r([0-7])$"), "x%dc%ds%db%dn%dr%d", 6, }, "memory": { Memory, - Node, //parent is actually a socket but we'll use node + Node, // Parent is actually a socket but we'll use node + "xXcCsSbBnNdD", regexp.MustCompile("^x([0-9]{1,4})c([0-7])s([0-9]+)b([0-9]+)n([0-9]+)d([0-9]+)$"), "x%dc%ds%db%dn%dd%d", 6, @@ -400,6 +445,7 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "processor": { Processor, Node, + "xXcCsSbBnNpP", regexp.MustCompile("^x([0-9]{1,4})c([0-7])s([0-9]+)b([0-9]+)n([0-9]+)p([0-3])$"), "x%dc%ds%db%dn%dp%d", 6, @@ -407,6 +453,7 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "routermodule": { RouterModule, Chassis, + "xXcCrR", regexp.MustCompile("^x([0-9]{1,4})c([0-7])r([0-9]+)$"), "x%dc%dr%d", 3, @@ -414,13 +461,23 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "routerfpga": { RouterFpga, RouterModule, + "xXcCrRfF", regexp.MustCompile("^x([0-9]{1,4})c([0-7])r([0-9]+)f([01])$"), "x%dc%dr%df%d", 4, }, + "routertor": { + RouterTOR, + RouterModule, + "xXcCrRtT", + regexp.MustCompile("^x([0-9]{1,4})c([0-7])r([0-9]+)t([0-9]+)$"), + "x%dc%dr%dt%d", + 4, + }, "routertorfpga": { RouterTORFpga, - RouterModule, + RouterTOR, + "xXcCrRtTfF", regexp.MustCompile("^x([0-9]{1,4})c([0-7])r([0-9]+)t([0-9]+)f([0-1])$"), "x%dc%dr%dt%df%d", 5, @@ -428,6 +485,7 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "routerbmc": { RouterBMC, RouterModule, + "xXcCrRbB", regexp.MustCompile("^x([0-9]{1,4})c([0-7])r([0-9]+)b([0-9]+)$"), "x%dc%dr%db%d", 4, @@ -435,6 +493,7 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "routerbmcnic": { RouterBMCNic, RouterBMC, + "xXcCrRbBiI", regexp.MustCompile("^x([0-9]{1,4})c([0-7])r([0-9]+)b([0-9]+)i([0-3])$"), "x%dc%dr%db%di%d", 5, @@ -442,6 +501,7 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "routerpowerconnector": { RouterPowerConnector, RouterModule, + "xXcCrRvV", regexp.MustCompile("^x([0-9]{1,4})c([0-7])r([0-9]+)v([1-2])$"), "x%dc%dr%dv%d", 4, @@ -449,6 +509,7 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "hsnasic": { HSNAsic, RouterModule, + "xXcCrRaA", regexp.MustCompile("^x([0-9]{1,4})c([0-7])r([0-9]+)a([0-3])$"), "x%dc%dr%da%d", 4, @@ -456,6 +517,7 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "hsnconnector": { HSNConnector, RouterModule, + "xXcCrRjJ", regexp.MustCompile("^x([0-9]{1,4})c([0-7])r([0-9]+)j([1-9][0-9]*)$"), "x%dc%dr%dj%d", 4, @@ -463,6 +525,7 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "hsnconnectorport": { HSNConnectorPort, HSNConnector, + "xXcCrRjJpP", regexp.MustCompile("^x([0-9]{1,4})c([0-7])r([0-9]+)j([1-9][0-9]*)p([012])$"), "x%dc%dr%dj%dp%d", 5, @@ -470,6 +533,7 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "hsnlink": { HSNLink, HSNAsic, + "xXcCrRaAlL", regexp.MustCompile("^x([0-9]{1,4})c([0-7])r([0-9]+)a([0-3])l([0-9]+)$"), "x%dc%dr%da%dl%d", 5, @@ -477,6 +541,7 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "mgmtswitch": { MgmtSwitch, Chassis, + "xXcCwW", regexp.MustCompile("^x([0-9]{1,4})c([0-7])w([1-9][0-9]*)$"), "x%dc%dw%d", 3, @@ -484,13 +549,23 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ "mgmtswitchconnector": { MgmtSwitchConnector, MgmtSwitch, + "xXcCwWjJ", regexp.MustCompile("^x([0-9]{1,4})c([0-7])w([1-9][0-9]*)j([1-9][0-9]*)$"), "x%dc%dw%dj%d", 4, }, + "mgmthlswitchenclosure": { + MgmtHLSwitchEnclosure, + Chassis, + "xXcChH", + regexp.MustCompile("^x([0-9]{1,4})c([0-7])h([1-9][0-9]*)$"), + "x%dc%dh%d", + 3, + }, "mgmthlswitch": { MgmtHLSwitch, - Chassis, + MgmtHLSwitchEnclosure, + "xXcChHsS", regexp.MustCompile("^x([0-9]{1,4})c([0-7])h([1-9][0-9]*)s([1-9])$"), "x%dc%dh%ds%d", 4, @@ -503,6 +578,16 @@ var hmsCompRecognitionTable = map[string]hmsCompRecognitionEntry{ // }, } +func GetHMSCompRecognitionTable() map[string]HMSCompRecognitionEntry { + copy := map[string]HMSCompRecognitionEntry{} + + for k, v := range hmsCompRecognitionTable { + copy[k] = v + } + + return copy +} + // Get the HMSType for a given xname, based on its pattern in the recognition // table above. // If no string matches, HMSTypeInvalid is returned. @@ -652,497 +737,41 @@ func ToHMSType(typeStr string) HMSType { } } -// Allow HMSType to be treated as a standard string type. -func (t HMSType) String() string { return string(t) } - -// -// State field used in component, set in response to events by state manager. -// 1.0.0 -// -type HMSState string - -// Valid state values for components - should refect hardware state -// Enabled/Disabled is a separate boolean field, as the component should -// still have it's actual physical state known and tracked at all times, so -// we know what it is when it is enabled. It also avoids the primary case -// where admins need to modify the state field manually. -// -// NOTE: there will be no state between on and ready. If the managed plane -// software does not have heartbeats, On is as high as it will ever get. -// So "active" is not useful. 'Paused' is not in scope now that the software -// status field exists. -const ( - StateUnknown HMSState = "Unknown" // The State is unknown. Appears missing but has not been confirmed as empty. - StateEmpty HMSState = "Empty" // The location is not populated with a component - StatePopulated HMSState = "Populated" // Present (not empty), but no further track can or is being done. - StateOff HMSState = "Off" // Present but powered off - StateOn HMSState = "On" // Powered on. If no heartbeat mechanism is available, it's software state may be unknown. - - StateStandby HMSState = "Standby" // No longer Ready and presumed dead. It typically means HB has been lost (w/alert). - StateHalt HMSState = "Halt" // No longer Ready and halted. OS has been gracefully shutdown or panicked (w/ alert). - StateReady HMSState = "Ready" // Both On and Ready to provide its expected services, i.e. used for jobs. - - // Retired (actually never used) states: - // StateActive HMSState = "Active" // If level-2 systems without hb monitoring can make a distinction between on and booting/booted. - // StatePaused HMSState = "Paused" // Was in a Ready state, but is temporarily unavailable due to admin action or a transient issue. -) - -var ErrHMSStateInvalid = e.NewChild("was not a valid HMS state") -var ErrHMSStateUnsupported = e.NewChild("HMSState value not supported for this operation") -var ErrHMSNeedForce = e.NewChild("operation not allowed and not forced.") - -// For case-insensitive verification and normalization of state strings -var hmsStateMap = map[string]HMSState{ - "unknown": StateUnknown, - "empty": StateEmpty, - "populated": StatePopulated, - "off": StateOff, - "on": StateOn, - "standby": StateStandby, - "halt": StateHalt, - "ready": StateReady, -} - -func GetHMSStateList() []string { - hmsStateList := []string{} - for _, state := range hmsStateMap { - hmsStateList = append(hmsStateList, state.String()) - } - return hmsStateList -} - -// Returns the given state string (adjusting any capitalization differences), -// if a valid state is given. Else, return the empty string. -func VerifyNormalizeState(stateStr string) string { - stateLower := strings.ToLower(stateStr) - value, ok := hmsStateMap[stateLower] - if ok != true { - return "" - } else { - return value.String() - } -} - -// Specifies valid STARTING states before changing to the indicated state, -// at least without forcing the change, which would normally be a bad idea. -// An empty array means "None without forcing. -var hmsValidStartStatesMap = map[string][]string{ - "unknown": []string{}, // Force/HSM only - "empty": []string{}, // Force/HSM only - "populated": []string{}, // Force/HSM only - "off": []string{string(StateOff), string(StateOn), string(StateStandby), string(StateHalt), string(StateReady)}, - "on": []string{string(StateOn), string(StateOff), string(StateStandby), string(StateHalt)}, - "standby": []string{string(StateStandby), string(StateReady)}, - "halt": []string{string(StateHalt), string(StateReady)}, - "ready": []string{string(StateReady), string(StateOn), string(StateOff), string(StateStandby), string(StateHalt)}, // Last three are needed (for now) if RF events break. -} - -// If ok == true, beforeStates contain valid current states a -// component can be in if it is being transitioned to afterState without -// being forced (either because it is a bad idea, or the state should -// only be set by HSM and not by other software). An empty array means 'None -// without force=true -// -// If ok == false, afterState matched no valid HMS State (case insensitive) -func GetValidStartStates(afterState string) (beforeStates []string, ok bool) { - stateLower := strings.ToLower(afterState) - beforeStates, ok = hmsValidStartStatesMap[stateLower] - return -} - -// Same as above, but with force flag. If not found, returns -// ErrHMSStateInvalid. If can only be forced, and force = false, -// error will be ErrHMSNeedForce. Otherwise list of starting states. -// If force = true and no errors, an empty array means no restrictions. -func GetValidStartStateWForce( - afterState string, - force bool, -) (beforeStates []string, err error) { - - beforeStates = []string{} - // See if transition is valid. - if force == false { - var ok bool - beforeStates, ok = GetValidStartStates(afterState) - if !ok { - err = ErrHMSStateInvalid - } else if len(beforeStates) == 0 { - err = ErrHMSNeedForce - } - } - return -} - -// Check to see if the state is above on (on is the highest we will get -// from Redfish, so these are state set by higher software layers) -func IsPostBootState(stateStr string) bool { - stateLower := strings.ToLower(stateStr) - value, ok := hmsStateMap[stateLower] - if ok != true { - return false - } else { - switch value { - //case StateActive: - // fallthrough - case StateStandby: - fallthrough - case StateHalt: - fallthrough - case StateReady: - return true - //case StatePaused: - // return true - default: - return false - } - } -} - -// Allow HMSState to be treated as a standard string type. -func (s HMSState) String() string { return string(s) } - -// -// Flag field used in component, set in response to events by state manager. -// 1.0.0 -// - -type HMSFlag string - -// Valid flag values. -const ( - FlagUnknown HMSFlag = "Unknown" - FlagOK HMSFlag = "OK" // Functioning properly - FlagWarning HMSFlag = "Warning" // Continues to operate, but has an issue that may require attention. - FlagAlert HMSFlag = "Alert" // No longer operating as expected. The state may also have changed due to error. - FlagLocked HMSFlag = "Locked" // Another service has reserved this component. -) - -// For case-insensitive verification and normalization of flag strings -var hmsFlagMap = map[string]HMSFlag{ - "unknown": FlagUnknown, - "ok": FlagOK, - "warning": FlagWarning, - "warn": FlagWarning, - "alert": FlagAlert, - "locked": FlagLocked, -} - -// Get a list of all valid HMS flags -func GetHMSFlagList() []string { - hmsFlagList := []string{} - for _, flag := range hmsFlagMap { - hmsFlagList = append(hmsFlagList, flag.String()) - } - return hmsFlagList -} - -// Returns the given flag string (adjusting any capitalization differences), -// if a valid flag was given. Else, return the empty string. -func VerifyNormalizeFlag(flagStr string) string { - flagLower := strings.ToLower(flagStr) - value, ok := hmsFlagMap[flagLower] - if ok != true { - return "" - } else { - return value.String() - } -} - -// As above, but if flag is the empty string, then return FlagOK. -// If non-empty and invalid, return the empty string. -func VerifyNormalizeFlagOK(flag string) string { - if flag == "" { - return FlagOK.String() - } - return VerifyNormalizeFlag(flag) -} - -// Allow HMSFlag to be treated as a standard string type. -func (f HMSFlag) String() string { return string(f) } - -// -// Role of component -// 1.0.0 -// - -type HMSRole string - -// Valid role values. -const ( - RoleCompute HMSRole = "Compute" - RoleService HMSRole = "Service" - RoleSystem HMSRole = "System" - RoleApplication HMSRole = "Application" - RoleStorage HMSRole = "Storage" - RoleManagement HMSRole = "Management" -) - -// For case-insensitive verification and normalization of role strings -var defaultHMSRoleMap = map[string]string{ - "compute": RoleCompute.String(), - "service": RoleService.String(), - "system": RoleSystem.String(), - "application": RoleApplication.String(), - "storage": RoleStorage.String(), - "management": RoleManagement.String(), -} - -var hmsRoleMap = defaultHMSRoleMap - -// Get a list of all valid HMS roles -func GetHMSRoleList() []string { - hmsRoleList := []string{} - for _, role := range hmsRoleMap { - hmsRoleList = append(hmsRoleList, role) - } - return hmsRoleList -} - -// Returns the given role string (adjusting any capitalization differences), -// if a valid role was given. Else, return the empty string. -func VerifyNormalizeRole(roleStr string) string { - roleLower := strings.ToLower(roleStr) - value, ok := hmsRoleMap[roleLower] - if ok != true { - return "" - } else { - return value - } -} - -// Allow HMSRole to be treated as a standard string type. -func (r HMSRole) String() string { return string(r) } - -// -// SubRole of component -// 1.0.0 -// - -type HMSSubRole string - -// Valid SubRole values. -const ( - SubRoleMaster HMSSubRole = "Master" - SubRoleWorker HMSSubRole = "Worker" - SubRoleStorage HMSSubRole = "Storage" -) - -// For case-insensitive verification and normalization of SubRole strings -var defaultHMSSubRoleMap = map[string]string{ - "master": SubRoleMaster.String(), - "worker": SubRoleWorker.String(), - "storage": SubRoleStorage.String(), -} - -var hmsSubRoleMap = defaultHMSSubRoleMap - -// Get a list of all valid HMS subroles -func GetHMSSubRoleList() []string { - hmsSubRoleList := []string{} - for _, subrole := range hmsSubRoleMap { - hmsSubRoleList = append(hmsSubRoleList, subrole) - } - return hmsSubRoleList -} - -// Returns the given SubRole string (adjusting any capitalization differences), -// if a valid SubRole was given. Else, return the empty string. -func VerifyNormalizeSubRole(subRoleStr string) string { - subRoleLower := strings.ToLower(subRoleStr) - value, ok := hmsSubRoleMap[subRoleLower] - if ok != true { - return "" - } else { - return value - } -} - -// Allow HMSSubRole to be treated as a standard string type. -func (r HMSSubRole) String() string { return string(r) } - -// -// HMSNetType - type of high speed network -// 1.0.0 -// - -type HMSNetType string - -const ( - NetSling HMSNetType = "Sling" - NetInfiniband HMSNetType = "Infiniband" - NetEthernet HMSNetType = "Ethernet" - NetOEM HMSNetType = "OEM" // Placeholder for non-slingshot - NetNone HMSNetType = "None" -) - -// For case-insensitive verification and normalization of HSN network types -var hmsNetTypeMap = map[string]HMSNetType{ - "sling": NetSling, - "infiniband": NetInfiniband, - "ethernet": NetEthernet, - "oem": NetOEM, - "none": NetNone, -} - -// Get a list of all valid HMS NetTypes -func GetHMSNetTypeList() []string { - hmsNetTypeList := []string{} - for _, netType := range hmsNetTypeMap { - hmsNetTypeList = append(hmsNetTypeList, netType.String()) - } - return hmsNetTypeList -} - -// Returns the given net type string (adjusting any capitalization differences), -// if a valid netType was given. Else, return the empty string. -func VerifyNormalizeNetType(netTypeStr string) string { - netTypeLower := strings.ToLower(netTypeStr) - value, ok := hmsNetTypeMap[netTypeLower] - if ok != true { - return "" - } else { - return value.String() +// GetHMSTypeFormatString for a given HMSType will return the corresponding +// fmt.Sprintf compatible format string, and the number of verbs are required +// for the format string. +func GetHMSTypeFormatString(hmsType HMSType) (string, int, error) { + typeLower := strings.ToLower(hmsType.String()) + if value, ok := hmsCompRecognitionTable[typeLower]; ok { + return value.GenStr, value.NumArgs, nil } -} - -// Allow HMSNetType to be treated as a standard string type. -func (r HMSNetType) String() string { return string(r) } - -// -// HMSArch - binary type needed for component -// 1.0.0 -// - -type HMSArch string -const ( - ArchX86 HMSArch = "X86" - ArchARM HMSArch = "ARM" - ArchUnknown HMSArch = "UNKNOWN" - ArchOther HMSArch = "Other" -) - -// For case-insensitive verification and normalization of HSN network types -var hmsArchMap = map[string]HMSArch{ - "x86": ArchX86, - "arm": ArchARM, - "unknown": ArchUnknown, - "other": ArchOther, + return "", 0, fmt.Errorf("unknown HMSType: %s", hmsType) } -// Get a list of all valid HMS Arch -func GetHMSArchList() []string { - hmsArchList := []string{} - for _, arch := range hmsArchMap { - hmsArchList = append(hmsArchList, arch.String()) +// GetHMSTypeRegex for a given HMSType will return the regular expression +// that matches to match xnames of that HMSType. +func GetHMSTypeRegex(hmsType HMSType) (*regexp.Regexp, error) { + typeLower := strings.ToLower(hmsType.String()) + if value, ok := hmsCompRecognitionTable[typeLower]; ok { + return value.Regex, nil } - return hmsArchList -} -// Returns the given arch string (adjusting any capitalization differences), -// if a valid arch was given. Else, return the empty string. -func VerifyNormalizeArch(archStr string) string { - archLower := strings.ToLower(archStr) - value, ok := hmsArchMap[archLower] - if ok != true { - return "" - } else { - return value.String() - } + return nil, fmt.Errorf("unknown HMSType: %s", hmsType) } -// Allow HMSArch to be treated as a standard string type. -func (r HMSArch) String() string { return string(r) } +// Allow HMSType to be treated as a standard string type. +func (t HMSType) String() string { return string(t) } +// Given a properly formatted xname, get its immediate parent. // -// HMSClass - Physical hardware profile -// 1.0.0 -// - -type HMSClass string - -const ( - ClassRiver HMSClass = "River" - ClassMountain HMSClass = "Mountain" - ClassHill HMSClass = "Hill" -) - -// For case-insensitive verification and normalization of HMS Class -var hmsClassMap = map[string]HMSClass{ - "river": ClassRiver, - "mountain": ClassMountain, - "hill": ClassHill, -} - -// Get a list of all valid HMS Class -func GetHMSClassList() []string { - hmsClassList := []string{} - for _, class := range hmsClassMap { - hmsClassList = append(hmsClassList, class.String()) - } - return hmsClassList -} - -// Returns the given class string (adjusting any capitalization differences), -// if a valid class was given. Else, return the empty string. -func VerifyNormalizeClass(classStr string) string { - classLower := strings.ToLower(classStr) - value, ok := hmsClassMap[classLower] - if ok != true { - return "" - } else { - return value.String() +// i.e. x0c0s22b11 would become x0c0s22 +func GetHMSCompParent(xname string) string { + hmsType := GetHMSType(xname) + if hmsType == CDU || hmsType == Cabinet { + return "s0" } -} - -// Allow HMSClass to be treated as a standard string type. -func (r HMSClass) String() string { return string(r) } - -// -// This is the equivalent to rs_node_t in Cascade. It is the minimal -// amount of of information for tracking component state and other vital -// info at an abstract level. The hwinv is for component-type specific -// fields and detailed HW attributes, i.e. just like XC. -// -// For most HMS operations (and non-inventory ones in the managed plane) -// this info should be sufficient. We want to keep it minimal for speed. -// Those fields that are not fixed at discovery should be those that can -// change outside of discovery in response to system activity, i.e. hwinv -// should contain only fields that are basically static between discoveries -// of the endpoint. Things like firmware versions might be an exception, -// but that would be a separate process SM would -// -// 1.0.0 -// -type Component struct { - ID string `json:"ID"` - Type string `json:"Type"` - State string `json:"State,omitempty"` - Flag string `json:"Flag,omitempty"` - Enabled *bool `json:"Enabled,omitempty"` - SwStatus string `json:"SoftwareStatus,omitempty"` - Role string `json:"Role,omitempty"` - SubRole string `json:"SubRole,omitempty"` - NID json.Number `json:"NID,omitempty"` - Subtype string `json:"Subtype,omitempty"` - NetType string `json:"NetType,omitempty"` - Arch string `json:"Arch,omitempty"` - Class string `json:"Class,omitempty"` - ReservationDisabled bool `json:"ReservationDisabled,omitempty"` - Locked bool `json:"Locked,omitempty"` -} -// A collection of 0-n Components. It could just be an ordinary -// array but we want to save the option to have indentifying info, etc. -// packaged with it, e.g. the query parameters or options that produced it, -// especially if there are fewer fields than normal being included. -type ComponentArray struct { - Components []*Component `json:"Components"` -} - -// Given a properly formatted xname, get its immediate parent. -// i.e. x0c0s22b11 would become x0c0s22 -func GetHMSCompParent(xname string) string { // Trim all trailing numbers, then in the result, trim all trailing // letters. pstr := strings.TrimRightFunc(xname, @@ -1210,3 +839,41 @@ func ValidateCompIDs(compIDs []string, dupsValid bool) ([]string, []string) { return valid, invalid } + +// Remove leading zeros, i.e. for each run of numbers, trim off leading +// zeros so each run starts with either non-zero, or is a single zero. +// This has been duplicated from hms-base, but it allows the packages to be independent. +func RemoveLeadingZeros(s string) string { + //var b strings.Builder // Go 1.10 + b := []byte("") + + // base case + length := len(s) + if length < 2 { + return s + } + // Look for 0 after letter and before number. Skip these and + // pretend the previous value was still a letter for the next + // round, to catch multiple leading zeros. + i := 0 + lastLetter := true + for ; i < length-1; i++ { + if s[i] == '0' && lastLetter == true { + if unicode.IsNumber(rune(s[i+1])) { + // leading zero + continue + } + } + if unicode.IsNumber(rune(s[i])) { + lastLetter = false + } else { + lastLetter = true + } + // b.WriteByte(s[i]) // Go 1.10 + b = append(b, s[i]) + } + //b.WriteByte(s[i]) // Go 1.10 + //return b.String() + b = append(b, s[i]) + return string(b) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index a0a2198..c48c4a6 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,12 +1,15 @@ -# github.com/Cray-HPE/hms-base v1.15.0 +# github.com/Cray-HPE/hms-base/v2 v2.0.1 ## explicit -github.com/Cray-HPE/hms-base -# github.com/Cray-HPE/hms-hmetcd v1.10.2 +github.com/Cray-HPE/hms-base/v2 +# github.com/Cray-HPE/hms-hmetcd v1.10.3 ## explicit github.com/Cray-HPE/hms-hmetcd # github.com/Cray-HPE/hms-msgbus v1.11.0 ## explicit github.com/Cray-HPE/hms-msgbus +# github.com/Cray-HPE/hms-xname v1.3.0 +## explicit +github.com/Cray-HPE/hms-xname/xnametypes # github.com/confluentinc/confluent-kafka-go v1.7.0 github.com/confluentinc/confluent-kafka-go/kafka github.com/confluentinc/confluent-kafka-go/kafka/librdkafka_vendor