1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2014-2016. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21%%
22%% The reason for this (test) counter server is that the
23%% agent test suite is implemented in such a way that the
24%% agent is started once and then used for several test cases.
25%% Each request is given a request id which *was* generated using
26%% random! It is therefor possible, although unlikely, that a
27%% request may get a request id that has recently been used,
28%% which will cause the agent to silently reject the request.
29%% For this reason, we start this server at the start of the
30%% agent suite and stop it at the end and all request ids are
31%% generated by this server.
32%%
33
34-module(snmp_test_mgr_counter_server).
35
36-export([start/0, stop/0, increment/4]).
37
38-define(SERVER, ?MODULE).
39-define(TAB,    snmp_test_mgr_counter_tab).
40
41
42%%%-------------------------------------------------------------------
43%%% API
44%%%-------------------------------------------------------------------
45
46-spec start() -> ok.
47
48start() ->
49    Parent      = self(),
50    ReqIdServer = spawn(fun() -> init(Parent) end),
51    receive
52	{ReqIdServer, ok} ->
53	    ok;
54	{ReqIdServer, {error, Reason}} ->
55	    exit({failed_starting_counter_server, Reason})
56    after 5000 ->
57	    exit(ReqIdServer, kill), % Cleanup, just in case
58	    exit({failed_starting_counter_server, timeout})
59    end.
60
61-spec stop() -> {ok, Counters :: list()} | {error, Reason :: term()}.
62
63stop() ->
64    request(stop).
65
66
67-spec increment(Counter  :: atom(),
68	       Initial   :: non_neg_integer(),
69	       Increment :: pos_integer(),
70	       Max       :: pos_integer()) ->
71    Next :: pos_integer().
72
73increment(Counter, Initial, Increment, Max) ->
74    Request = {increment, Counter, Initial, Increment, Max},
75    case request(Request) of
76	{ok, ReqId} ->
77	    ReqId;
78	{error, Reason} ->
79	    exit(Reason)
80    end.
81
82
83request(Request) ->
84    Id  = make_ref(),
85    Msg = {self(), Id, Request},
86    try
87	begin
88	    global:send(?SERVER, Msg),
89	    receive
90		{reply, Id, Reply} ->
91		    {ok, Reply}
92	    end
93	end
94    catch
95	T:E ->
96	    {error, {T, E}}
97    end.
98
99
100%%%-------------------------------------------------------------------
101%%% Internal functions
102%%%-------------------------------------------------------------------
103
104init(Parent) ->
105    p("starting"),
106    case global:register_name(?SERVER, self()) of
107	yes ->
108	    p("name registration ok"),
109	    Parent ! {self(), ok};
110	no ->
111	    p("name registration failed"),
112	    Parent ! {self(), registration_failed},
113	    exit(registration_failed)
114    end,
115    ets:new(?TAB, [set, named_table, {keypos, 1}]),
116    loop().
117
118loop() ->
119    receive
120	{From, Id, {increment, Counter, Initial, Increment, Max}} ->
121	    Position  = 2,
122	    Threshold = Max,
123	    SetValue  = Initial,
124	    UpdateOp  = {Position, Increment, Threshold, SetValue},
125	    NextVal =
126		try ets:update_counter(?TAB, Counter, UpdateOp) of
127		    Next when is_integer(Next) ->
128			p("increment ~w: (next) ~w", [Counter, Next]),
129			Next
130		catch
131		    error:badarg ->
132			%% Oups, first time
133			p("increment ~w: (initial) ~w", [Counter, Initial]),
134			ets:insert(?TAB, {Counter, Initial}),
135			Initial
136		end,
137	    From ! {reply, Id, NextVal},
138	    loop();
139
140	{From, Id, stop} ->
141	    p("stop"),
142	    Counters = ets:tab2list(?TAB),
143	    From ! {reply, Id, Counters},
144	    exit(normal)
145    end.
146
147
148p(F) ->
149    p(F, []).
150
151p(F, A) ->
152    io:format("*** [~s] COUNTER-SERVER [~w] " ++ F ++ "~n",
153	      [snmp_test_lib:formated_timestamp(), self() | A]).
154