From 1a1e2367db05ca0d8febe126ed107dccf063bb31 Mon Sep 17 00:00:00 2001 From: Martin Logan Date: Thu, 14 Oct 2010 22:27:06 -0500 Subject: [PATCH] further along --- ebin/resource_discovery.app | 38 ---- include/resource_discovery.hrl | 6 - overview.edoc | 140 ------------ sinan.cfg | 2 +- src/rd_core.erl | 258 --------------------- src/rd_heartbeat.erl | 114 ---------- src/rd_store.erl | 266 ---------------------- src/rd_sup.erl | 86 ------- src/resource_discovery.erl | 394 --------------------------------- 9 files changed, 1 insertion(+), 1303 deletions(-) delete mode 100644 ebin/resource_discovery.app delete mode 100644 include/resource_discovery.hrl delete mode 100644 overview.edoc delete mode 100644 src/rd_core.erl delete mode 100644 src/rd_heartbeat.erl delete mode 100644 src/rd_store.erl delete mode 100644 src/rd_sup.erl delete mode 100644 src/resource_discovery.erl diff --git a/ebin/resource_discovery.app b/ebin/resource_discovery.app deleted file mode 100644 index 5113ebf..0000000 --- a/ebin/resource_discovery.app +++ /dev/null @@ -1,38 +0,0 @@ -%%% -*- mode:erlang -*- -{application, resource_discovery, - [ - % A quick description of the application. - {description, "Resource discovery & management"}, - - % The version of the applicaton - {vsn, "0.2.0.0"}, - - % All modules used by the application. - {modules, - [ - resource_discovery, - rd_core, - rd_heartbeat, - rd_store, - rd_sup - ]}, - - % All of the registered names the application uses. - {registered, []}, - - % Applications that are to be started prior to this one. - {applications, - [ - kernel, - stdlib, - gas - ]}, - - % configuration parameters - {env, []}, - - % The M F A to start this application. - {mod, {resource_discovery, []}} - ] -}. - diff --git a/include/resource_discovery.hrl b/include/resource_discovery.hrl deleted file mode 100644 index 35578d1..0000000 --- a/include/resource_discovery.hrl +++ /dev/null @@ -1,6 +0,0 @@ -%%% Type Definitions --type resource_type() :: atom(). --type resource() :: term(). --type resource_tuple() :: {resource_type(), resource()}. - - diff --git a/overview.edoc b/overview.edoc deleted file mode 100644 index e596887..0000000 --- a/overview.edoc +++ /dev/null @@ -1,140 +0,0 @@ -@title The Resource Discovery Application -@doc - - - -

-The Resource Discovery application allows you to set up smart Erlang clusters. Producers and consumers within an Erlang cloud can find eachother with no pre configuration needed. For example lets say we have three services in an Erlang cloud of nodes: -

- - - -

-Each Video Display Agent needs to know about the System Logger. The Video Producer also needs to know about the System Logger and additionally each Video Producer needs to know about each Video Display Agent so that it can broadcast to each of them. Before understanding how all that is expressed in Resource Discovery we need to get some definitions out of the way. -

- -

Definitions

- -

-A "Resource Type" is the name of a particular type of resource. For example all instances of Video Producer could each have the type "video_producer". Each Video Producer has the same type name but each instance of that resource is different and so has it's own "Resource Instance" identifier. The "Resource Instance" of a "Resource Type" could be any Erlang term(), but is often times a node(), pid(), or registered name. In the case of our Video producer let's say it is an Erlang node name. Finally we have the "Resource" itself. A "Resource" is a type containing both a "Resource Type" and a "Resource Instance", for example a Video Producer resource, of which there could be many, could look like {video_producer, vid@mynet.com}. -

- -

Expressing Relationships

- -

-Using these definitions we can describe the relationships that we have between our different types, the Video Display Agent, the Video Producer, and the System Logger. The above, Resources and Resource Types are grouped into three categories for describing resource relationships. -

- - - -

-Local Resources are those resources that a running Erlang node has to share with the network. A Video Display Agent node could make a call like what is below to express what it has to share with the network. -

- -``` -resource_discovery:add_local_resources({video_producer, vid@mynet.com}). -''' - -

-A Video Producer node needs to know about the System Logger instance and about all instances of Video Display Agent nodes. To express this it could make a call like what is below to express this. -

- -``` -resource_discovery:add_target_types([video_producer, system_logger]). -''' - -

Finding and Using Resources

- -

-Now we are set up for our systems to obtain "Cached Resources". Cached Resources are those that have been discovered and are ready to be used. When each of the nodes comes up onto the network they will make the following call: -

- -``` -resource_discovery:inform_network(). -''' - -

-This will communicate with all other nodes in the current network. When this function returns all Resources on the network that match the local nodes Target Types will be cached locally. Furthermore the local nodes Local Resources will be presented to all other nodes on the network to give them an opportunity to cache them if they match their respective Target Types. This call will timeout in approximately 10 seconds by default. If you desire a non blocking async call to inform the network use ``async_inform_network'' instead. -

- -

-The Cached Resources are now ready to be used. There is one question that this may raise though, which is when other nodes come online and present resources how will we know it locally. Well, if you want immediate notification of new Cached Resources you can subscribe to notifications by using: -

- -``` -resource_discovery:add_callback_modules([video_send, log_send]). -''' - -

-The above call will ensure that the exported function "resource_up/1" will be called upon the caching of a new resource within the modules "video_send" and "log_send". Those functions will be called each time this happens regardless of the resource and so the resource up function should use pattern matching like that pictured below to only concern itself with the resources it needs to act upon. -

- -``` -resource_up({system_logger, Instance}) -> - ... do stuff ... -resource_up(_DontCare) -> - ok. -''' - -

-Finally to use a resource from the resource cache Resource Discovery provides a number of useful functions. The most common of these is ``get_resource/1''. This will loop through resources in round robin fashion supplying a different resource each time it is called. There are other functions available for more control over the specific resources of a particular type you get. There are also functions provided for removing resources that have been found to be malfunctioning or simply not around anymore. This is up to the user of resource discovery to do at the application level. -

- -

Getting into an Erlang Cloud

- -

-Getting into an Erlang node cloud is required as a minimal bootstrap for Resource Discovery. To accomplish initial contact by pinging remote nodes already in the desired cloud something similar to the following entries should be in your Erlang config file. If you already have a resource discovery entry in your config file there is no need to add a new one, you should in that case just add the contact_nodes entry into the already existant resource_discovery section of the config. -

- -``` -{resource_discovery, - [ - {contact_nodes, [node1@mynet.com, node2@mynet.com]} - ] -} -''' - -

-These entries instruct resource discovery to ping node1 and node2 in order to join an Erlang cloud. At least one of the pings must succeed in order for startup to succeed. Optionally all this can be overridden at the command line with the -contact_node flag: -

- -``` --contact_node node3@mynet.com -''' - -

-The above flag set at startup would instruct Resource Discovery to ping node3 instead of those configured in the config file. In the future a plugin scheme will be added so that other methods of bootstrapping into a cloud may be added. -

- -

Overcomming network and resource failures with heartbeating

- -

-At times resources fail and are perhaps taken out of the store of Cached Resources. At other times networks may fail and clouds become separated. One way of overcomming all of this is to setup heartbeats from Resource Discovery. Resource discovery can be configured to heartbeat at a specified interval. This means that it will both re-ping the configured contact nodes and will run an async_inform_network at the specified interval. To enable this place configuration similar to what is below in you Erlang config file. If you already have a resource discovery entry in your config file there is no need to add a new one, you should in that case just add the heartbeat_frequency entry into the already existant resource_discovery section of the config. -

- -``` -{resource_discovery, - [ - {heartbeat_frequency, 60000} - ] -} -''' - -

-An entry like that above tells Resource Discovery to ping every 60000 milliseconds, or one minute. An entry of 0 disables heartbeating all together and is the default if not configured. -

- -

-Enjoy! Send questions of comments to erlware-questions@erlware.org or erlware-dev@erlware.org. -

- -@author Martin Logan -@copyright 2008 Erlware diff --git a/sinan.cfg b/sinan.cfg index 19d78ac..4e24100 100644 --- a/sinan.cfg +++ b/sinan.cfg @@ -1,5 +1,5 @@ project : { name : resource_discovery - vsn : "0.2.0.0" + vsn : "0.1.0.0" }, diff --git a/src/rd_core.erl b/src/rd_core.erl deleted file mode 100644 index 15858c3..0000000 --- a/src/rd_core.erl +++ /dev/null @@ -1,258 +0,0 @@ -%%%------------------------------------------------------------------- -%%% @author Martin Logan -%%% @copyright 2008 Erlware -%%% @doc -%%% Cache and distribute resources. -%%% @end -%%%------------------------------------------------------------------- --module(rd_core). - --behaviour(gen_server). - -%% API --export([ - start_link/0, - sync_resources/1, - trade_resources/0 - ]). - -% Fetch --export([ - round_robin_get/1 - ]). - -% Store --export([ - store_local_resource_tuples/1, - store_callback_modules/1, - store_target_resource_types/1 - ]). - -% Delete --export([ - delete_local_resource_tuple/1, - delete_target_type/1, - delete_callback_module/1, - delete_resource_tuple/1 - ]). - -%% gen_server callbacks --export([init/1, handle_call/3, handle_cast/2, handle_info/2, - terminate/2, code_change/3]). - --include("resource_discovery.hrl"). - --define(SERVER, ?MODULE). - --record(state, {}). - -%%%=================================================================== -%%% API -%%%=================================================================== - -%%-------------------------------------------------------------------- -%% @doc -%% Starts the server -%% -%% @spec start_link() -> {ok, Pid} | ignore | {error, Error} -%% @end -%%-------------------------------------------------------------------- -start_link() -> - gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). - -%%----------------------------------------------------------------------- -%% @doc Store the callback modules for the local system. -%% @end -%%----------------------------------------------------------------------- --spec store_callback_modules([atom()]) -> no_return(). -store_callback_modules([H|_] = Modules) when is_atom(H) -> - gen_server:call(?SERVER, {store_callback_modules, Modules}). - -%%----------------------------------------------------------------------- -%% @doc Store the target types for the local system. Store an "I Want" -%% type. These are the types we wish to find among the node cluster. -%% @end -%%----------------------------------------------------------------------- --spec store_target_resource_types([atom()]) -> no_return(). -store_target_resource_types([H|_] = TargetTypes) when is_atom(H) -> - gen_server:call(?SERVER, {store_target_resource_types, TargetTypes}). - -%%----------------------------------------------------------------------- -%% @doc Store the "I haves" or local_resources for resource discovery. -%% @end -%%----------------------------------------------------------------------- --spec store_local_resource_tuples([resource_tuple()]) -> ok. -store_local_resource_tuples([{_,_}|_] = LocalResourceTuples) -> - gen_server:call(?SERVER, {store_local_resource_tuples, LocalResourceTuples}). - -%%----------------------------------------------------------------------- -%% @doc Remove a callback module. -%% @end -%%----------------------------------------------------------------------- --spec delete_callback_module(atom()) -> true. -delete_callback_module(CallBackModule) -> - gen_server:call(?SERVER, {delete_callback_module, CallBackModule}). - -%%----------------------------------------------------------------------- -%% @doc Remove a target type. -%% @end -%%----------------------------------------------------------------------- --spec delete_target_type(atom()) -> true. -delete_target_type(TargetType) -> - gen_server:call(?SERVER, {delete_target_type, TargetType}). - -%%----------------------------------------------------------------------- -%% @doc Remove a local resource. -%% @end -%%----------------------------------------------------------------------- --spec delete_local_resource_tuple(resource_tuple()) -> true. -delete_local_resource_tuple(LocalResourceTuple) -> - gen_server:call(?SERVER, {delete_local_resource_tuple, LocalResourceTuple}). - -%%----------------------------------------------------------------------- -%% @doc Remove a resource. -%% @end -%%----------------------------------------------------------------------- --spec delete_resource_tuple(resource_tuple()) -> true. -delete_resource_tuple({_,_} = ResourceTuple) -> - gen_server:call(?SERVER, {delete_resource_tuple, ResourceTuple}). - -%%-------------------------------------------------------------------- -%% @doc inform an rd_core server of local resources and target types. -%% This will prompt the remote servers to asyncronously send -%% back remote resource information. -%% @end -%%-------------------------------------------------------------------- --spec trade_resources() -> ok. -trade_resources() -> - gen_server:cast(?SERVER, trade_resources). - -%%----------------------------------------------------------------------- -%% @doc Gets resource of a particular type outputs and places it in last position. -%% @end -%%----------------------------------------------------------------------- --spec round_robin_get(resource_type()) -> {ok, resource()} | {error, not_found}. -round_robin_get(Type) -> - gen_server:call(?SERVER, {round_robin_get, Type}). - -%%----------------------------------------------------------------------- -%% @doc Gets resource of a particular type outputs and places it in last position. -%% @end -%%----------------------------------------------------------------------- --spec sync_resources(node()) -> ok. -sync_resources(Node) -> - LocalResourceTuples = rd_store:get_local_resource_tuples(), - TargetTypes = rd_store:get_target_types(), - {ok, FilteredRemotes} = gen_server:call({?SERVER, Node}, {sync_resources, {LocalResourceTuples, TargetTypes}}), - rd_store:store_resource_tuples(FilteredRemotes), - make_callbacks(FilteredRemotes), - ok. - -%%%=================================================================== -%%% gen_server callbacks -%%%=================================================================== - -init([]) -> - error_logger:info_msg("~n", []), - {ok, #state{}}. - -handle_call({sync_resources, {RemoteResourceTuples, RemoteTargetTypes}}, _From, State) -> - LocalResourceTuples = rd_store:get_local_resource_tuples(), - TargetTypes = rd_store:get_target_types(), - FilteredRemotes = filter_resource_tuples_by_types(TargetTypes, RemoteResourceTuples), - FilteredLocals = filter_resource_tuples_by_types(RemoteTargetTypes, LocalResourceTuples), - rd_store:store_resource_tuples(FilteredRemotes), - make_callbacks(FilteredRemotes), - {reply, {ok, FilteredLocals}, State}; -handle_call({round_robin_get, Type}, _From, State) -> - Reply = rd_store:round_robin_get(Type), - {reply, Reply, State}; -handle_call({store_callback_modules, Modules}, _From, State) -> - rd_store:store_callback_modules(Modules), - {reply, ok, State}; -handle_call({store_target_resource_types, TargetTypes}, _From, State) -> - rd_store:store_target_resource_types(TargetTypes), - {reply, ok, State}; -handle_call({store_local_resource_tuples, LocalResourceTuples}, _From, State) -> - rd_store:store_local_resource_tuples(LocalResourceTuples), - {reply, ok, State}; - -handle_call({delete_callback_module, Module}, _From, State) -> - rd_store:delete_callback_module(Module), - {reply, ok, State}; -handle_call({delete_target_type, TargetType}, _From, State) -> - rd_store:delete_target_type(TargetType), - {reply, ok, State}; -handle_call({delete_local_resource_tuple, LocalResourceTuple}, _From, State) -> - rd_store:delete_local_resource_tuple(LocalResourceTuple), - {reply, ok, State}; -handle_call({delete_resource_tuple, ResourceTuple}, _From, State) -> - rd_store:delete_local_resource_tuple(ResourceTuple), - {reply, ok, State}. - -handle_cast(trade_resources, State) -> - ResourceTuples = rd_store:get_local_resource_tuples(), - lists:foreach( - fun(Node) -> - gen_server:cast({?SERVER, Node}, - {trade_resources, {node(), ResourceTuples}}) - end, - nodes(known)), - {noreply, State}; -handle_cast({trade_resources, {ReplyTo, Remotes}}, State) -> - error_logger:info_msg("got remotes ~p~n", [Remotes]), - Locals = rd_store:get_local_resource_tuples(), - TargetTypes = rd_store:get_target_types(), - FilteredRemotes = filter_resource_tuples_by_types(TargetTypes, Remotes), - error_logger:info_msg("got remotes and filtered ~p~n", [FilteredRemotes]), - rd_store:store_resource_tuples(FilteredRemotes), - make_callbacks(FilteredRemotes), - reply(ReplyTo, Locals), - {noreply, State}. - -handle_info(_Info, State) -> - {noreply, State}. - -terminate(_Reason, _State) -> - error_logger:info_msg("", []), - ok. - -code_change(_OldVsn, State, _Extra) -> - {ok, State}. - -%%%=================================================================== -%%% Internal functions -%%%=================================================================== - -%% @private -%% @doc return a list of resources that have a resource_type() found in the target -%% types list. -%% @end -filter_resource_tuples_by_types(TargetTypes, Resources) -> - Fun = - fun({Type, _Instance} = Resource, Acc) -> - case lists:member(Type, TargetTypes) of - true -> [Resource|Acc]; - false -> Acc - end - end, - lists:foldl(Fun, [], Resources). - -%% @private -%% @doc call each callback function for each new resource in its own process. -make_callbacks(NewResources) -> - lists:foreach( - fun(Module) -> - lists:foreach(fun(Resource) -> - spawn(fun() -> Module:resource_up(Resource) end) end, - NewResources) - end, - rd_store:get_callback_modules()). - -reply(noreply, _LocalResources) -> - ok; -reply(_ReplyTo, []) -> - ok; -reply(ReplyTo, LocalResources) -> - gen_server:cast({?SERVER, ReplyTo}, - {trade_resources, {noreply, LocalResources}}). diff --git a/src/rd_heartbeat.erl b/src/rd_heartbeat.erl deleted file mode 100644 index 4561261..0000000 --- a/src/rd_heartbeat.erl +++ /dev/null @@ -1,114 +0,0 @@ -%%%------------------------------------------------------------------- -%%% File : rd_heartbeat.erl -%%% Author : Martin J. Logan -%%% @doc This does an inform nodes at a specified time interval. -%%% @end -%%%------------------------------------------------------------------- --module(rd_heartbeat). - --behaviour(gen_server). - -%% External exports --export([ start_link/1, start_link/0]). - -%% gen_server callbacks --export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). - --define(SERVER, ?MODULE). - --record(state, {frequency}). - -%%==================================================================== -%% External functions -%%==================================================================== - -%%-------------------------------------------------------------------- -%% @doc Starts the server -%%
 
-%% Expects:
-%%  Frequency - The frequency of heartbeats in milliseconds.
-%% 
-%% @spec start_link(Frequency) -> {ok, Pid} -%% @end -%%-------------------------------------------------------------------- -start_link(Frequency) -> - gen_server:start_link({local, ?SERVER}, ?MODULE, [Frequency], []). - -%% @spec start_link() -> {ok, Pid} -%% @equiv start_link(0) -start_link() -> - %% The default value is 0 which indicates no heartbeating. - {ok, Frequency} = gas:get_env(resource_discovery, heartbeat_frequency, 0), - start_link(Frequency). - -%%==================================================================== -%% Server functions -%%==================================================================== - -%%-------------------------------------------------------------------- -%% Function: init/1 -%% Description: Initiates the server -%% Returns: {ok, State} | -%% {ok, State, Timeout} | -%% ignore | -%% {stop, Reason} -%%-------------------------------------------------------------------- -init([Frequency]) -> - error_logger:info_msg("~n", []), - ok = resource_discovery:contact_nodes(), - {ok, #state{frequency = Frequency}, Frequency}. - -%%-------------------------------------------------------------------- -%% Function: handle_call/3 -%% Description: Handling call messages -%% Returns: {reply, Reply, State} | -%% {reply, Reply, State, Timeout} | -%% {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, Reply, State} | (terminate/2 is called) -%% {stop, Reason, State} (terminate/2 is called) -%%-------------------------------------------------------------------- -handle_call(_Request, _From, State) -> - {reply, ok, State}. - -%%-------------------------------------------------------------------- -%% Function: handle_cast/2 -%% Description: Handling cast messages -%% Returns: {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, State} (terminate/2 is called) -%%-------------------------------------------------------------------- -handle_cast(_Msg, State) -> - {noreply, State}. - -%%-------------------------------------------------------------------- -%% Function: handle_info/2 -%% Description: Handling all non call/cast messages -%% Returns: {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, State} (terminate/2 is called) -%%-------------------------------------------------------------------- -handle_info(timeout, State = #state{frequency = 0}) -> - {stop, normal, State}; -handle_info(timeout, State = #state{frequency = Frequency}) -> - resource_discovery:contact_nodes(), - resource_discovery:inform_network(), - {noreply, State, Frequency}. - -%%-------------------------------------------------------------------- -%% Function: terminate/2 -%% Description: Shutdown the server -%% Returns: any (ignored by gen_server) -%%-------------------------------------------------------------------- -terminate(_Reason, _State) -> - error_logger:info_msg("~n", []), - ok. - -%%-------------------------------------------------------------------- -%% Func: code_change/3 -%% Purpose: Convert process state when code is changed -%% Returns: {ok, NewState} -%%-------------------------------------------------------------------- -code_change(_OldVsn, State, _Extra) -> - {ok, State}. - diff --git a/src/rd_store.erl b/src/rd_store.erl deleted file mode 100644 index 94b0a38..0000000 --- a/src/rd_store.erl +++ /dev/null @@ -1,266 +0,0 @@ -%%%------------------------------------------------------------------- -%%% File : rd_store.erl -%%% Author : Martin J. Logan -%%% @doc The storage management functions for resource discovery -%%% @end -%%%------------------------------------------------------------------- --module(rd_store). - -%%-------------------------------------------------------------------- -%% Include files -%%-------------------------------------------------------------------- --include("resource_discovery.hrl"). --include("eunit.hrl"). - -%%-------------------------------------------------------------------- -%% External exports -%%-------------------------------------------------------------------- - -% Create --export([ - new/0 - ]). - -% Lookup --export([ - round_robin_get/1, - get_resources/1, - get_callback_modules/0, - get_local_resource_tuples/0, - get_target_types/0, - get_num_types/0, - get_types/0 - ]). - -% Delete --export([ - delete_local_resource/1, - delete_target_type/1, - delete_callback_module/1, - delete_resource/1 - ]). - -% Store --export([ - store_local_resource_tuples/1, - store_callback_modules/1, - store_target_resource_types/1, - store_resource_tuples/1, - store_resource_tuple/1 - ]). - -%%-------------------------------------------------------------------- -%% Macros -%%-------------------------------------------------------------------- --define(LKVStore, rd_local_kv_store). --define(RS, rd_resource_store). - -%%==================================================================== -%% External functions -%%==================================================================== - -%%%--------------------------- -%%% Local Type, Target type Storage -%%%--------------------------- - -%%----------------------------------------------------------------------- -%% @doc LPS stands for "Local Parameter Store". -%% Initialises persistent storage. -%% @end -%%----------------------------------------------------------------------- -new() -> - ets:new(?RS, [named_table, public]), - ets:new(?LKVStore, [named_table, public]). - -%%----------------------------------------------------------------------- -%% @doc Store the callback modules for the local system. -%% @end -%%----------------------------------------------------------------------- --spec store_callback_modules([atom()]) -> no_return(). -store_callback_modules([H|_] = Modules) when is_atom(H) -> - ets:insert(?LKVStore, {callback_modules, - lists:concat([get_callback_modules(), Modules])}). - -%%----------------------------------------------------------------------- -%% @doc Output the callback modules. -%% @end -%%----------------------------------------------------------------------- --spec get_callback_modules() -> [atom()]. -get_callback_modules() -> - case ets:lookup(?LKVStore, callback_modules) of - [{callback_modules, CallBackModules}] -> - CallBackModules; - [] -> - [] - end. - -%%----------------------------------------------------------------------- -%% @doc Remove a callback module. -%% @end -%%----------------------------------------------------------------------- --spec delete_callback_module(atom()) -> true. -delete_callback_module(CallBackModule) -> - NewCallBackModules = lists:delete(CallBackModule, get_callback_modules()), - ets:insert(?LKVStore, {callback_modules, NewCallBackModules}). - -%%----------------------------------------------------------------------- -%% @doc Store the target types for the local system. Store an "I Want" -%% type. These are the types we wish to find among the node cluster. -%% @end -%%----------------------------------------------------------------------- --spec store_target_resource_types([atom()]) -> no_return(). -store_target_resource_types([H|_] = TargetTypes) when is_atom(H) -> - ets:insert(?LKVStore, {target_types, - lists:concat([get_target_types(), TargetTypes])}). - -%%----------------------------------------------------------------------- -%% @doc Output the target types. These are the resource types we wish -%% to find within our node cluster. -%% @end -%%----------------------------------------------------------------------- --spec get_target_types() -> [atom()]. -get_target_types() -> - case ets:lookup(?LKVStore, target_types) of - [{target_types, TargetTypes}] -> - TargetTypes; - [] -> - [] - end. - -%%----------------------------------------------------------------------- -%% @doc Remove a target type. -%% @end -%%----------------------------------------------------------------------- --spec delete_target_type(atom()) -> true. -delete_target_type(TargetType) -> - ets:insert(?LKVStore, {target_types, lists:delete(TargetType, get_target_types())}). - -%%----------------------------------------------------------------------- -%% @doc Store the "I haves" or local_resources for resource discovery. -%% @end -%%----------------------------------------------------------------------- --spec store_local_resource_tuples([resource_tuple()]) -> ok. -store_local_resource_tuples([{_,_}|_] = LocalResourceTuples) -> - ets:insert(?LKVStore, {local_resources, - lists:concat([get_local_resource_tuples(), LocalResourceTuples])}). - -%%----------------------------------------------------------------------- -%% @doc Output the local resources. -%% @end -%%----------------------------------------------------------------------- --spec get_local_resource_tuples() -> [resource_tuple()]. -get_local_resource_tuples() -> - case ets:lookup(?LKVStore, local_resources) of - [{local_resources, LocalResources}] -> - LocalResources; - [] -> - [] - end. - -%%----------------------------------------------------------------------- -%% @doc Remove a local resource. -%% @end -%%----------------------------------------------------------------------- --spec delete_local_resource(resource_tuple()) -> true. -delete_local_resource(LocalResource) -> - ets:insert(?LKVStore, {local_resources, lists:delete(LocalResource, get_local_resource_tuples())}). - -%%%--------------------------- -%%% Network Resource Storage -%%%--------------------------- - -%%----------------------------------------------------------------------- -%% @doc Outputs a list of all resources for a particular type. -%% @end -%%----------------------------------------------------------------------- --spec get_resources(resource_type()) -> [resource()]. -get_resources(Type) -> - case ets:lookup(?RS, Type) of - [{Type, Resources}] -> - Resources; - [] -> - [] - end. - -%%----------------------------------------------------------------------- -%% @doc Adds a new resource. -%% @end -%%----------------------------------------------------------------------- --spec store_resource_tuple(resource_tuple()) -> no_return(). -store_resource_tuple({Type, Resource}) when is_atom(Type) -> - ets:insert(?RS, {Type, [Resource|get_resources(Type)]}). - --spec store_resource_tuples([resource_tuple()]) -> no_return(). -store_resource_tuples([]) -> - ok; -store_resource_tuples([{_,_}|_] = ResourceTuples) -> - lists:foreach(fun(ResourceTuple) -> - store_resource_tuple(ResourceTuple) - end, ResourceTuples). - -%%----------------------------------------------------------------------- -%% @doc Remove a single resource. -%% @end -%%----------------------------------------------------------------------- --spec delete_resource(resource_tuple()) -> no_return(). -delete_resource({Type, Resource}) -> - ets:insert(?RS, {Type, lists:delete(Resource, get_resources(Type))}). - -%%----------------------------------------------------------------------- -%% @doc Gets resource of a particular type outputs and places it in last position. -%% @end -%%----------------------------------------------------------------------- --spec round_robin_get(resource_type()) -> {ok, resource()} | {error, not_found}. -round_robin_get(Type) -> - case get_resources(Type) of - [Resource|RL] -> - ets:insert(?RS, {Type, RL ++ [Resource]}), - {ok, Resource}; - [] -> - {error, not_found} - end. - -%%----------------------------------------------------------------------- -%% @doc Outputs the number of resource types. -%% @end -%%----------------------------------------------------------------------- -get_num_types() -> - length(ets:match(?RS, {'$1', '_'})). - -%%----------------------------------------------------------------------- -%% @doc Outputs the types of resources. -%% @end -%%----------------------------------------------------------------------- -get_types() -> - [E || [E] <- ets:match(?RS, {'$1', '_'})]. - -%%%===================================================================== -%%% Testing functions -%%%===================================================================== - -get_callback_modules_test() -> - new(), - store_callback_modules([a, b]), - ?assertMatch([a, b], get_callback_modules()), - delete_callback_module(a), - ?assertMatch([b], get_callback_modules()). - -get_resources_test() -> - store_resource_tuples([{a, a}, {a, b}, {b, a}]), - ?assertMatch([{a, a}, {a, b}], get_resources(a)), - delete_resource({a, a}), - ?assertMatch([{a, b}], get_resources(a)). - -get_local_resource_tuples_test() -> - store_local_resource_tuples([{a, a}, {a, b}, {b, a}]), - ?assertMatch([{a, a}, {a, b}, {b, a}], get_local_resource_tuples()), - delete_local_resource({a, a}), - ?assertMatch([{a, b}, {b, a}], get_local_resource_tuples()). - -get_target_types_test() -> - store_target_resource_types([a, b]), - ?assertMatch([a, b], get_target_types()), - delete_target_type(a), - ?assertMatch([b], get_target_types()). - - diff --git a/src/rd_sup.erl b/src/rd_sup.erl deleted file mode 100644 index 15c4720..0000000 --- a/src/rd_sup.erl +++ /dev/null @@ -1,86 +0,0 @@ -%%%------------------------------------------------------------------- -%%% File : rd_sup.erl -%%% Author : Martin J. Logan -%%% @doc The super. -%%%------------------------------------------------------------------- --module(rd_sup). - --behaviour(supervisor). -%%-------------------------------------------------------------------- -%% Include files -%%-------------------------------------------------------------------- - -%%-------------------------------------------------------------------- -%% External exports -%%-------------------------------------------------------------------- --export([ - start_link/1 - ]). - -%%-------------------------------------------------------------------- -%% Internal exports -%%-------------------------------------------------------------------- --export([ - init/1 - ]). - -%%-------------------------------------------------------------------- -%% Macros -%%-------------------------------------------------------------------- --define(SERVER, ?MODULE). --define(HEART, heart). - -%%-------------------------------------------------------------------- -%% Records -%%-------------------------------------------------------------------- - -%%==================================================================== -%% External functions -%%==================================================================== -%%-------------------------------------------------------------------- -%% @doc Starts the supervisor. -%% @spec start_link(StartArgs) -> {ok, Pid} -%% @end -%%-------------------------------------------------------------------- -start_link(_) -> - supervisor:start_link({local, ?SERVER}, ?MODULE, []). - -%%==================================================================== -%% Server functions -%%==================================================================== -%%-------------------------------------------------------------------- -%% @hidden -%% Func: init/1 -%% Returns: {ok, {SupFlags, [ChildSpec]}} | -%% ignore | -%% {error, Reason} -%%-------------------------------------------------------------------- -init([]) -> - RestartStrategy = one_for_one, - MaxRestarts = 1000, - MaxTimeBetRestarts = 3600, - - SupFlags = {RestartStrategy, MaxRestarts, MaxTimeBetRestarts}, - - ChildSpecs = - [ - {rd_core, - {rd_core, start_link, []}, - permanent, - 1000, - worker, - [rd_core]}, - {rd_heartbeat, - {rd_heartbeat, start_link, []}, - transient, - brutal_kill, - worker, - [rd_heartbeat]} - ], - - {ok, {SupFlags, ChildSpecs}}. - - - - - diff --git a/src/resource_discovery.erl b/src/resource_discovery.erl deleted file mode 100644 index bc8e4a2..0000000 --- a/src/resource_discovery.erl +++ /dev/null @@ -1,394 +0,0 @@ -%%%------------------------------------------------------------------- -%%% File : resource_discovery.erl -%%% Author : Martin J. Logan -%%% @doc -%%% -%%% @type resource_tuple() = {resource_type(), resource()}. The type -%%% of a resource followed by the actual resource. Local -%%% resource tuples are communicated to other resource discovery -%%% instances. -%%% @type resource_type() = atom(). The name of a resource, how it is identified. For example -%%% a type of service that you have on the network may be identified by it's node name -%%% in which case you might have a resource type of 'my_service' of which there may be -%%% many node names representing resources such as {my_service, myservicenode@myhost}. -%%% @type resource() = term(). Either a concrete resource or a reference to one like a pid(). -%%% @end -%%%------------------------------------------------------------------- --module(resource_discovery). - -%%-------------------------------------------------------------------- -%% External exports -%%-------------------------------------------------------------------- - -% Standard exports. --export([ - start/2 - ]). - -% Add --export([ - add_local_resource_tuples/1, - add_local_resource_tuple/1, - add_target_resource_types/1, - add_target_resource_type/1, - add_callback_modules/1, - add_callback_module/1 - ]). - -% Get --export([ - get_resource/1, - get_resources/1, - get_num_resource/1, - get_resource_types/0, - get_num_resource_types/0, - get_contact_nodes/0 - ]). - -% Delete --export([ - delete_local_resource_tuple/1, - delete_target_resource_type/1, - delete_resource_tuple/1 - ]). - -% Other --export([ - trade_resources/0, - sync_resources/1, - sync_resources/0, - contact_nodes/0, - rpc_call/4 - ]). - --include("resource_discovery.hrl"). - -%%-------------------------------------------------------------------- -%% Macros -%%-------------------------------------------------------------------- --define(RD, rd_core). - -%%==================================================================== -%% External functions -%%==================================================================== - -%%-------------------------------------------------------------------- -%% @doc Starts the resource discovery application. -%% @spec start(Type, StartArgs) -> {ok, Pid} | {ok, Pid, State} | {error, Reason} -%% @end -%%-------------------------------------------------------------------- -start(_Type, StartArgs) -> - % Create the storage for the local parameters; i.e. LocalTypes - % and TargetTypes. - rd_store:new(), - rd_sup:start_link(StartArgs). - -%%-------------------------------------------------------------------- -%% @doc inform an rd_core server of local resources and target types. -%% This will prompt the remote servers to asyncronously send -%% back remote resource information. -%% @end -%%-------------------------------------------------------------------- --spec trade_resources() -> ok. -trade_resources() -> - rd_core:trade_resources(). - -%%----------------------------------------------------------------------- -%% @doc Syncronizes resources between the caller and the node supplied. -%% Like trade resources but this call blocks. -%% @end -%%----------------------------------------------------------------------- --spec sync_resources(timeout()) -> ok. -sync_resources(Timeout) -> - Self = self(), - Pids = [spawn(fun() -> - Self ! {'$sync_resources$', self(), (catch rd_core:sync_resources(Node))} - end) - || Node <- nodes(known)], - get_responses(Pids, Timeout). - -get_responses([], _Timeout) -> - ok; -get_responses(Pids, Timeout) -> - %% XXX TODO fix the timeout by subracting elapsed time. - %% XXX TODO perhaps use the response. - receive - {'$sync_resources$', Pid, _Resp} -> - NewPids = lists:delete(Pid, Pids), - get_responses(NewPids, Timeout) - after - Timeout -> - {error, timeout} - end. - --spec sync_resources() -> ok. -sync_resources() -> - sync_resources(10000). - -%%------------------------------------------------------------------------------ -%% @doc Adds to the list of target types. Target types are the types -%% of resources that this instance of resource_discovery will cache following -%% a notification of such a resource from a resource_discovery instance. -%% This includes the local instance. -%% @end -%%------------------------------------------------------------------------------ --spec add_target_resource_types([resource_type()]) -> no_return(). -add_target_resource_types([H|_] = TargetTypes) when is_atom(H) -> - rd_store:store_target_resource_types(TargetTypes). - --spec add_target_resource_type(resource_type()) -> no_return(). -add_target_resource_type(TargetType) when is_atom(TargetType) -> - add_target_resource_types([TargetType]). - -%%------------------------------------------------------------------------------ -%% @doc Adds to the list of local resource tuples. -%% @end -%%------------------------------------------------------------------------------ --spec add_local_resource_tuples([resource_tuple()]) -> no_return(). -add_local_resource_tuples([{T,_}|_] = LocalResourceTuples) when is_atom(T) -> - rd_store:store_local_resource_tuples(LocalResourceTuples). - --spec add_local_resource_tuple(resource_tuple()) -> no_return(). -add_local_resource_tuple({T,_} = LocalResourceTuple) when is_atom(T) -> - add_local_resource_tuples([LocalResourceTuple]). - -%%------------------------------------------------------------------------------ -%% @doc Add a callback module or modules to the list of callbacks to be -%% called upon new resources entering the system. -%% @end -%%------------------------------------------------------------------------------ --spec add_callback_modules([atom()]) -> no_return(). -add_callback_modules([H|_] = Modules) when is_atom(H) -> - rd_store:store_callback_modules(Modules). - --spec add_callback_module(atom()) -> no_return(). -add_callback_module(Module) when is_atom(Module) -> - add_callback_modules([Module]). - -%%------------------------------------------------------------------------------ -%% @doc Replies with the cached resource. Round robins though the resources -%% cached. -%% @end -%%------------------------------------------------------------------------------ --spec get_resource(resource_type()) -> {ok, resource()} | {error, no_resources}. -get_resource(Type) when is_atom(Type) -> - rd_core:round_robin_get(Type). - -%%------------------------------------------------------------------------------ -%% @doc Returns ALL cached resources for a particular type. -%% @end -%%------------------------------------------------------------------------------ --spec get_resources(resource_type()) -> [resource()]. -get_resources(Type) -> - rd_store:get_resources(Type). - -%%------------------------------------------------------------------------------ -%% @doc Removes a cached resource from the resource pool. Only returns after the -%% resource has been deleted. -%% @end -%%------------------------------------------------------------------------------ --spec delete_resource_tuple(resource_tuple()) -> ok. -delete_resource_tuple(ResourceTuple = {_,_}) -> - rd_store:delete_resource_tuple(ResourceTuple). - -%%------------------------------------------------------------------------------ -%% @doc Counts the cached instances of a particular resource type. -%% @end -%%------------------------------------------------------------------------------ --spec get_num_resource(resource_type()) -> integer(). -get_num_resource(Type) -> - gen_server:call(?RD, {get_num_resource, Type}). - -%%------------------------------------------------------------------------------ -%% @doc Remove a target type and all associated resources. -%% @end -%%------------------------------------------------------------------------------ --spec delete_target_resource_type(resource_type()) -> true. -delete_target_resource_type(Type) -> - rd_core:delete_target_resource_type(Type). - -%%------------------------------------------------------------------------------ -%% @doc Remove a local resource. The resource will no longer be available for -%% other nodes to discover once this call returns. -%% @end -%%------------------------------------------------------------------------------ --spec delete_local_resource_tuple(resource_tuple()) -> no_return(). -delete_local_resource_tuple(LocalResourceTuple) -> - rd_core:delete_local_resource_tuple(LocalResourceTuple). - -%%------------------------------------------------------------------------------ -%% @doc Gets a list of the types that have resources that have been cached. -%% @end -%%------------------------------------------------------------------------------ --spec get_resource_types() -> [resource_type()]. -get_resource_types() -> - gen_server:call(?RD, get_resource_types). - -%%------------------------------------------------------------------------------ -%% @doc Gets the number of resource types locally cached. -%% @end -%%------------------------------------------------------------------------------ --spec get_num_resource_types() -> integer(). -get_num_resource_types() -> - gen_server:call(?RD, get_num_resource_types). - -%%------------------------------------------------------------------------------ -%% @doc Contacts resource discoveries initial contact node. -%% -%% The initial contact node is specified in configuration with: -%% -%% {contact_nodes, [NodeName]} -%% -%% The config can be overridden by specifying a contact node at the command line -%% like so: -%% -%% -contact_node foo@bar.com -%% -%% -%% @spec contact_nodes(Timeout) -> ok | {error, bad_contact_node} | {error, no_contact_node} -%% where -%% Timeout = Milliseconds::integer() -%% @end -%%------------------------------------------------------------------------------ -contact_nodes(Timeout) -> - {ok, ContactNodes} = - case lists:keysearch(contact_node, 1, init:get_arguments()) of - {value, {contact_node, [I_ContactNode]}} -> - gas:set_env(resource_discovery, contact_nodes, [I_ContactNode]), - {ok, [list_to_atom(I_ContactNode)]}; - _ -> - gas:get_env(resource_discovery, contact_nodes, []) - end, - ping_contact_nodes(ContactNodes, Timeout). - -%% @spec contact_nodes() -> pong | pang | no_contact_node -%% @equiv contact_nodes(10000) -contact_nodes() -> - contact_nodes(10000). - -ping_contact_nodes([], _Timeout) -> - error_logger:info_msg("No contact node specified. Potentially running in a standalone node~n", []), - {error, no_contact_node}; -ping_contact_nodes(Nodes, Timeout) -> - Reply = do_until(fun(Node) -> - case sync_ping(Node, Timeout) of - pong -> - true; - pang -> - error_logger:info_msg("ping contact node at ~p failed~n", [Node]), - false - end - end, - Nodes), - - case Reply of - false -> - {error, bad_contact_node}; - true -> - ok - end. - - -%%------------------------------------------------------------------------------ -%% @doc Get the contact node for the application. -%% @spec get_contact_nodes() -> {ok, Value} | undefined -%% where -%% Value = node() | [node()] -%% @end -%%------------------------------------------------------------------------------ -get_contact_nodes() -> - gas:get_env(resource_discovery, contact_nodes). - -%%------------------------------------------------------------------------------ -%% @doc Execute an rpc on a cached resource. If the result of the rpc is {badrpc, reason} the -%% resource is deleted and the next resource is tried, else the result is -%% returned to the user. -%%
-%% Varibles:
-%%  Type - The resource type to get from resource discovery.
-%% 
-%% @end -%%------------------------------------------------------------------------------ --spec rpc_call(resource_type(), atom(), atom(), [term()]) -> term() | {error, no_resources}. -rpc_call(Type, Module, Function, Args) -> - case get_resource(Type) of - {ok, Resource} -> - io:format("got a resource ~p~n", [Resource]), - case rpc:call(Resource, Module, Function, Args) of - {badrpc, Reason} -> - io:format("got a badrpc ~p~n", [Reason]), - delete_resource_tuple({Type, Resource}), - rpc_call(Type, Module, Function, Args); - Reply -> - io:format("result of rpc was ~p~n", [Reply]), - Reply - end; - {error, no_resources} -> - {error, no_resources} - end. - - -%%---------------------------------------------------------------------------- -%% @private -%% @doc Applies a fun to all elements of a list until getting a non false -%% return value from the passed in fun. -%% @end -%%---------------------------------------------------------------------------- --spec do_until(term(), list()) -> term() | false. -do_until(_F, []) -> - false; -do_until(F, [Last]) -> - F(Last); -do_until(F, [H|T]) -> - case F(H) of - false -> do_until(F, T); - Return -> Return - end. - -%%-------------------------------------------------------------------- -%% @private -%% @doc Pings a node and returns only after the net kernal distributes the nodes. -%% This function will return pang after 10 seconds if the Node is not found in nodes() -%% -%% @end -%%-------------------------------------------------------------------- --spec sync_ping(node(), timeout()) -> pang | pong. -sync_ping(Node, Timeout) -> - case net_adm:ping(Node) of - pong -> - case poll_until(fun() -> lists:member(Node, nodes(known)) end, 500, Timeout / 500) of - true -> pong; - false -> pang - end; - pang -> - pang - end. - -%%-------------------------------------------------------------------- -%% @private -%% @doc This is a higher order function that allows for Iterations -%% number of executions of Fun until false is not returned -%% from the Fun pausing for PauseMS after each execution. -%%
-%% Variables:
-%%  Fun - A fun to execute per iteration.
-%%  Iterations - The maximum number of iterations to try getting Reply out of Fun.  
-%%  PauseMS - The number of miliseconds to wait inbetween each iteration.
-%%  Return - What ever the fun returns.
-%% 
-%% @end -%%-------------------------------------------------------------------- --spec poll_until(term(), timeout(), timeout()) -> term() | false. -poll_until(Fun, 0, _PauseMS) -> - Fun(); -poll_until(Fun, Iterations, PauseMS) -> - case Fun() of - false -> - timer:sleep(PauseMS), - case Iterations of - infinity -> poll_until(Fun, Iterations, PauseMS); - Iterations -> poll_until(Fun, Iterations - 1, PauseMS) - end; - Reply -> - Reply - end.