From 3e8aa3462a151d6b528bab917bfa7b4aab021ba5 Mon Sep 17 00:00:00 2001
From: Micael Karlberg <bmk@erlang.org>
Date: Fri, 14 Mar 2014 18:41:23 +0100
Subject: [snmp/agent] Fixed request id generation

When testing the agent, it is started and then used for a series
(v1, v2, v3, ...) of tests. But the individual test cases are
use one instance of the test manager. Because of this, the requerst
id cannot be a counter starting from 1 or any counter for that
matter. Instead every request id was generated using random.
But this is obiously not a guarantee fo uniqueness, it merely
makes it unlikely that the request ids will be reused.
To get around this problem (without having to rewrite the
entire agent test suite) a simple (global) counter server is
introduced. It is started at the start of the agent test suite
(init_per_suite) and stopped at the end (end_per_suite).
---
 lib/snmp/test/modules.mk                       |   3 +-
 lib/snmp/test/snmp_agent_test.erl              |  13 +++
 lib/snmp/test/snmp_test_mgr.erl                |   3 +-
 lib/snmp/test/snmp_test_mgr_counter_server.erl | 152 +++++++++++++++++++++++++
 4 files changed, 169 insertions(+), 2 deletions(-)
 create mode 100644 lib/snmp/test/snmp_test_mgr_counter_server.erl

(limited to 'lib/snmp')

diff --git a/lib/snmp/test/modules.mk b/lib/snmp/test/modules.mk
index 3d658bf8e8..fd8315ec4d 100644
--- a/lib/snmp/test/modules.mk
+++ b/lib/snmp/test/modules.mk
@@ -2,7 +2,7 @@
 
 # %CopyrightBegin%
 #
-# Copyright Ericsson AB 2004-2012. All Rights Reserved.
+# Copyright Ericsson AB 2004-2014. All Rights Reserved.
 #
 # The contents of this file are subject to the Erlang Public License,
 # Version 1.1, (the "License"); you may not use this file except in
@@ -42,6 +42,7 @@ TEST_UTIL_MODULES = \
 	snmp_test_manager \
 	snmp_test_mgr \
 	snmp_test_mgr_misc \
+	snmp_test_mgr_counter_server \
 	sa \
 	klas3 \
 	test1 \
diff --git a/lib/snmp/test/snmp_agent_test.erl b/lib/snmp/test/snmp_agent_test.erl
index 84fa1af71d..a6571548d6 100644
--- a/lib/snmp/test/snmp_agent_test.erl
+++ b/lib/snmp/test/snmp_agent_test.erl
@@ -555,6 +555,8 @@ init_per_suite(Config0) when is_list(Config0) ->
 
     Config3 = [{mib_dir, MibDir}, {std_mib_dir, StdMibDir} | Config2],
 
+    snmp_test_mgr_counter_server:start(), 
+
     ?DBG("init_per_suite -> end with"
 	 "~n   Config3: ~p", [Config3]),
 
@@ -565,6 +567,17 @@ end_per_suite(Config) when is_list(Config) ->
     ?DBG("end_per_suite -> entry with"
 	 "~n   Config: ~p", [Config]),
 
+    case snmp_test_mgr_counter_server:stop() of
+    	{ok, Counters} ->
+    	    ?DBG("end_per_suite -> sucessfully stopped counter server"
+    		 "~n   Counters: ~p", [Counters]);
+    
+    	{error, Reason} ->
+    	    ?DBG("end_per_suite -> failed stopping counter server"
+    		 "~n   Reason: ~p", [Reason])
+    end,
+
+    ?DBG("end_per_suite -> end", []),
     Config.
 
 
diff --git a/lib/snmp/test/snmp_test_mgr.erl b/lib/snmp/test/snmp_test_mgr.erl
index 40fcbce8f1..d4eb00ff91 100644
--- a/lib/snmp/test/snmp_test_mgr.erl
+++ b/lib/snmp/test/snmp_test_mgr.erl
@@ -657,7 +657,8 @@ make_vb(Oid) ->
     #varbind{oid = Oid, variabletype = 'NULL', value = 'NULL'}.
 
 make_request_id() ->
-    random:uniform(16#FFFFFFF-1).
+    %% random:uniform(16#FFFFFFF-1).
+    snmp_test_mgr_counter_server:increment(mgr_request_id, 1, 1, 2147483647).
 
 echo_pdu(PDU, MiniMIB) ->
     io:format("~s", [snmp_misc:format_pdu(PDU, MiniMIB)]).
diff --git a/lib/snmp/test/snmp_test_mgr_counter_server.erl b/lib/snmp/test/snmp_test_mgr_counter_server.erl
new file mode 100644
index 0000000000..db31e0380b
--- /dev/null
+++ b/lib/snmp/test/snmp_test_mgr_counter_server.erl
@@ -0,0 +1,152 @@
+%% 
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2014-2014. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%% 
+
+%% 
+%% The reason for this (test) counter server is that the 
+%% agent test suite is implemented in such a way that the 
+%% agent is started once and then used for several test cases.
+%% Each request is given a request id which *was* generated using 
+%% random! It is therefor possible, although unlikely, that a 
+%% request may get a request id that has recently been used, 
+%% which will cause the agent to silently reject the request.
+%% For this reason, we start this server at the start of the
+%% agent suite and stop it at the end and all request ids are 
+%% generated by this server.
+%% 
+
+-module(snmp_test_mgr_counter_server).
+
+-export([start/0, stop/0, increment/4]).
+
+-define(SERVER, ?MODULE).
+-define(TAB,    snmp_test_mgr_counter_tab).
+
+
+%%%-------------------------------------------------------------------
+%%% API
+%%%-------------------------------------------------------------------
+
+-spec start() -> ok.
+    
+start() ->
+    Parent      = self(),
+    ReqIdServer = spawn(fun() -> init(Parent) end),
+    receive
+	{ReqIdServer, ok} ->
+	    ok;
+	{ReqIdServer, {error, Reason}} ->
+	    exit({failed_starting_counter_server, Reason})
+    after 5000 ->
+	    exit(ReqIdServer, kill), % Cleanup, just in case
+	    exit({failed_starting_counter_server, timeout})
+    end.
+
+-spec stop() -> {ok, Counters :: list()} | {error, Reason :: term()}.
+    
+stop() ->
+    request(stop).
+
+
+-spec increment(Counter  :: atom(), 
+	       Initial   :: non_neg_integer(), 
+	       Increment :: pos_integer(), 
+	       Max       :: pos_integer()) -> 
+    Next :: pos_integer().
+
+increment(Counter, Initial, Increment, Max) ->
+    Request = {increment, Counter, Initial, Increment, Max}, 
+    case request(Request) of
+	{ok, ReqId} ->
+	    ReqId;
+	{error, Reason} ->
+	    exit(Reason)
+    end.
+
+
+request(Request) ->
+    Id  = make_ref(),
+    Msg = {self(), Id, Request}, 
+    try
+	begin
+	    global:send(?SERVER, Msg),
+	    receive
+		{reply, Id, Reply} ->
+		    {ok, Reply}
+	    end
+	end
+    catch
+	T:E ->
+	    {error, {T, E}}
+    end.
+		
+    
+%%%-------------------------------------------------------------------
+%%% Internal functions
+%%%-------------------------------------------------------------------
+
+init(Parent) ->
+    p("starting"),
+    case global:register_name(?SERVER, self()) of
+	yes ->
+	    p("name registration ok"),
+	    Parent ! {self(), ok};
+	no ->
+	    p("name registration failed"),
+	    Parent ! {self(), registration_failed},
+	    exit(registration_failed)
+    end,
+    ets:new(?TAB, [set, named_table, {keypos, 1}]),
+    loop().
+
+loop() ->
+    receive
+	{From, Id, {increment, Counter, Initial, Increment, Max}} ->
+	    Position  = 2,
+	    Threshold = Max,
+	    SetValue  = Initial,
+	    UpdateOp  = {Position, Increment, Threshold, SetValue},
+	    NextVal = 
+		try ets:update_counter(?TAB, Counter, UpdateOp) of
+		    Next when is_integer(Next) ->
+			p("increment ~w: (next) ~w", [Counter, Next]),
+			Next
+		catch
+		    error:badarg ->
+			%% Oups, first time
+			p("increment ~w: (initial) ~w", [Counter, Initial]),
+			ets:insert(?TAB, {Counter, Initial}),
+			Initial
+		end,
+	    From ! {reply, Id, NextVal},
+	    loop();
+
+	{From, Id, stop} ->
+	    p("stop"),
+	    Counters = ets:tab2list(?TAB), 
+	    From ! {reply, Id, Counters}, 
+	    exit(normal)
+    end.
+
+
+p(F) ->
+    p(F, []).
+
+p(F, A) ->
+    io:format("*** [~s] COUNTER-SERVER [~w] " ++ F ++ "~n", 
+	      [snmp_test_lib:formated_timestamp(), self() | A]).
-- 
cgit v1.2.3