func setupV2cClientTest(logger seelog.LoggerInterface, testIdGenerator chan string) { Describe("V2cClient", func() { var ( clientCtxt *snmp.ClientContext numClients int clients []*snmp.V2cClient err error ) BeforeEach(func() { testId := <-testIdGenerator clientCtxt = snmp.NewClientContext(testId, 1000, logger) numClients = 1 }) JustBeforeEach(func() { clients = make([]*snmp.V2cClient, numClients) for i := 0; i < numClients; i++ { clients[i], err = clientCtxt.NewV2cClientWithPort("private", "localhost", 2000+i) } }) AfterEach(func() { clientCtxt.Shutdown() }) Describe("creating a client", func() { var errClient *snmp.V2cClient Context("using valid parameters", func() { It("should create a correctly configured client", func() { Ω(clients[0]).ShouldNot(BeNil()) Ω(err).Should(BeNil()) Ω(clients[0].Community).Should(Equal("private")) Ω(clients[0].Address.String()).Should(Equal("127.0.0.1:" + strconv.Itoa(2000))) }) }) Context("using an invalid", func() { ValidateClientCreationFailure := func(errorMsg string) { It("should generate an error with appropriate error text", func() { Ω(errClient).Should(BeNil()) Ω(err).ShouldNot(BeNil()) Ω(err.Error()).Should(Equal(errorMsg)) }) } BeforeEach(func() { numClients = 0 }) Context("address", func() { BeforeEach(func() { errClient, err = clientCtxt.NewV2cClientWithPort("private", "blahblah", 2000) }) ValidateClientCreationFailure("lookup blahblah: no such host") }) Context("port (out of range low)", func() { BeforeEach(func() { errClient, err = clientCtxt.NewV2cClientWithPort("private", "localhost", 0) }) ValidateClientCreationFailure("invalid port: 0") }) Context("port (extremly out of range low)", func() { BeforeEach(func() { errClient, err = clientCtxt.NewV2cClientWithPort("private", "localhost", math.MinInt64) }) ValidateClientCreationFailure(fmt.Sprintf("invalid port: %d", math.MinInt64)) }) Context("port (out of range high)", func() { BeforeEach(func() { errClient, err = clientCtxt.NewV2cClientWithPort("private", "localhost", 65536) }) ValidateClientCreationFailure("invalid port: 65536") }) Context("port (extremely out of range high)", func() { BeforeEach(func() { errClient, err = clientCtxt.NewV2cClientWithPort("private", "localhost", math.MaxInt64) }) ValidateClientCreationFailure(fmt.Sprintf("invalid port: %d", math.MaxInt64)) }) }) }) Describe("creating a request", func() { var req *snmp.CommunityRequest ValidateRequest := func() { It("should be possible", func() { Ω(req).ShouldNot(BeNil()) Ω(req.GetError()).Should(BeNil()) Ω(req.GetResponse()).Should(BeNil()) }) } AfterEach(func() { clientCtxt.FreeV2cRequest(req) }) Context("as a GET request", func() { Context("with no OIDS", func() { BeforeEach(func() { req = clientCtxt.AllocateV2cGetRequest() }) ValidateRequest() }) Context("with a predefined set of OIDS", func() { BeforeEach(func() { oids := []snmp.ObjectIdentifier{snmp.SYS_OBJECT_ID_OID, snmp.SYS_NAME_OID, snmp.SYS_LOCATION_OID, snmp.SYS_DESCR_OID, snmp.SYS_CONTACT_OID, snmp.SYS_UPTIME_OID} req = clientCtxt.AllocateV2cGetRequestWithOids(oids) }) ValidateRequest() }) }) Context("as a SET request", func() { BeforeEach(func() { req = clientCtxt.AllocateV2cSetRequest() }) ValidateRequest() }) Context("as a GETNEXT request", func() { BeforeEach(func() { req = clientCtxt.AllocateV2cGetNextRequest() }) ValidateRequest() }) }) Describe("sending a request to a non-existent agent", func() { ValidateRequestTimeout := func(retries int, timeoutSeconds int) { It("should timeout after the appropriate amount of time", func(done Done) { var waitGroup sync.WaitGroup waitGroup.Add(numClients) start := time.Now() for i := 0; i < numClients; i++ { clients[i].Retries = retries clients[i].TimeoutSeconds = timeoutSeconds go func(client *snmp.V2cClient) { req := clientCtxt.AllocateV2cGetRequest() client.SendRequest(req) err := req.GetError() Ω(err).ShouldNot(BeNil()) _, ok := err.(snmp.TimeoutError) Ω(ok).Should(BeTrue()) waitGroup.Done() clientCtxt.FreeV2cRequest(req) }(clients[i]) } waitGroup.Wait() Ω(time.Since(start).Seconds()).Should(BeNumerically("<", float64(timeoutSeconds*(retries+1))+0.2)) statsBin, err := clientCtxt.GetStatsBin(0) Ω(err).Should(BeNil()) Ω(statsBin.Stats[snmp.StatType_RESPONSES_RECEIVED]).Should(Equal(0)) Ω(statsBin.Stats[snmp.StatType_RESPONSES_RECEIVED_AFTER_REQUEST_TIMED_OUT]).Should(Equal(0)) Ω(statsBin.Stats[snmp.StatType_REQUESTS_TIMED_OUT_AFTER_RESPONSE_PROCESSED]).Should(Equal(0)) Ω(statsBin.Stats[snmp.StatType_REQUESTS_TIMED_OUT]).Should(Equal(numClients * retries)) Ω(statsBin.Stats[snmp.StatType_REQUESTS_SENT]).Should(Equal(numClients)) Ω(statsBin.Stats[snmp.StatType_REQUESTS_RETRIES_EXHAUSTED]).Should(Equal(numClients)) Ω(statsBin.Stats[snmp.StatType_REQUESTS_FORWARDED_TO_FLOW_CONTROL]).Should(Equal(numClients * (retries + 1))) Ω(statsBin.Stats[snmp.StatType_OUTBOUND_MESSAGES_SENT]).Should(Equal(numClients * (retries + 1))) close(done) }, float64(timeoutSeconds*(retries+1))+2) } Context("from a single client", func() { Context("using 0 retries and a timeout of 1 second", func() { ValidateRequestTimeout(0, 1) }) Context("using 1 retry and a timeout of 2 seconds", func() { ValidateRequestTimeout(1, 2) }) Context("using 2 retries and a timeout of 1 second", func() { ValidateRequestTimeout(2, 1) }) }) Context("from multiple clients", func() { BeforeEach(func() { numClients = 50 }) Context("using 0 retries and a timeout of 1 second", func() { ValidateRequestTimeout(0, 1) }) Context("using 1 retry and a timeout of 2 seconds", func() { ValidateRequestTimeout(1, 2) }) Context("using 2 retries and a timeout of 1 second", func() { ValidateRequestTimeout(2, 1) }) }) }) Describe("sending multiple requests to a non-existent agent", func() { Context("from a single client", func() { Context("using 0 retries and a timeout of 1 second", func() { timeoutSeconds := 1 retries := 0 numRequests := 3 It("should timeout after the appropriate amount of time", func(done Done) { var waitGroup sync.WaitGroup waitGroup.Add(numRequests) clients[0].Retries = retries clients[0].TimeoutSeconds = timeoutSeconds start := time.Now() for i := 0; i < numRequests; i++ { go func() { req := clientCtxt.AllocateV2cGetRequest() clients[0].SendRequest(req) err := req.GetError() Ω(err).ShouldNot(BeNil()) _, ok := err.(snmp.TimeoutError) Ω(ok).Should(BeTrue(), spew.Sdump(req)) waitGroup.Done() }() } waitGroup.Wait() Ω(time.Since(start).Seconds()).Should(BeNumerically("<", float64(timeoutSeconds*(retries+1)*numRequests)+0.2)) statsBin, err := clientCtxt.GetStatsBin(0) Ω(err).Should(BeNil()) Ω(statsBin.Stats[snmp.StatType_RESPONSES_RECEIVED]).Should(Equal(0)) Ω(statsBin.Stats[snmp.StatType_RESPONSES_RECEIVED_AFTER_REQUEST_TIMED_OUT]).Should(Equal(0)) Ω(statsBin.Stats[snmp.StatType_REQUESTS_TIMED_OUT_AFTER_RESPONSE_PROCESSED]).Should(Equal(0)) Ω(statsBin.Stats[snmp.StatType_REQUESTS_TIMED_OUT]).Should(Equal(numClients * numRequests * retries)) Ω(statsBin.Stats[snmp.StatType_REQUESTS_SENT]).Should(Equal(numClients * numRequests)) Ω(statsBin.Stats[snmp.StatType_REQUESTS_RETRIES_EXHAUSTED]).Should(Equal(numClients * numRequests)) Ω(statsBin.Stats[snmp.StatType_REQUESTS_FORWARDED_TO_FLOW_CONTROL]).Should(Equal(numClients * numRequests * (retries + 1))) Ω(statsBin.Stats[snmp.StatType_OUTBOUND_MESSAGES_SENT]).Should(Equal(numClients * numRequests * (retries + 1))) close(done) }, float64(timeoutSeconds*(retries+1)*numRequests)+2) }) }) }) Describe("sending a request to an active agent", func() { var ( agent *snmp.Agent ) BeforeEach(func() { agent = snmp.NewAgentWithPort("testAgent", 10, 2000, logger) time.Sleep(1 * time.Second) }) AfterEach(func() { agent.Shutdown() }) ValidateResponse := func(retries int, timeoutSeconds int) { It("should return a valid response", func(done Done) { var waitGroup sync.WaitGroup waitGroup.Add(numClients) start := time.Now() for i := 0; i < numClients; i++ { clients[i].Retries = retries clients[i].TimeoutSeconds = timeoutSeconds go func(client *snmp.V2cClient) { req := clientCtxt.AllocateV2cGetRequest() req.AddOid(snmp.SYS_OBJECT_ID_OID) req.AddOid(snmp.SYS_DESCR_OID) client.SendRequest(req) err := req.GetError() Ω(err).Should(BeNil()) resp := req.GetResponse() Ω(resp).ShouldNot(BeNil()) clientCtxt.FreeV2cRequest(req) waitGroup.Done() }(clients[i]) } waitGroup.Wait() Ω(time.Since(start).Seconds()).Should(BeNumerically("<", float64(timeoutSeconds*(retries+1))+0.2)) statsBin, err := clientCtxt.GetStatsBin(0) Ω(err).Should(BeNil()) Ω(statsBin.Stats[snmp.StatType_RESPONSES_RECEIVED]).Should(Equal(0)) Ω(statsBin.Stats[snmp.StatType_RESPONSES_RECEIVED_AFTER_REQUEST_TIMED_OUT]).Should(Equal(0)) Ω(statsBin.Stats[snmp.StatType_REQUESTS_TIMED_OUT_AFTER_RESPONSE_PROCESSED]).Should(Equal(0)) Ω(statsBin.Stats[snmp.StatType_REQUESTS_TIMED_OUT]).Should(Equal(numClients * retries)) Ω(statsBin.Stats[snmp.StatType_REQUESTS_SENT]).Should(Equal(numClients)) Ω(statsBin.Stats[snmp.StatType_REQUESTS_RETRIES_EXHAUSTED]).Should(Equal(numClients)) Ω(statsBin.Stats[snmp.StatType_REQUESTS_FORWARDED_TO_FLOW_CONTROL]).Should(Equal(numClients * (retries + 1))) Ω(statsBin.Stats[snmp.StatType_OUTBOUND_MESSAGES_SENT]).Should(Equal(numClients * (retries + 1))) close(done) }, float64(timeoutSeconds*(retries+1))+2) } Context("from a single client", func() { Context("using 0 retries and a timeout of 1 second", func() { ValidateResponse(0, 1) }) // Context("using 1 retry and a timeout of 2 seconds", func() { // ValidateRequestTimeout(1, 2) // }) // Context("using 2 retries and a timeout of 1 second", func() { // ValidateRequestTimeout(2, 1) // }) }) }) }) }
func setupV2cClientTest(logger seelog.LoggerInterface, testIdGenerator chan string) { Describe("V2cClient", func() { var ( clientCtxt *snmp.ClientContext numClients int clients []*snmp.V2cClient err error ) BeforeEach(func() { testId := <-testIdGenerator clientCtxt = snmp.NewClientContext(testId, 1000, logger) clientCtxt.SetDecodeErrorLogging(true) numClients = 1 }) JustBeforeEach(func() { clients = make([]*snmp.V2cClient, numClients) for i := 0; i < numClients; i++ { clients[i], err = clientCtxt.NewV2cClientWithPort("private", "localhost", 2000) } }) AfterEach(func() { clientCtxt.Shutdown() }) Describe("creating a client", func() { var errClient *snmp.V2cClient Context("using valid parameters", func() { It("should create a correctly configured client", func() { Ω(clients[0]).ShouldNot(BeNil()) Ω(err).Should(BeNil()) Ω(clients[0].Community).Should(Equal("private")) Ω(clients[0].Address.String()).Should(Equal("127.0.0.1:" + strconv.Itoa(2000))) }) }) Context("using an invalid", func() { ValidateClientCreationFailure := func(errorMsg string) { It("should generate an error with appropriate error text", func() { Ω(errClient).Should(BeNil()) Ω(err).ShouldNot(BeNil()) Ω(err.Error()).Should(Equal(errorMsg)) }) } BeforeEach(func() { numClients = 0 }) Context("address", func() { BeforeEach(func() { errClient, err = clientCtxt.NewV2cClientWithPort("private", "blahblah", 2000) }) ValidateClientCreationFailure("lookup blahblah: no such host") }) Context("port (out of range low)", func() { BeforeEach(func() { errClient, err = clientCtxt.NewV2cClientWithPort("private", "localhost", 0) }) ValidateClientCreationFailure("invalid port: 0") }) Context("port (extremly out of range low)", func() { BeforeEach(func() { errClient, err = clientCtxt.NewV2cClientWithPort("private", "localhost", math.MinInt64) }) ValidateClientCreationFailure(fmt.Sprintf("invalid port: %d", math.MinInt64)) }) Context("port (out of range high)", func() { BeforeEach(func() { errClient, err = clientCtxt.NewV2cClientWithPort("private", "localhost", 65536) }) ValidateClientCreationFailure("invalid port: 65536") }) Context("port (extremely out of range high)", func() { BeforeEach(func() { errClient, err = clientCtxt.NewV2cClientWithPort("private", "localhost", math.MaxInt64) }) ValidateClientCreationFailure(fmt.Sprintf("invalid port: %d", math.MaxInt64)) }) }) }) Describe("creating a request", func() { var req snmp.CommunityRequest ValidateRequest := func() { It("should be possible", func() { Ω(req).ShouldNot(BeNil()) Ω(req.GetError()).Should(BeNil()) Ω(req.GetResponse()).Should(BeNil()) }) } AfterEach(func() { clientCtxt.FreeV2cRequest(req) }) Context("as a GET request", func() { Context("with no OIDS", func() { BeforeEach(func() { req = clientCtxt.AllocateV2cGetRequest() }) ValidateRequest() }) Context("with a predefined set of OIDS", func() { BeforeEach(func() { oids := []snmp.ObjectIdentifier{snmp.SYS_OBJECT_ID_OID, snmp.SYS_NAME_OID, snmp.SYS_LOCATION_OID, snmp.SYS_DESCR_OID, snmp.SYS_CONTACT_OID, snmp.SYS_UPTIME_OID} req = clientCtxt.AllocateV2cGetRequestWithOids(oids) }) ValidateRequest() }) }) Context("as a SET request", func() { BeforeEach(func() { req = clientCtxt.AllocateV2cSetRequest() }) ValidateRequest() }) Context("as a GETNEXT request", func() { BeforeEach(func() { req = clientCtxt.AllocateV2cGetNextRequest() }) ValidateRequest() }) }) Describe("sending a request to a non-existent agent", func() { ValidateRequestTimeout := func(retries int, timeoutSeconds int) { It("should timeout after the appropriate amount of time", func(done Done) { var waitGroup sync.WaitGroup waitGroup.Add(numClients) start := time.Now() for i := 0; i < numClients; i++ { clients[i].Retries = retries clients[i].TimeoutSeconds = timeoutSeconds go func(client *snmp.V2cClient) { req := clientCtxt.AllocateV2cGetRequest() client.SendRequest(req) err := req.GetError() Ω(err).ShouldNot(BeNil()) _, ok := err.(snmp.TimeoutError) Ω(ok).Should(BeTrue()) waitGroup.Done() clientCtxt.FreeV2cRequest(req) }(clients[i]) } waitGroup.Wait() Ω(time.Since(start).Seconds()).Should(BeNumerically("<", float64(timeoutSeconds*(retries+1))+0.2)) requestCount := numClients msgCount := requestCount * (retries + 1) validateStats(clientCtxt, map[snmp.StatType]int{ snmp.StatType_REQUESTS_SENT: requestCount, snmp.StatType_REQUEST_RETRIES_EXHAUSTED: requestCount, snmp.StatType_REQUESTS_TIMED_OUT: requestCount * retries, snmp.StatType_REQUESTS_FORWARDED_TO_FLOW_CONTROL: msgCount, snmp.StatType_OUTBOUND_MESSAGES_SENT: msgCount, }) close(done) }, float64(timeoutSeconds*(retries+1))+2) } Context("from a single client", func() { Context("using 0 retries and a timeout of 1 second", func() { ValidateRequestTimeout(0, 1) }) Context("using 1 retry and a timeout of 2 seconds", func() { ValidateRequestTimeout(1, 2) }) Context("using 2 retries and a timeout of 1 second", func() { ValidateRequestTimeout(2, 1) }) }) Context("from multiple clients", func() { BeforeEach(func() { numClients = 50 }) Context("using 0 retries and a timeout of 1 second", func() { ValidateRequestTimeout(0, 1) }) Context("using 1 retry and a timeout of 2 seconds", func() { ValidateRequestTimeout(1, 2) }) Context("using 2 retries and a timeout of 1 second", func() { ValidateRequestTimeout(2, 1) }) }) }) Describe("sending multiple requests to a non-existent agent", func() { ValidateRequestTimeout := func(retries int, timeoutSeconds int, numRequests int) { It("should timeout after the appropriate amount of time", func(done Done) { var waitGroup sync.WaitGroup waitGroup.Add(numRequests * numClients) start := time.Now() for i := 0; i < numClients; i++ { clients[i].Retries = retries clients[i].TimeoutSeconds = timeoutSeconds go func(client *snmp.V2cClient) { for j := 0; j < numRequests; j++ { req := clientCtxt.AllocateV2cGetRequest() client.SendRequest(req) err := req.GetError() Ω(err).ShouldNot(BeNil()) _, ok := err.(snmp.TimeoutError) Ω(ok).Should(BeTrue()) waitGroup.Done() clientCtxt.FreeV2cRequest(req) } }(clients[i]) } waitGroup.Wait() Ω(time.Since(start).Seconds()).Should(BeNumerically("<", float64(timeoutSeconds*(retries+1)*numRequests)+0.2)) requestCount := numClients * numRequests msgCount := requestCount * (retries + 1) validateStats(clientCtxt, map[snmp.StatType]int{ snmp.StatType_REQUESTS_SENT: requestCount, snmp.StatType_REQUEST_RETRIES_EXHAUSTED: requestCount, snmp.StatType_REQUESTS_TIMED_OUT: requestCount * retries, snmp.StatType_REQUESTS_FORWARDED_TO_FLOW_CONTROL: msgCount, snmp.StatType_OUTBOUND_MESSAGES_SENT: msgCount, }) close(done) }, float64(timeoutSeconds*(retries+1)*numRequests)+2) } Context("from a single client", func() { Context("using 0 retries and a timeout of 1 second", func() { ValidateRequestTimeout(0, 1, 3) }) Context("using 1 retries and a timeout of 2 seconds", func() { ValidateRequestTimeout(1, 2, 3) }) Context("using 2 retries and a timeout of 1 second", func() { ValidateRequestTimeout(2, 1, 3) }) }) Context("from multiple clients", func() { BeforeEach(func() { numClients = 50 }) Context("using 0 retries and a timeout of 1 second", func() { ValidateRequestTimeout(0, 1, 3) }) Context("using 1 retries and a timeout of 2 seconds", func() { ValidateRequestTimeout(1, 2, 3) }) Context("using 2 retries and a timeout of 1 second", func() { ValidateRequestTimeout(2, 1, 3) }) }) }) Describe("sending a request to an active agent", func() { var ( agent *snmp.Agent ) BeforeEach(func() { agent = snmp.NewAgentWithPort("testAgent", 10, 2000, logger, new(fakeTransactionProvider)) agent.RegisterSingleVarOidHandler(snmp.SYS_OBJECT_ID_OID, snmp.NewObjectIdentifierOidHandler(snmp.ObjectIdentifier{1, 3, 6, 1, 4, 1, 424242, 1, 1}, false)) agent.RegisterSingleVarOidHandler(snmp.SYS_DESCR_OID, snmp.NewStringOidHandler("Test System Description", false)) agent.SetDecodeErrorLogging(true) }) AfterEach(func() { agent.Shutdown() }) ValidateResponse := func(retries int, timeoutSeconds int) { It("should return a valid response", func(done Done) { var waitGroup sync.WaitGroup waitGroup.Add(numClients) start := time.Now() for i := 0; i < numClients; i++ { clients[i].Retries = retries clients[i].TimeoutSeconds = timeoutSeconds go func(client *snmp.V2cClient) { req := clientCtxt.AllocateV2cGetRequestWithOids([]snmp.ObjectIdentifier{snmp.SYS_OBJECT_ID_OID, snmp.SYS_DESCR_OID}) client.SendRequest(req) err := req.GetError() Ω(err).Should(BeNil()) resp := req.GetResponse() Ω(resp).ShouldNot(BeNil()) logger.Debugf("Response %s", spew.Sdump(resp)) clientCtxt.FreeV2cRequest(req) waitGroup.Done() }(clients[i]) } waitGroup.Wait() Ω(time.Since(start).Seconds()).Should(BeNumerically("<", float64(0.2))) // Check Client stats validateStats(clientCtxt, map[snmp.StatType]int{ snmp.StatType_RESPONSES_RECEIVED: numClients, snmp.StatType_REQUESTS_SENT: numClients, snmp.StatType_REQUESTS_FORWARDED_TO_FLOW_CONTROL: numClients, snmp.StatType_OUTBOUND_MESSAGES_SENT: numClients, snmp.StatType_INBOUND_MESSAGES_RECEIVED: numClients, snmp.StatType_RESPONSES_RELEASED_TO_CLIENT: numClients, }) // Check Agent stats validateStats(agent, map[snmp.StatType]int{ snmp.StatType_INBOUND_MESSAGES_RECEIVED: numClients, snmp.StatType_GET_REQUESTS_RECEIVED: numClients, snmp.StatType_OUTBOUND_MESSAGES_SENT: numClients, }) close(done) }, 2) } Context("from a single client", func() { Context("using 0 retries and a timeout of 1 second", func() { ValidateResponse(0, 1) }) Context("using 1 retry and a timeout of 2 seconds", func() { ValidateResponse(1, 2) }) Context("using 2 retries and a timeout of 1 second", func() { ValidateResponse(2, 1) }) }) Context("from multiple clients", func() { BeforeEach(func() { numClients = 50 }) Context("using 0 retries and a timeout of 1 second", func() { ValidateResponse(0, 1) }) Context("using 1 retry and a timeout of 2 seconds", func() { ValidateResponse(1, 2) }) Context("using 2 retries and a timeout of 1 second", func() { ValidateResponse(2, 1) }) }) }) }) }