aboutsummaryrefslogtreecommitdiffstats
path: root/src/util.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/util.erl')
-rw-r--r--src/util.erl192
1 files changed, 192 insertions, 0 deletions
diff --git a/src/util.erl b/src/util.erl
new file mode 100644
index 0000000..d96675b
--- /dev/null
+++ b/src/util.erl
@@ -0,0 +1,192 @@
+% ============LICENSE_START=======================================================
+% org.onap.dcae
+% ================================================================================
+% Copyright (c) 2017 AT&T Intellectual Property. All rights reserved.
+% ================================================================================
+% Licensed under the Apache License, Version 2.0 (the "License");
+% you may not use this file except in compliance with the License.
+% You may obtain a copy of the License at
+%
+% http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS,
+% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+% See the License for the specific language governing permissions and
+% limitations under the License.
+% ============LICENSE_END=========================================================
+%
+% ECOMP is a trademark and service mark of AT&T Intellectual Property.
+
+-module(util).
+-include("application.hrl").
+
+-export([concat/1,
+ get_platform_envs_and_config/0,
+ resolve_cbs/2,
+ initialize_database/0,
+ get_all_appnames_from_db/0,
+ get_my_version/0,
+ get_programs_for_pfapp_from_db/1,
+ gen_uuid/0,
+ iso/0,
+ iso_elapsed/2,
+ to_str/1,
+ ip_to_str/1,
+ update_with_new_config_map/2,
+ ejson_to_map/1
+ ]).
+
+%http://stackoverflow.com/questions/39757020/erlang-drying-up-stringbinary-concatenation
+%NOTE! Does not work or bomb when an element in the list is an atom. Must be a string or binary. Maybe add a check for this
+to_string(Value) when is_binary(Value) -> binary_to_list(Value);
+to_string(Value) -> Value.
+concat(List) ->
+ lists:flatten(lists:map(fun to_string/1, List)).
+
+resolve_cbs(XER, ConsulURL) ->
+ %Ideally this function would dissapear if we get real DNS. This essentially is doing an SRV record lookup every time someone needs the bindng URL
+ %This allows the broker to handle the case where the CBS moves IP or Ports
+ %New as of 6/28/17: Uses the hardcoded short name for the CBS
+ {IP, Port} = consul_interface:consul_get_service_ip_port(XER, "config_binding_service", ConsulURL),
+ concat(["http://", IP, ":", integer_to_binary(Port)]).
+
+get_platform_envs_and_config() ->
+ %Get platform envs needed for broker operation, then fetch my config.
+ %If something critical fails, returns [], else [ConsulURL, CDAPUrl, BoundConfigMap]
+ MyName = os:getenv("HOSTNAME"),
+ ConsulHost = os:getenv("CONSUL_HOST"),
+ case MyName == false orelse ConsulHost == false of
+ true -> [];
+ false ->
+ %build Consul URL
+ ConsulURL = concat(["http://", ConsulHost, ":8500"]),
+
+ %Bind my own config map
+ %generate my own XER here
+ XER = gen_uuid(),
+ {200, BoundConfig} = consul_interface:consul_bind_config(XER, MyName, ConsulURL),
+ BoundConfigMap = jiffy:decode(jiffy:encode(BoundConfig), [return_maps]), %kind of an interesting way to turn an erlang proplist into a map
+
+ %Here, we waterfall looking for "CDAP_CLUSTER_TO_MANAGE".
+ %First, we will check for environmnental variables for a cluster *NAME*
+ %If that is not found, then we will check out bound config for a fully bound URL
+ %If that is also not found, let it crash baby.
+ CDAPURL = case os:getenv("CDAP_CLUSTER_TO_MANAGE") of
+ false ->
+ list_to_binary(concat(["http://", lists:nth(1, maps:get(<<"cdap_cluster_to_manage">>, BoundConfigMap))])); %cbs returns ip:port. need http:// or will get "no adaptors found" error
+ CDAPName ->
+ {IP, Port} = consul_interface:consul_get_service_ip_port(XER, CDAPName, ConsulURL),
+ list_to_binary(concat(["http://", IP, ":", integer_to_binary(Port)]))
+ end,
+ [MyName, ConsulURL, CDAPURL, BoundConfigMap]
+ end.
+
+initialize_database() ->
+ %Create the database (currently MNesia) if it does not exist, and the application table.
+ %Or, do nothing.
+ N = node(),
+ lager:info(io_lib:format("Initializing database. My node name is ~s", [N])),
+
+ %set MNesia dir
+ application:set_env(mnesia, dir, "/var/mnesia/"),
+
+ %stop if running, can't create schema if it is. Dont check status, OK if stopped
+ mnesia:stop(),
+
+ %create the schema if it does not already exist. Dont check status, ok if exists
+ %erlang:display(mnesia:delete_schema([N])),
+ mnesia:create_schema([N]),
+ %start MNesia, assert it works
+
+ ok = mnesia:start(), %start MNesia, bomb if alreay started, should not happen
+ lager:info("Mnesia started"),
+
+ %try to create the table, or if it exists, do nothing
+ %erlang:display(mnesia:delete_table(application)),
+ case mnesia:create_table(application, [{attributes, record_info(fields, application)}, {disc_copies, [N]}]) of
+ {aborted,{already_exists,application}} ->
+ lager:info("Application table already exists");
+ {atomic,ok} ->
+ lager:info(io_lib:format("Created application table on ~s", [N]))
+ end,
+
+ %try to create the app supplementaty table, or if it exists, do nothing
+ %erlang:display(mnesia:delete_table(application)),
+ case mnesia:create_table(prog_flow_supp, [{attributes, record_info(fields, prog_flow_supp)}, {disc_copies, [N]}]) of
+ {aborted,{already_exists, prog_flow_supp}} ->
+ lager:info("prog_flow_supp table already exists");
+ {atomic,ok} ->
+ lager:info(io_lib:format("Created prog_flow_supp table on ~s", [N]))
+ end,
+
+ %wait up to 30s for the table to come up. Usually instantaneous. If it takes more crash abd burn
+ ok = mnesia:wait_for_tables([application, prog_flow_supp], 30000),
+ ok.
+
+get_all_appnames_from_db() ->
+ {atomic, Apps} = mnesia:transaction(fun() -> mnesia:match_object(application, #application{_ = '_'}, read) end),
+ lists:map(fun(X) -> {application, Appname,_,_,_,_,_,_,_,_} = X,
+ Appname
+ end, Apps).
+
+-spec get_programs_for_pfapp_from_db(binary()) -> lprogram().
+get_programs_for_pfapp_from_db(Appname) ->
+ {atomic, [#prog_flow_supp{appname = Appname, programs=Programs}]} = mnesia:transaction(fun() -> mnesia:match_object(prog_flow_supp, #prog_flow_supp{appname = Appname, _ = '_'}, read) end),
+ Programs.
+
+get_my_version() ->
+ %stolen from the SO post I asked about: http://stackoverflow.com/questions/43147530/erlang-programmatically-get-application-version/43152182#43152182
+ case lists:keyfind(cdapbroker, 1, application:loaded_applications()) of
+ {_, _, Ver} -> list_to_binary(Ver);
+ false -> <<"error">>
+ end.
+
+gen_uuid() ->
+ %generate an RFC compliant v1 uuid using lib
+ uuid:to_string(uuid:uuid1()).
+
+iso() ->
+ %generate 8601 ts
+ iso8601:format(erlang:timestamp()).
+
+iso_elapsed(Endtime, Starttime) ->
+ %%%...subtract two isos and return the number of seconds elapsed between Starttime and Endtime
+ Edt = iso8601:parse(Endtime),
+ Sdt = iso8601:parse(Starttime),
+ Egs = calendar:datetime_to_gregorian_seconds(Edt),
+ Sgs = calendar:datetime_to_gregorian_seconds(Sdt),
+ Egs - Sgs.
+
+to_str("") -> "";
+to_str(Term) -> lists:flatten(io_lib:format("~p", [Term])).
+
+-spec ip_to_str({inet:ip_address(), inet:port_number()}) -> binary().
+%nasty.. I miss pythons x <= Foo <= Y syntax.. or something mathematical like Foo in [X..Y].. erlang not good 4 math
+ip_to_str({{A,B,C,D}, Port}) when A >= 0 andalso A =< 255 andalso B >= 0 andalso B =< 255 andalso C >= 0 andalso C =< 255 andalso D >= 0 andalso D =< 255 andalso port >= 0 andalso Port =<65535 ->
+ concat([to_str(A),".",to_str(B),".", to_str(C),".",to_str(D),":",to_str(Port)]);
+ip_to_str({_,_}) -> invalid.
+
+update_with_new_config_map(NewConfig, OldConfig) ->
+ %helper for smart_reconfigure, broken out so we can unit test it.
+ %
+ %Takes in a new config, some keys in which may be shared with OldConfig, and returns a new map with the same keys as OldConfig, except values that had overlap were replaced by NewConfig
+ %if no keys in NewConfig overlap with OldConfig, returns the atom 'nooverlap'
+ %
+ %This is very similar to the maps:merge/2 builtin but that will inject keys of newconfig that were not in oldconfig. We need a "RIGHT JOIN"
+ NCKeys = maps:keys(NewConfig),
+ ConfigOverlaps = [X || X <- NCKeys, maps:is_key(X, OldConfig)],
+ case ConfigOverlaps of
+ [] -> nooverlap;
+ _ ->
+ %we have an entry that should be in app config
+ %build a new map with just the keys to update
+ Pred = fun(X,_) -> lists:member(X, ConfigOverlaps) end,
+ NewVals = maps:filter(Pred, NewConfig),
+ maps:merge(OldConfig, NewVals)
+ end.
+
+ejson_to_map(E) ->
+ %takes the jiffy "ejson: format of {[{<<"foo">>, <<"bar">>}, {<<"foo2">>, <<"bar2">>}]} and turns it into a map,
+ %usefu because ejsons do not appear to be order-independent-comparable, but maps are (e.g., two maps are equal if all their k+v are equal but agnostic to order)
+ jiffy:decode(jiffy:encode(E), [return_maps]).