initial import of webmachine-based api
This commit is contained in:
parent
6a9d4d3f62
commit
6d907016dd
|
@ -0,0 +1,6 @@
|
|||
% -*- mode: erlang -*-
|
||||
{["src/*"],
|
||||
[{i, "include"},
|
||||
{outdir, "ebin"},
|
||||
debug_info]
|
||||
}.
|
|
@ -0,0 +1,23 @@
|
|||
ERL ?= erl
|
||||
EBIN_DIRS := $(wildcard deps/*/ebin)
|
||||
APP := flukso
|
||||
|
||||
all: erl ebin/$(APP).app erlrrd
|
||||
|
||||
erl:
|
||||
@$(ERL) -pa $(EBIN_DIRS) -noinput +B \
|
||||
-eval 'case make:all() of up_to_date -> halt(0); error -> halt(1) end.'
|
||||
|
||||
docs:
|
||||
@erl -noshell -run edoc_run application '$(APP)' '"."' '[]'
|
||||
|
||||
clean:
|
||||
@echo "removing:"
|
||||
@rm -fv ebin/*.beam ebin/*.app
|
||||
|
||||
ebin/$(APP).app: src/$(APP).app
|
||||
@cp -v src/$(APP).app $@
|
||||
|
||||
erlrrd:
|
||||
@(cd deps/erlrrd;$(MAKE))
|
||||
|
|
@ -0,0 +1 @@
|
|||
fess <fess-erlrrd@fess.org>
|
|
@ -0,0 +1,10 @@
|
|||
Version 0.3.0
|
||||
* enable a timeout env_var for setting the port timeout
|
||||
* change app spec to use a port time out of 30s.
|
||||
|
||||
Version 0.2.2
|
||||
* catch port exit and exit the gen_server
|
||||
|
||||
VERSION 0.2.1
|
||||
* rebuild with latest framewerk to get erlrc hooks
|
||||
* specify dependency on rrdtool
|
|
@ -0,0 +1,11 @@
|
|||
all:
|
||||
(cd src;$(MAKE) all)
|
||||
|
||||
edoc:
|
||||
(cd src;$(MAKE) edoc)
|
||||
|
||||
test:
|
||||
(cd src;$(MAKE) test)
|
||||
|
||||
clean:
|
||||
(cd src;$(MAKE) clean)
|
|
@ -0,0 +1 @@
|
|||
# put whatever (auto)make commands here, they will be included from Makefile.am
|
|
@ -0,0 +1,9 @@
|
|||
|
||||
This module provides a gen_server controlling a Port to "rrdtool -"
|
||||
|
||||
rrdtool is a very nice graphing system as well as logger for time series data.
|
||||
for more info on rrdtool check out:
|
||||
|
||||
http://oss.oetiker.ch/rrdtool/
|
||||
|
||||
for more info on this, the erlrrd module see the doc directory.
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
if you're trying to build this package from the source repository
|
||||
ie: not from a dist tarball you'll need to understand the "Framewerk
|
||||
build system", and the usual autoconf, automake, make voodoo. Framewerk
|
||||
is just more voodoo on top of that to make things "easier".
|
|
@ -0,0 +1,17 @@
|
|||
#! /bin/sh
|
||||
|
||||
if test -d fw/bin
|
||||
then
|
||||
PATH="`pwd`/fw/bin:$PATH"
|
||||
export PATH
|
||||
fi
|
||||
|
||||
fwb=`which fw-bootstrap`
|
||||
|
||||
if test -z "$fwb"
|
||||
then
|
||||
echo "bootstrap: fatal: fw-bootstrap not installed or not in PATH" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
"$fwb" --fw_version "0.1.2" --name erlrrd --template erlang --revision svn --svn_project_path https://erlrrd.googlecode.com/svn/trunk/erlrrd "$@"
|
|
@ -0,0 +1,5 @@
|
|||
dnl -- include additional autoconf commands here
|
||||
dnl -- do not include AC_OUTPUT, this is called for you
|
||||
|
||||
|
||||
AC_CONFIG_FILES([doc/overview.edoc])
|
|
@ -0,0 +1 @@
|
|||
# put whatever (auto)make commands here, they will be included from Makefile.am
|
|
@ -0,0 +1,9 @@
|
|||
@author @FW_PACKAGE_MAINTAINER@
|
||||
@copyright 2007
|
||||
@version @VERSION@
|
||||
@title erlrrd - erlang bindings for rrdtool
|
||||
@doc erlrrd is a gen_server controlling an erlang Port running the command "rrdtool -"
|
||||
it provides functions for each of the
|
||||
[http://oss.oetiker.ch/rrdtool/index.en.html rrdtool]
|
||||
commands with
|
||||
a bit of error checking to make sure the communication doesn't hang.
|
|
@ -0,0 +1,12 @@
|
|||
{application, erlrrd,
|
||||
[{description, "erlang rrdtool port"},
|
||||
{vsn, "1.0"},
|
||||
{modules, [
|
||||
erlrrd,
|
||||
erlrrd_app,
|
||||
erlrrd_sup
|
||||
]},
|
||||
{registered, [erlrrd, erlrrd_sup]},
|
||||
{mod, {erlrrd_app, []}},
|
||||
{env, []},
|
||||
{applications, [kernel, stdlib]}]}.
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
# put whatever (auto)make commands here, they will be included from Makefile.am
|
|
@ -0,0 +1,30 @@
|
|||
# The FW_PACKAGE_MAINTAINER field is populated with the
|
||||
# environment variable FW_PACKAGE_DEFAULT_MAINTAINER if non-empty
|
||||
|
||||
FW_PACKAGE_NAME="erlrrd"
|
||||
FW_PACKAGE_VERSION="0.3.0"
|
||||
FW_PACKAGE_MAINTAINER="fess <fess-erlrrd@fess.org>"
|
||||
FW_PACKAGE_SHORT_DESCRIPTION="rrdtool binding for erlang. [ via an erlang Port. ]"
|
||||
FW_PACKAGE_DESCRIPTION="`cat README`"
|
||||
FW_PACKAGE_ARCHITECTURE_DEPENDENT="0"
|
||||
|
||||
FW_SUBVERSION_TAG_ROOT="https://erlrrd.googlecode.com/svn/tags/"
|
||||
|
||||
# Dependency information. The native syntax corresponds to Debian,
|
||||
# http://www.debian.org/doc/debian-policy/ch-relationships.html
|
||||
# Section 7.1 "Syntax of Relationship Fields"
|
||||
#
|
||||
# For other packaging systems, the syntax is translated for you.
|
||||
|
||||
FW_PACKAGE_DEPENDS="rrdtool"
|
||||
FW_PACKAGE_CONFLICTS=""
|
||||
FW_PACKAGE_PROVIDES=""
|
||||
FW_PACKAGE_REPLACES=""
|
||||
|
||||
FW_PACKAGE_BUILD_DEPENDS="eunit"
|
||||
FW_PACKAGE_BUILD_CONFLICTS=""
|
||||
|
||||
FW_ERL_APP_ENVIRONMENT="[
|
||||
{rrdtoolcmd, \"rrdtool -\"},
|
||||
{timeout, 30000 }
|
||||
]"
|
|
@ -0,0 +1,9 @@
|
|||
#! /bin/sh
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
# post-install
|
||||
#
|
||||
# Executed after the package is installed.
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
exit 0
|
|
@ -0,0 +1,9 @@
|
|||
#! /bin/sh
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
# post-remove
|
||||
#
|
||||
# Executed after the package is removed.
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
exit 0
|
|
@ -0,0 +1,9 @@
|
|||
#! /bin/sh
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
# pre-install
|
||||
#
|
||||
# Executed before the package is installed.
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
exit 0
|
|
@ -0,0 +1,9 @@
|
|||
#! /bin/sh
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
# pre-remove
|
||||
#
|
||||
# Executed before the package is removed.
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
exit 0
|
|
@ -0,0 +1,10 @@
|
|||
#! /bin/sh
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
# start
|
||||
#
|
||||
# Executed when the package (service) is started up.
|
||||
# Not supported by all package formats.
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
exit 0
|
|
@ -0,0 +1,10 @@
|
|||
#! /bin/sh
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
# start
|
||||
#
|
||||
# Executed when the package (service) is shut down.
|
||||
# Not supported by all package formats.
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
exit 0
|
|
@ -0,0 +1,20 @@
|
|||
include ../support/include.mk
|
||||
|
||||
APPLICATION=erlrrd
|
||||
DOC_OPTS={dir,\"../doc\"}
|
||||
|
||||
all: $(EBIN_FILES)
|
||||
|
||||
debug:
|
||||
$(MAKE) DEBUG=-DDEBUG
|
||||
|
||||
clean:
|
||||
rm -rf $(EBIN_FILES)
|
||||
|
||||
edoc:
|
||||
$(ERL) -noshell -pa ../ebin \
|
||||
-eval "edoc:application($(APPLICATION), \".\", [$(DOC_OPTS)])" \
|
||||
-s init stop
|
||||
|
||||
test: all
|
||||
$(ERL) -noshell -pa ../ebin -s $(APPLICATION) test -s init stop
|
|
@ -0,0 +1,16 @@
|
|||
# put whatever (auto)make commands here, they will be included from Makefile.am
|
||||
|
||||
DIALYZERFLAGS = -Wno_improper_lists
|
||||
|
||||
dist_erlappsrc_DATA = \
|
||||
$(wildcard *.erl)
|
||||
|
||||
dist_erlappinclude_DATA = \
|
||||
$(wildcard *.hrl)
|
||||
|
||||
erlappebin_SCRIPTS = \
|
||||
@FW_PACKAGE_NAME@.app \
|
||||
$(patsubst %.erl, %.beam, $(dist_erlappsrc_DATA))
|
||||
|
||||
check_DATA = \
|
||||
.dialyzer_ok
|
|
@ -0,0 +1,12 @@
|
|||
{application, erlrrd,
|
||||
[{description, "erlang rrdtool port"},
|
||||
{vsn, "1.0"},
|
||||
{modules, [
|
||||
erlrrd,
|
||||
erlrrd_app,
|
||||
erlrrd_sup
|
||||
]},
|
||||
{registered, [erlrrd, erlrrd_sup]},
|
||||
{mod, {erlrrd_app, []}},
|
||||
{env, []},
|
||||
{applications, [kernel, stdlib]}]}.
|
|
@ -0,0 +1,865 @@
|
|||
-module(erlrrd).
|
||||
-include_lib ("eunit/include/eunit.hrl").
|
||||
|
||||
-export([create/1, update/1, updatev/1, dump/1, restore/1, last/1,
|
||||
first/1, info/1, fetch/1, tune/1, resize/1, xport/1,
|
||||
graph/1, lastupdate/1, ls/0, cd/1, mkdir/1, pwd/0
|
||||
]).
|
||||
|
||||
-export([start_link/1, start_link/0]).
|
||||
-export([start/0]).
|
||||
-export([stop/0]).
|
||||
-export([combine/1, c/1]).
|
||||
|
||||
-behaviour(gen_server).
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-record( state2, { port, timeout } ).
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%% Public
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
|
||||
%% @equiv erlrrd_app:start()
|
||||
start() -> erlrrd_app:start().
|
||||
%% @equiv erlrrd_app:stop()
|
||||
stop() -> erlrrd_app:stop().
|
||||
|
||||
%% @spec start_link(RRDToolCmd) -> Result
|
||||
%% RRDToolCmd = string()
|
||||
%% Result = {ok,Pid} | ignore | {error,Error}
|
||||
%% Pid = pid()
|
||||
%% Error = {already_started,Pid} | shutdown | term()
|
||||
%% @doc calls gen_server:start_link
|
||||
%% RRDToolCmd is the command passed to open_port()
|
||||
%% usually "rrdtool -"
|
||||
start_link(RRDToolCmd) when is_list(RRDToolCmd) ->
|
||||
application:set_env(erlrrd, rrdtoolcmd, RRDToolCmd ),
|
||||
start_link().
|
||||
|
||||
%% @equiv start_link("rrdtool -")
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
||||
|
||||
%% @spec combine(List) -> List
|
||||
%% List = [ term() ]
|
||||
%% @doc "joins" and quotes the given arg list.
|
||||
%% takes a list of arguments, and returns a deeplist with
|
||||
%% each argument surrounded by double quotes
|
||||
%% then separated by spaces. Note
|
||||
%% it does not try to escape any double quotes
|
||||
%% in the arguments.
|
||||
%%
|
||||
%% combine(["these", "are", "my args"]). ->
|
||||
%%
|
||||
%% [["\"","these","\""]," ",["\"","are","\""]," ",["\"","my args","\""]]
|
||||
%%
|
||||
%% it is intended as a convinence function to the
|
||||
%% rrdtool commands which all take a single iodata() argument
|
||||
%% which represents the string to be passed as the arguments
|
||||
%% to the corresponding rrdtool command.
|
||||
%%
|
||||
%% erlrrd:xport(erlrrd:c(["DEF:foo=/path with/space/foo.rrd:foo:AVERAGE", "XPORT:foo"])).
|
||||
combine(Args) ->
|
||||
join([ [ "\"", X, "\"" ] || X <- Args ], " ").
|
||||
%% @spec c(List) -> List
|
||||
%% List = [ term() ]
|
||||
% @equiv combine(Args)
|
||||
c(Args) -> combine(Args).
|
||||
|
||||
|
||||
% rrdtool commands
|
||||
|
||||
%% @spec create(erlang:iodata()) -> { ok, Response } |
|
||||
%% { error, Reason }
|
||||
%% Reason = iolist()
|
||||
%% Response = iolist()
|
||||
%% @doc Set up a new Round Robin Database (RRD). Check
|
||||
%% [http://oss.oetiker.ch/rrdtool/doc/rrdcreate.en.html rrdcreate].
|
||||
create (Args) when is_list(Args) -> do(create, Args).
|
||||
|
||||
%% @spec update(erlang:iodata()) -> { ok, Response } |
|
||||
%% { error, Reason }
|
||||
%% Reason = iolist()
|
||||
%% Response = iolist()
|
||||
%% @doc Store new data values into an RRD. Check
|
||||
%% [http://oss.oetiker.ch/rrdtool/doc/rrdupdate.en.html rrdupdate].
|
||||
update (Args) when is_list(Args) -> do(update, Args).
|
||||
|
||||
%% @spec updatev(erlang:iodata()) -> { ok, Response } |
|
||||
%% { error, Reason }
|
||||
%% Reason = iolist()
|
||||
%% Response = iolist()
|
||||
%% @doc Operationally equivalent to update except for output. Check
|
||||
%% [http://oss.oetiker.ch/rrdtool/doc/rrdupdate.en.html rrdupdate].
|
||||
updatev (Args) when is_list(Args) -> do(updatev, Args).
|
||||
|
||||
%% @spec dump(erlang:iodata()) -> { ok, Response } |
|
||||
%% { error, Reason }
|
||||
%% Reason = iolist()
|
||||
%% Response = iolist()
|
||||
%% @doc Dump the contents of an RRD in plain ASCII. In connection with
|
||||
%% restore you can use this to move an RRD from one computer
|
||||
%% architecture to another. Check
|
||||
%% [http://oss.oetiker.ch/rrdtool/doc/rrddump.en.html rrddump].
|
||||
dump (Args) when is_list(Args) -> do(dump, Args).
|
||||
|
||||
%% @spec restore(erlang:iodata()) -> { ok, Response } |
|
||||
%% { error, Reason }
|
||||
%% Reason = iolist()
|
||||
%% Response = iolist()
|
||||
%% @doc Restore an RRD in XML format to a binary RRD. Check
|
||||
%% [http://oss.oetiker.ch/rrdtool/doc/rrdrestore.en.html rrdrestore]
|
||||
restore (Args) when is_list(Args) -> do(restore, Args).
|
||||
|
||||
%% @spec last(erlang:iodata()) -> { ok, Response } |
|
||||
%% { error, Reason }
|
||||
%% Reason = iolist()
|
||||
%% Response = integer()
|
||||
%% @doc Return the date of the last data sample in an RRD. Check
|
||||
%% [http://oss.oetiker.ch/rrdtool/doc/rrdlast.en.html rrdlast]
|
||||
last (Args) when is_list(Args) ->
|
||||
case do(last, Args) of
|
||||
{ error, Reason } -> { error, Reason };
|
||||
{ ok, [[Response]] } -> { ok, erlang:list_to_integer(Response) }
|
||||
end.
|
||||
|
||||
%% @spec lastupdate(erlang:iodata()) -> { ok, Response } |
|
||||
%% { error, Reason }
|
||||
%% Reason = iolist()
|
||||
%% Response = iolist()
|
||||
%% @doc Return the most recent update to an RRD. Check
|
||||
%% [http://oss.oetiker.ch/rrdtool/doc/rrdlastupdate.en.html rrdlastupdate]
|
||||
lastupdate (Args) when is_list(Args) -> do(lastupdate, Args).
|
||||
|
||||
%% @spec first(erlang:iodata()) -> { ok, Response } |
|
||||
%% { error, Reason }
|
||||
%% Reason = iolist()
|
||||
%% Response = integer()
|
||||
%% @doc Return the date of the first data sample in an RRA within an
|
||||
%% RRD. Check
|
||||
%% [http://oss.oetiker.ch/rrdtool/doc/rrdfirst.en.html rrdfirst]
|
||||
first (Args) when is_list(Args) ->
|
||||
case do(first, Args) of
|
||||
{ error, Reason } -> { error, Reason };
|
||||
{ ok, [[Response]] } -> { ok, erlang:list_to_integer(Response) }
|
||||
end.
|
||||
|
||||
%% @spec info(erlang:iodata()) -> { ok, Response } |
|
||||
%% { error, Reason }
|
||||
%% Reason = iolist()
|
||||
%% Response = iolist()
|
||||
%% @doc Get information about an RRD. Check
|
||||
%% [http://oss.oetiker.ch/rrdtool/doc/rrdinfo.en.html rrdinfo].
|
||||
info (Args) when is_list(Args) -> do(info, Args).
|
||||
|
||||
%% @spec fetch(erlang:iodata()) -> { ok, Response } |
|
||||
%% { error, Reason }
|
||||
%% Reason = iolist()
|
||||
%% Response = iolist()
|
||||
%% @doc Get data for a certain time period from a RRD. The graph func-
|
||||
%% tion uses fetch to retrieve its data from an RRD. Check
|
||||
%% [http://oss.oetiker.ch/rrdtool/doc/rrdfetch.en.html rrdfetch].
|
||||
fetch (Args) when is_list(Args) -> do(fetch, Args).
|
||||
|
||||
%% @spec tune(erlang:iodata()) -> { ok, Response } |
|
||||
%% { error, Reason }
|
||||
%% Reason = iolist()
|
||||
%% Response = iolist()
|
||||
%% @doc Alter setup of an RRD. Check
|
||||
%% [http://oss.oetiker.ch/rrdtool/doc/rrdtune.en.html rrdtune].
|
||||
tune (Args) when is_list(Args) -> do(tune, Args).
|
||||
|
||||
%% @spec resize(erlang:iodata()) -> { ok, Response } |
|
||||
%% { error, Reason }
|
||||
%% Reason = iolist()
|
||||
%% Response = iolist()
|
||||
%% @doc Change the size of individual RRAs. This is dangerous! Check
|
||||
%% rrdresize.
|
||||
resize (Args) when is_list(Args) -> do(resize, Args).
|
||||
|
||||
%% @spec xport(erlang:iodata()) -> { ok, Response } |
|
||||
%% { error, Reason }
|
||||
%% Reason = iolist()
|
||||
%% Response = iolist()
|
||||
%% @doc Export data retrieved from one or several RRDs. Check
|
||||
%% [http://oss.oetiker.ch/rrdtool/doc/rrdxport.en.html rrdxport]
|
||||
%%
|
||||
%% erlrrd:xport("'DEF:foo=/path with/space/foo.rrd:foo:AVERAGE' XPORT:foo").
|
||||
%%
|
||||
%% erlrrd:xport(erlrrd:c(["DEF:foo=/path with/space/foo.rrd:foo:AVERAGE", "XPORT:foo"])).
|
||||
xport (Args) when is_list(Args) -> do(xport, Args).
|
||||
|
||||
%% @spec graph(erlang:iodata()) -> { ok, Response } |
|
||||
%% { error, Reason }
|
||||
%% Reason = iolist()
|
||||
%% Response = iolist()
|
||||
%% @doc Create a graph from data stored in one or several RRDs. Apart
|
||||
%% from generating graphs, data can also be extracted to stdout.
|
||||
%% Check
|
||||
%% [http://oss.oetiker.ch/rrdtool/doc/rrdgraph.en.html rrdgraph].
|
||||
graph (Args) when is_list(Args) ->
|
||||
% TODO: scan for this pattern w/out flattening the io_list?
|
||||
% TODO: support graphing to stdout!!! :)
|
||||
Flat = erlang:binary_to_list(erlang:iolist_to_binary(Args)),
|
||||
case regexp:match(Flat, "(^| )-( |$)") of
|
||||
{ match, _, _ } ->
|
||||
% graph to stdout will break this Ports parsing of reponses..
|
||||
{ error, "Graphing to stdout not supported." };
|
||||
nomatch ->
|
||||
do(graph, Args)
|
||||
end.
|
||||
|
||||
%%%%% rrd "remote" commands %%%%
|
||||
|
||||
%% @spec cd(erlang:iodata()) -> ok |
|
||||
%% { error, Reason }
|
||||
%% Reason = iolist()
|
||||
%% @doc ask the rrdtool unix process to change directories
|
||||
%%
|
||||
%% erlrrd:cd("/usr/share/rrd/data").
|
||||
%%
|
||||
%% erlrrd:cd(erlrrd:combine(["/Users/foo/Library/Application Support/myapp/rrd"]).
|
||||
cd (Arg) when is_list(Arg) ->
|
||||
case do(cd, Arg) of
|
||||
{ error, Reason } -> { error, Reason };
|
||||
{ ok, _ } -> ok
|
||||
end.
|
||||
|
||||
%% @spec mkdir(erlang:iodata()) -> { ok, Response } |
|
||||
%% { error, Reason }
|
||||
%% Reason = iolist()
|
||||
%% Response = iolist()
|
||||
%% @doc ask the rrdtool unix process to create a directory
|
||||
mkdir (Arg) when is_list(Arg) -> do(mkdir, Arg).
|
||||
|
||||
%% @spec ls() -> { ok, Response } | { error, Reason }
|
||||
%% Reason = iolist()
|
||||
%% Response = iolist()
|
||||
%% @doc lists all *.rrd files in rrdtool unix process'
|
||||
%% current working directory
|
||||
ls () -> do(ls, [] ).
|
||||
|
||||
%% @spec pwd() -> { ok, Response } | { error, Reason }
|
||||
%% Reason = iolist()
|
||||
%% Response = string()
|
||||
%% @doc return the rrdtool unix process'
|
||||
%% current working directory.
|
||||
pwd () ->
|
||||
case do(pwd, []) of
|
||||
{ error, Reason } -> { error, Reason };
|
||||
{ ok, [[Response]] } -> { ok, Response }
|
||||
end.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%% Gen server interface poo
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
%% @hidden
|
||||
init([]) ->
|
||||
RRDToolCmd = case application:get_env(erlrrd, rrdtoolcmd) of
|
||||
{ ok, Cmd } -> Cmd;
|
||||
undefined -> "rrdtool -"
|
||||
end,
|
||||
Timeout = case application:get_env(erlrrd, timeout) of
|
||||
{ ok, T } -> T;
|
||||
undefined -> 3000
|
||||
end,
|
||||
process_flag(trap_exit, true),
|
||||
Port = erlang:open_port(
|
||||
{spawn, RRDToolCmd},
|
||||
[ {line, 10000}, eof, exit_status, stream ]
|
||||
),
|
||||
{ok, #state2{port = Port, timeout = Timeout}}.
|
||||
|
||||
%% handle_call
|
||||
%% @hidden
|
||||
handle_call(
|
||||
{do, Action, Args },
|
||||
_From,
|
||||
#state2{port = Port, timeout = Timeout } = State
|
||||
) ->
|
||||
Line = [ erlang:atom_to_list(Action), " ", Args , "\n"],
|
||||
port_command(Port, Line),
|
||||
case collect_response(Port, Timeout) of
|
||||
{response, Response} ->
|
||||
{reply, { ok, Response }, State};
|
||||
{ error, timeout } ->
|
||||
{stop, port_timeout, State};
|
||||
{ error, Error } ->
|
||||
{reply, { error, Error }, State}
|
||||
end.
|
||||
|
||||
%% handle_cast
|
||||
%% @hidden
|
||||
handle_cast(_Msg, State) ->
|
||||
% io:format(user, "Got unexpected cast msg: ~p~n", [Msg]),
|
||||
%% TODO error/event loging in erlang style.
|
||||
{noreply, State}.
|
||||
|
||||
%% handle_info
|
||||
%% @hidden
|
||||
handle_info({ Port , {exit_status, Status}}, State)
|
||||
when
|
||||
Port =:= State#state2.port
|
||||
->
|
||||
{ stop, { port_exit, Status }, State};
|
||||
|
||||
handle_info(_Msg, State) ->
|
||||
% io:format(user, "Got unexpected info msg: ~p~n", [Msg]),
|
||||
%% TODO error/event loging in erlang style.
|
||||
{noreply, State}.
|
||||
|
||||
%% terminate
|
||||
%% @hidden
|
||||
terminate(_Reason, _State) -> ok.
|
||||
|
||||
%% code_change
|
||||
%% @hidden
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
% Private poo
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
do(Command, Args) ->
|
||||
case has_newline(Args) of
|
||||
true -> { error, "No newlines" };
|
||||
false -> gen_server:call (?MODULE, { do, Command, Args } )
|
||||
end.
|
||||
|
||||
join([Head | [] ], _Sep) ->
|
||||
[Head];
|
||||
join([Head | Tail], Sep) ->
|
||||
[ Head, Sep | join(Tail, Sep) ].
|
||||
|
||||
has_newline([]) -> false;
|
||||
has_newline(<<>>) -> false;
|
||||
has_newline([ H | T])
|
||||
when is_list(H); is_binary(H) ->
|
||||
case has_newline(H) of
|
||||
true -> true;
|
||||
false -> has_newline(T)
|
||||
end;
|
||||
has_newline([ H | T]) when is_integer(H) ->
|
||||
if
|
||||
H =:= $\n -> true;
|
||||
true -> has_newline(T)
|
||||
end;
|
||||
has_newline(<<H:8,T/binary>>) ->
|
||||
if
|
||||
H =:= $\n -> true;
|
||||
true -> has_newline(T)
|
||||
end.
|
||||
|
||||
|
||||
collect_response(Port, Timeout ) ->
|
||||
collect_response(Port, [], [], Timeout ).
|
||||
|
||||
collect_response( Port, RespAcc, LineAcc, Timeout) ->
|
||||
receive
|
||||
{Port, {data, {eol, "OK u:" ++ _T }}} ->
|
||||
{response, lists:reverse(RespAcc)};
|
||||
{Port, {data, {eol, "ERROR: " ++ Error }}} ->
|
||||
{error, [ Error, lists:reverse(RespAcc)]};
|
||||
{Port, {data, {eol, Result}}} ->
|
||||
Line = lists:reverse([Result | LineAcc]),
|
||||
collect_response(Port, [Line | RespAcc], [], Timeout);
|
||||
{Port, {data, {noeol, Result}}} ->
|
||||
collect_response(Port, RespAcc, [Result | LineAcc], Timeout)
|
||||
|
||||
%% Prevent the gen_server from hanging indefinitely in case the
|
||||
%% spawned process is taking too long processing the request.
|
||||
after Timeout ->
|
||||
{ error, timeout }
|
||||
end.
|
||||
-ifdef(EUNIT).
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%% Test Helpers
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
test_start_stop_(StartFun, StopFun, Tag) ->
|
||||
{ spawn,
|
||||
{ inorder,
|
||||
[
|
||||
check_stopped_(Tag),
|
||||
{ Tag, setup,
|
||||
StartFun,
|
||||
StopFun,
|
||||
check_started_(Tag)
|
||||
},
|
||||
check_stopped_(Tag)
|
||||
]
|
||||
}
|
||||
}.
|
||||
|
||||
check_started_(Tag) ->
|
||||
wrap_tag_(Tag,
|
||||
[
|
||||
?_test(ls()),
|
||||
?_test(pwd()),
|
||||
?_test(ok),
|
||||
?_test(ok)
|
||||
]
|
||||
).
|
||||
|
||||
check_stopped_(Tag) ->
|
||||
wrap_tag_(Tag,
|
||||
[
|
||||
?_assertExit( { noproc, _ }, pwd()),
|
||||
?_assertExit( { noproc, _ }, ls())
|
||||
]
|
||||
).
|
||||
|
||||
check_last_(RRDFile,Now) ->
|
||||
fun () ->
|
||||
{ ok, When } = erlrrd:last(RRDFile),
|
||||
When = Now
|
||||
end.
|
||||
|
||||
start_helper_() ->
|
||||
application:unset_env(erlrrd, rrdtoolcmd),
|
||||
application:unset_env(erlrrd, timeout),
|
||||
{ok, Pid} = start_link(),
|
||||
Pid.
|
||||
stop_helper_(Pid) -> stop_helper_(Pid, 3000).
|
||||
stop_helper_(Pid, Timeout) ->
|
||||
true = exit(Pid, normal),
|
||||
receive
|
||||
{'EXIT', Pid, Reason} -> Reason
|
||||
after Timeout ->
|
||||
throw({ timeout, { Pid, "Pid not responding to EXIT?"} })
|
||||
end.
|
||||
|
||||
% check if the dir we're in end's in /tests
|
||||
check_cwd_helper_() ->
|
||||
{ ok, L } = file:get_cwd(),
|
||||
"stset/" ++ _ = lists:reverse(L).
|
||||
|
||||
p_func(X,Steps) ->
|
||||
Pi = math:pi(),
|
||||
5 * (
|
||||
math:sin( 3*Pi/2 + X/(Steps / (8*Pi) ) )
|
||||
+ 1
|
||||
).
|
||||
|
||||
time_since_epoch() ->
|
||||
calendar:datetime_to_gregorian_seconds( erlang:universaltime())
|
||||
- ( 719528 * 86400 ).
|
||||
|
||||
wrap_tag_(T,L) when is_list(L) ->
|
||||
[ { T, X } || X <- L ].
|
||||
|
||||
cast(Blah) ->
|
||||
gen_server:cast(?MODULE, Blah ).
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%% Tests
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
%%%% test starting and stopping %%%%
|
||||
|
||||
-define(spew(Args), io:format(user, "~p~n", [{Args}])).
|
||||
-define(assertExists(File),
|
||||
{ ok, _ } = file:read_file_info(File)).
|
||||
-define(assertEnoent(File),
|
||||
{ error, enoent } = file:read_file_info(File)).
|
||||
|
||||
|
||||
start_link_test_() ->
|
||||
test_start_stop_(
|
||||
fun start_helper_/0,
|
||||
fun stop_helper_/1,
|
||||
"start_link test"
|
||||
).
|
||||
|
||||
start_stop_test_() ->
|
||||
test_start_stop_(
|
||||
fun() -> ok = start() end,
|
||||
fun(_) -> ok = stop() end,
|
||||
"start/0 stop/0"
|
||||
).
|
||||
|
||||
start_sup_test_() ->
|
||||
test_start_stop_(
|
||||
fun() ->
|
||||
{ok,Pid} = erlrrd_sup:start_link(),
|
||||
Pid
|
||||
end,
|
||||
fun stop_helper_/1,
|
||||
"sup:start_link/0 exit/2"
|
||||
).
|
||||
|
||||
start_sup2_test_() ->
|
||||
test_start_stop_(
|
||||
fun() ->
|
||||
check_cwd_helper_(),
|
||||
{ok,Pid} = erlrrd_sup:start_link("./dummyrrdtool"),
|
||||
Pid
|
||||
end,
|
||||
fun stop_helper_/1,
|
||||
"sup:start_link/1 exit/2"
|
||||
).
|
||||
|
||||
start_app_test_() ->
|
||||
test_start_stop_(
|
||||
fun() -> ok = erlrrd_app:start() end,
|
||||
fun(_) -> ok = erlrrd_app:stop() end,
|
||||
"app:start/0 app:stop/0"
|
||||
).
|
||||
|
||||
%%%% test interfaces %%%%
|
||||
|
||||
datain_dataout_test_() ->
|
||||
Prefix = "foo",
|
||||
RRDFile = Prefix ++ ".rrd",
|
||||
PNGFile = Prefix ++ ".png",
|
||||
RRDDump = Prefix ++ ".rrd.xml",
|
||||
RRDRestoredFile = Prefix ++ ".2.rrd",
|
||||
Now = time_since_epoch(),
|
||||
Then = Now - 86400,
|
||||
StepSize = 60,
|
||||
Steps = round((Now - Then) / StepSize),
|
||||
{ setup,
|
||||
fun() ->
|
||||
check_cwd_helper_(),
|
||||
file:delete(RRDFile),
|
||||
file:delete(RRDDump),
|
||||
file:delete(RRDRestoredFile),
|
||||
?assertEnoent(RRDFile),
|
||||
{ ok, Pid } = start_link(),
|
||||
Pid
|
||||
end,
|
||||
fun(Pid) ->
|
||||
stop_helper_(Pid),
|
||||
file:delete(RRDFile),
|
||||
file:delete(RRDDump),
|
||||
file:delete(RRDRestoredFile),
|
||||
ok
|
||||
end,
|
||||
{ inorder,
|
||||
[
|
||||
% create an rrd
|
||||
fun() ->
|
||||
{ok, _ } = erlrrd:create([
|
||||
io_lib:fwrite("~s --start ~B", [RRDFile, Then]),
|
||||
io_lib:fwrite(" --step ~B DS:thedata:GAUGE:~B:U:U",
|
||||
[ StepSize, StepSize ]),
|
||||
io_lib:fwrite(" RRA:AVERAGE:0.5:1:~B",[Steps])
|
||||
])
|
||||
end,
|
||||
|
||||
% write sin wave to rrd
|
||||
fun() ->
|
||||
lists:foreach(
|
||||
fun(X) ->
|
||||
P = p_func(X,Steps),
|
||||
%io:format(user, "~s ~B:~f~n", [ RRDFile, Then + X * StepSize, P ]),
|
||||
{ok, _ } = erlrrd:update(
|
||||
io_lib:format("~s ~B:~f", [ RRDFile, Then + X * StepSize, P ])
|
||||
)
|
||||
end,
|
||||
lists:seq(1, Steps)
|
||||
)
|
||||
end,
|
||||
|
||||
% check the update times
|
||||
check_last_(RRDFile, Now),
|
||||
fun() ->
|
||||
{ ok, When } = erlrrd:first(RRDFile),
|
||||
RoundThen = (erlang:trunc(Then/StepSize) + 1) * StepSize,
|
||||
When = RoundThen
|
||||
end,
|
||||
?_test(?assertMatch({error, _}, erlrrd:last("/somenothing/file"))),
|
||||
?_test(?assertMatch({error, _}, erlrrd:first("/somenothing/file"))),
|
||||
|
||||
% make a graph!! :)
|
||||
fun() ->
|
||||
{ ok, _ } = erlrrd:graph([
|
||||
"-l 0 -r", " ",
|
||||
"-w 700 -h 200 -a PNG ", PNGFile,
|
||||
" DEF:thedata=", RRDFile, ":thedata:AVERAGE AREA:thedata#CC9945",
|
||||
io_lib:format(" --start ~B --end ~B", [ Then, Now ])
|
||||
])
|
||||
% ok, now how can we check the graph??? hmm.
|
||||
end,
|
||||
|
||||
% run fetch
|
||||
fun() ->
|
||||
{ ok, _Data } = erlrrd:fetch([
|
||||
RRDFile, " AVERAGE ",
|
||||
io_lib:format(" --start ~B --end ~B", [ Then, Now ])
|
||||
]),
|
||||
%?spew(Data),
|
||||
%TODO check data
|
||||
ok
|
||||
end,
|
||||
|
||||
% dump the rrd
|
||||
fun() ->
|
||||
?assertEnoent(RRDDump),
|
||||
{ ok, _ } = erlrrd:dump( RRDFile ++ " " ++ RRDDump ),
|
||||
?assertExists(RRDDump)
|
||||
end,
|
||||
|
||||
% restore the rrd
|
||||
fun() ->
|
||||
?assertExists(RRDDump),
|
||||
?assertEnoent(RRDRestoredFile),
|
||||
{ ok, _ } = erlrrd:restore( RRDDump ++ " " ++ RRDRestoredFile ),
|
||||
?assertExists(RRDRestoredFile)
|
||||
end,
|
||||
check_last_(RRDRestoredFile, Now),
|
||||
|
||||
% xport
|
||||
{ "xport",
|
||||
fun() ->
|
||||
?assertExists(RRDRestoredFile),
|
||||
{ok, Response} = xport([
|
||||
"DEF:c=", RRDRestoredFile, ":thedata:AVERAGE",
|
||||
" XPORT:c",
|
||||
io_lib:format(" --start ~B --end ~B", [ Then, Now ])
|
||||
]),
|
||||
%% TODO check response
|
||||
Response
|
||||
end
|
||||
},
|
||||
|
||||
%lastupdate
|
||||
fun() ->
|
||||
{ ok, [ _, _, [Last]] } = lastupdate(RRDRestoredFile),
|
||||
?assertMatch(
|
||||
{match, _, _ },
|
||||
regexp:match(Last,
|
||||
% TODO make the regex match beginning of line? why no work?
|
||||
io_lib:format("~B", [ Now ])
|
||||
)
|
||||
),
|
||||
ok
|
||||
end,
|
||||
|
||||
% info
|
||||
fun() ->
|
||||
{ ok, Info } = info(RRDFile),
|
||||
[ ["filename = " ++ _L] | _T ] = Info
|
||||
end,
|
||||
|
||||
fun() -> commas_are_cool end
|
||||
]
|
||||
}
|
||||
}.
|
||||
|
||||
remote_cmds_test_() ->
|
||||
Dir = "makadir",
|
||||
{ setup,
|
||||
fun() ->
|
||||
check_cwd_helper_(),
|
||||
?assertEnoent(Dir),
|
||||
start_helper_()
|
||||
end,
|
||||
fun(P) ->
|
||||
file:del_dir(Dir),
|
||||
stop_helper_(P)
|
||||
end,
|
||||
{ inorder,
|
||||
[
|
||||
% pwd
|
||||
{ "pwd1", fun() ->
|
||||
{ ok, Cwd } = erlrrd:pwd(),
|
||||
"stset/" ++ _ = lists:reverse(Cwd)
|
||||
end},
|
||||
{ "mkdir", fun() -> erlrrd:mkdir(Dir) end },
|
||||
{ "cd ", fun() -> ok = erlrrd:cd(Dir) end},
|
||||
% pwd
|
||||
{ "pwd2",
|
||||
fun() ->
|
||||
{ ok, Cwd } = erlrrd:pwd(),
|
||||
{ match, _, _ } =
|
||||
regexp:match(Cwd, "/tests/" ++ Dir ++ "$")
|
||||
end
|
||||
},
|
||||
{ "ls",
|
||||
fun() ->
|
||||
% relys on '.' and '..' always returning first?
|
||||
% is that a bad idea?
|
||||
{ ok, List } = ls(),
|
||||
io:format(user," ~p~n", [List]),
|
||||
?assert( lists:any(fun(X) -> X =:= ["d .."] end, List))
|
||||
end },
|
||||
fun() -> commas_are_cool end
|
||||
]
|
||||
}
|
||||
}.
|
||||
|
||||
c_test_() ->
|
||||
[
|
||||
?_test(
|
||||
[
|
||||
["\"", "these", "\""], " ",
|
||||
["\"", "are", "\""], " ",
|
||||
["\"", "my", "\""], " ",
|
||||
["\"", "args", "\""]
|
||||
] = c(["these", "are", "my", "args"])),
|
||||
?_test([[ "\"", "a", "\""]] = c(["a"]))
|
||||
].
|
||||
|
||||
pass_newlines_test_() ->
|
||||
M = "No newlines",
|
||||
{ setup,
|
||||
fun start_helper_/0,
|
||||
fun stop_helper_/1,
|
||||
[
|
||||
?_assertMatch( { error, M }, cd("..\n")),
|
||||
?_assertMatch( { error, M }, info("fart.rrd\n")),
|
||||
?_assertMatch( { error, M },
|
||||
create(["foo.rrd bar baz", [[[[<<"blahdedah\n">>]]]], "haha"])
|
||||
),
|
||||
fun() -> ok end
|
||||
]
|
||||
}.
|
||||
|
||||
graph_to_stdout_saftey_test_() ->
|
||||
M = { error, "Graphing to stdout not supported." },
|
||||
[
|
||||
?_assertMatch( M, graph(" -")),
|
||||
?_assertMatch( M, graph("-")),
|
||||
?_assertMatch( M, graph(" - ")),
|
||||
?_assertMatch( M, graph([" ", "-"," "])),
|
||||
?_assertMatch( M, graph([" ", [[["-"]]]," "])),
|
||||
?_assertMatch( M, graph([" ", [[["-"]]]])),
|
||||
?_assertMatch( M, graph([[[["-"]]]," "])),
|
||||
?_assertMatch( M, graph([" ", [[[<<"-">>]]]," "])),
|
||||
?_assertMatch( M, graph([" ", [[[<<"-">>]]]])),
|
||||
?_assertMatch( M, graph([[[[<<"-">>]]]," "])),
|
||||
fun() -> ok end
|
||||
].
|
||||
|
||||
%%%% tests for corner cases %%%%
|
||||
|
||||
cause_long_response_test_() ->
|
||||
{ setup,
|
||||
fun() ->
|
||||
check_cwd_helper_(),
|
||||
{ ok, Pid } = start_link("./dummyrrdtool -"),
|
||||
Pid
|
||||
end,
|
||||
fun stop_helper_/1,
|
||||
?_test(do(longresponse, []))
|
||||
}.
|
||||
|
||||
cause_timeout_test_() ->
|
||||
{ setup,
|
||||
fun() ->
|
||||
check_cwd_helper_(),
|
||||
ok = application:set_env(erlrrd, timeout, 1),
|
||||
{ ok, Pid } = erlrrd_sup:start_link("./dummyrrdtool -"),
|
||||
Pid
|
||||
end,
|
||||
fun stop_helper_/1,
|
||||
?_assertExit({port_timeout, _}, do(timeout, []))
|
||||
}.
|
||||
|
||||
%%%% tests of privates %%%%
|
||||
|
||||
join_test() ->
|
||||
[ "a", " ", "b", " ", "c"] = join(["a", "b", "c"], " ").
|
||||
join_test_() ->
|
||||
[
|
||||
?_test([ "a", " ", "b", " ", "c"] = join(["a", "b", "c"], " ")),
|
||||
?_assertNot([ "a", "b", " ", "c"] =:= join(["a", "b", "c"], " "))
|
||||
].
|
||||
|
||||
has_newline_test_() ->
|
||||
[
|
||||
?_test( true = has_newline("\n")),
|
||||
?_test( true = has_newline(["\n"])),
|
||||
?_test( true = has_newline(
|
||||
["these", ["are", [ "my args" ] | <<"newline\n">> ], "so", "there"])),
|
||||
?_test( false = has_newline(
|
||||
["these", ["are", [ "my args" ] | <<"newline">> ], "so", "there"])),
|
||||
?_test( true = has_newline(
|
||||
["these\n", ["are", [ "my args" ] | <<"newline">> ], "so", "there"])),
|
||||
?_test( true = has_newline(
|
||||
["these", ["are",
|
||||
[ "my args" | [[[[[[[[ "blah\n"]]]]]]]] ] | <<"newline">> ],
|
||||
"so", "there"])),
|
||||
?_test( false = has_newline("")),
|
||||
?_test( false = has_newline([])),
|
||||
?_test( false = has_newline(<<>>))
|
||||
].
|
||||
|
||||
%%%%% tests to get coverage %%%%%
|
||||
|
||||
should_be_done_better_test_() ->
|
||||
{ setup,
|
||||
fun start_helper_/0,
|
||||
fun stop_helper_/1,
|
||||
[
|
||||
?_assertMatch( {error, _ }, tune("blah") ),
|
||||
?_assertMatch( {error, _ }, resize("blah") ),
|
||||
?_assertMatch( {error, _ }, updatev("blah") ),
|
||||
?_test(ok)
|
||||
]
|
||||
}.
|
||||
|
||||
handle_cast_test_() ->
|
||||
{ setup,
|
||||
fun start_helper_/0,
|
||||
fun stop_helper_/1,
|
||||
?_test(cast(blah))
|
||||
}.
|
||||
|
||||
handle_info_test_() ->
|
||||
{ setup,
|
||||
fun start_helper_/0,
|
||||
fun stop_helper_/1,
|
||||
?_test(?MODULE ! yo)
|
||||
}.
|
||||
|
||||
stop_helper_test_() ->
|
||||
{ setup,
|
||||
% start fun
|
||||
fun() ->
|
||||
F = fun(F) ->
|
||||
receive
|
||||
Any -> io:format(user,"~p Hey, got: ~p~n", [ self(), Any ])
|
||||
end,
|
||||
F(F)
|
||||
end,
|
||||
spawn(
|
||||
fun() ->
|
||||
process_flag(trap_exit, true),
|
||||
F(F)
|
||||
end
|
||||
)
|
||||
end,
|
||||
% stop fun,
|
||||
fun(P) -> exit(P, kill) end,
|
||||
% test gen
|
||||
fun(P) ->
|
||||
?_assertThrow({timeout,_}, stop_helper_(P, 1))
|
||||
end
|
||||
}.
|
||||
|
||||
port_exit_test_() ->
|
||||
?_assert(
|
||||
begin
|
||||
io:format(user, "~n==== test: expect erlrrd exit~n", []),
|
||||
{ok,Pid} = start_link("./dummyrrdtool"),
|
||||
{ok, _} = do(die, []),
|
||||
receive
|
||||
{ 'EXIT', Pid, {port_exit, 1} } -> true
|
||||
after 4000 ->
|
||||
exit(Pid,kill),
|
||||
false
|
||||
end
|
||||
end
|
||||
).
|
||||
|
||||
code_change_test() ->
|
||||
{ ok, state } = code_change( oldvsn, state, extra ).
|
||||
|
||||
-endif.
|
|
@ -0,0 +1,18 @@
|
|||
-module(erlrrd_app).
|
||||
|
||||
-export([start/0, stop/0]).
|
||||
-behavior(application).
|
||||
-export([start/2, stop/1]).
|
||||
|
||||
|
||||
start() ->
|
||||
application:start(erlrrd).
|
||||
|
||||
start(_Type, _Args) ->
|
||||
erlrrd_sup:start_link().
|
||||
|
||||
stop() ->
|
||||
application:stop(erlrrd).
|
||||
|
||||
stop(_State) ->
|
||||
ok.
|
|
@ -0,0 +1,42 @@
|
|||
-module(erlrrd_sup).
|
||||
|
||||
-export([start_link/1, start_link/0]).
|
||||
|
||||
-behavior(supervisor).
|
||||
|
||||
-export([init/1]).
|
||||
|
||||
|
||||
%% @spec start_link(RRDToolCmd) -> Result
|
||||
%% RRDToolCmd = string()
|
||||
%% Result = {ok,Pid} | ignore | {error,Error}
|
||||
%% Pid = pid()
|
||||
%% Error = {already_started,Pid} | shutdown | term()
|
||||
start_link(RRDToolCmd) ->
|
||||
application:set_env(erlrrd_sup, rrdtoolcmd, RRDToolCmd),
|
||||
supervisor:start_link(erlrrd_sup, []).
|
||||
|
||||
%% @spec start_link() -> Result
|
||||
%% Result = {ok,Pid} | ignore | {error,Error}
|
||||
%% Pid = pid()
|
||||
%% Error = {already_started,Pid} | shutdown | term()
|
||||
start_link() ->
|
||||
supervisor:start_link(erlrrd_sup, []).
|
||||
|
||||
init(_) ->
|
||||
{
|
||||
ok,
|
||||
{
|
||||
{one_for_one, 5, 10 },
|
||||
[
|
||||
{
|
||||
erlrrd,
|
||||
{ erlrrd, start_link, [] },
|
||||
permanent,
|
||||
3000,
|
||||
worker,
|
||||
[ erlrrd ]
|
||||
}
|
||||
]
|
||||
}
|
||||
}.
|
|
@ -0,0 +1,39 @@
|
|||
## -*- makefile -*-
|
||||
|
||||
######################################################################
|
||||
## Erlang
|
||||
|
||||
ERL := erl
|
||||
ERLC := $(ERL)c
|
||||
|
||||
INCLUDE_DIRS := ../include $(wildcard ../deps/*/include)
|
||||
EBIN_DIRS := $(wildcard ../deps/*/ebin)
|
||||
ERLC_FLAGS := -W $(INCLUDE_DIRS:../%=-I ../%) $(EBIN_DIRS:%=-pa %)
|
||||
|
||||
ifndef no_debug_info
|
||||
ERLC_FLAGS += +debug_info
|
||||
endif
|
||||
|
||||
ifdef debug
|
||||
ERLC_FLAGS += -Ddebug
|
||||
endif
|
||||
|
||||
EBIN_DIR := ../ebin
|
||||
EMULATOR := beam
|
||||
|
||||
ERL_SOURCES := $(wildcard *.erl)
|
||||
ERL_HEADERS := $(wildcard *.hrl) $(wildcard ../include/*.hrl)
|
||||
ERL_OBJECTS := $(ERL_SOURCES:%.erl=$(EBIN_DIR)/%.$(EMULATOR))
|
||||
ERL_OBJECTS_LOCAL := $(ERL_SOURCES:%.erl=./%.$(EMULATOR))
|
||||
APP_FILES := $(wildcard *.app)
|
||||
EBIN_FILES = $(ERL_OBJECTS) $(APP_FILES:%.app=../ebin/%.app)
|
||||
MODULES = $(ERL_SOURCES:%.erl=%)
|
||||
|
||||
../ebin/%.app: %.app
|
||||
cp $< $@
|
||||
|
||||
$(EBIN_DIR)/%.$(EMULATOR): %.erl
|
||||
$(ERLC) $(ERLC_FLAGS) -o $(EBIN_DIR) $<
|
||||
|
||||
./%.$(EMULATOR): %.erl
|
||||
$(ERLC) $(ERLC_FLAGS) -o . $<
|
|
@ -0,0 +1,4 @@
|
|||
TESTS = \
|
||||
module-erlrrd
|
||||
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
#!/bin/sh
|
||||
|
||||
while read cmd rest
|
||||
do
|
||||
case $cmd in
|
||||
pwd)
|
||||
echo "ERROR: pwd disabled for testing"
|
||||
;;
|
||||
timeout)
|
||||
sleep 1
|
||||
echo "ERROR: timed out"
|
||||
;;
|
||||
die)
|
||||
echo "OK u:0.01 s:0.02 r:0.03"
|
||||
exit 1
|
||||
;;
|
||||
longresponse)
|
||||
perl -le 'print "a234567890123456789012345678901234567890" x (255)'
|
||||
echo "OK u:0.01 s:0.02 r:0.03"
|
||||
;;
|
||||
*)
|
||||
echo $cmd $rest
|
||||
echo "OK u:0.01 s:0.02 r:0.03"
|
||||
;;
|
||||
esac
|
||||
done
|
|
@ -0,0 +1 @@
|
|||
../../webmachine
|
|
@ -0,0 +1 @@
|
|||
{["sensor", sensor], flukso_resource, []}.
|
|
@ -0,0 +1,8 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>It Worked</title>
|
||||
</head>
|
||||
<body>
|
||||
MochiWeb running.
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,14 @@
|
|||
{application, flukso,
|
||||
[{description, "flukso"},
|
||||
{vsn, "0.1"},
|
||||
{modules, [
|
||||
flukso,
|
||||
flukso_app,
|
||||
flukso_sup,
|
||||
flukso_deps,
|
||||
flukso_resource
|
||||
]},
|
||||
{registered, []},
|
||||
{mod, {flukso_app, []}},
|
||||
{env, []},
|
||||
{applications, [kernel, stdlib, crypto]}]}.
|
|
@ -0,0 +1,44 @@
|
|||
%% @author author <author@example.com>
|
||||
%% @copyright YYYY author.
|
||||
|
||||
%% @doc TEMPLATE.
|
||||
|
||||
-module(flukso).
|
||||
-author('author <author@example.com>').
|
||||
-export([start/0, start_link/0, stop/0]).
|
||||
|
||||
ensure_started(App) ->
|
||||
case application:start(App) of
|
||||
ok ->
|
||||
ok;
|
||||
{error, {already_started, App}} ->
|
||||
ok
|
||||
end.
|
||||
|
||||
%% @spec start_link() -> {ok,Pid::pid()}
|
||||
%% @doc Starts the app for inclusion in a supervisor tree
|
||||
start_link() ->
|
||||
flukso_deps:ensure(),
|
||||
ensure_started(erlrrd),
|
||||
ensure_started(crypto),
|
||||
ensure_started(webmachine),
|
||||
flukso_sup:start_link().
|
||||
|
||||
%% @spec start() -> ok
|
||||
%% @doc Start the flukso server.
|
||||
start() ->
|
||||
flukso_deps:ensure(),
|
||||
ensure_started(erlrrd),
|
||||
ensure_started(crypto),
|
||||
ensure_started(webmachine),
|
||||
application:start(flukso).
|
||||
|
||||
%% @spec stop() -> ok
|
||||
%% @doc Stop the flukso server.
|
||||
stop() ->
|
||||
Res = application:stop(flukso),
|
||||
application:stop(erlrrd),
|
||||
application:stop(webmachine),
|
||||
application:stop(crypto),
|
||||
Res.
|
||||
|
|
@ -0,0 +1 @@
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
%% @author author <author@example.com>
|
||||
%% @copyright YYYY author.
|
||||
|
||||
%% @doc Callbacks for the flukso application.
|
||||
|
||||
-module(flukso_app).
|
||||
-author('author <author@example.com>').
|
||||
|
||||
-behaviour(application).
|
||||
-export([start/2,stop/1]).
|
||||
|
||||
|
||||
%% @spec start(_Type, _StartArgs) -> ServerRet
|
||||
%% @doc application start callback for flukso.
|
||||
start(_Type, _StartArgs) ->
|
||||
flukso_deps:ensure(),
|
||||
flukso_sup:start_link().
|
||||
|
||||
%% @spec stop(_State) -> ServerRet
|
||||
%% @doc application stop callback for flukso.
|
||||
stop(_State) ->
|
||||
ok.
|
|
@ -0,0 +1,84 @@
|
|||
%% @author author <author@example.com>
|
||||
%% @copyright YYYY author.
|
||||
|
||||
%% @doc Ensure that the relatively-installed dependencies are on the code
|
||||
%% loading path, and locate resources relative
|
||||
%% to this application's path.
|
||||
|
||||
-module(flukso_deps).
|
||||
-author('author <author@example.com>').
|
||||
|
||||
-export([ensure/0, ensure/1]).
|
||||
-export([get_base_dir/0, get_base_dir/1]).
|
||||
-export([local_path/1, local_path/2]).
|
||||
-export([deps_on_path/0, new_siblings/1]).
|
||||
|
||||
%% @spec deps_on_path() -> [ProjNameAndVers]
|
||||
%% @doc List of project dependencies on the path.
|
||||
deps_on_path() ->
|
||||
F = fun (X, Acc) ->
|
||||
ProjDir = filename:dirname(X),
|
||||
case {filename:basename(X),
|
||||
filename:basename(filename:dirname(ProjDir))} of
|
||||
{"ebin", "deps"} ->
|
||||
[filename:basename(ProjDir) | Acc];
|
||||
_ ->
|
||||
Acc
|
||||
end
|
||||
end,
|
||||
ordsets:from_list(lists:foldl(F, [], code:get_path())).
|
||||
|
||||
%% @spec new_siblings(Module) -> [Dir]
|
||||
%% @doc Find new siblings paths relative to Module that aren't already on the
|
||||
%% code path.
|
||||
new_siblings(Module) ->
|
||||
Existing = deps_on_path(),
|
||||
SiblingEbin = filelib:wildcard(local_path(["deps", "*", "ebin"], Module)),
|
||||
Siblings = [filename:dirname(X) || X <- SiblingEbin,
|
||||
ordsets:is_element(
|
||||
filename:basename(filename:dirname(X)),
|
||||
Existing) =:= false],
|
||||
lists:filter(fun filelib:is_dir/1,
|
||||
lists:append([[filename:join([X, "ebin"]),
|
||||
filename:join([X, "include"])] ||
|
||||
X <- Siblings])).
|
||||
|
||||
|
||||
%% @spec ensure(Module) -> ok
|
||||
%% @doc Ensure that all ebin and include paths for dependencies
|
||||
%% of the application for Module are on the code path.
|
||||
ensure(Module) ->
|
||||
code:add_paths(new_siblings(Module)),
|
||||
code:clash(),
|
||||
ok.
|
||||
|
||||
%% @spec ensure() -> ok
|
||||
%% @doc Ensure that the ebin and include paths for dependencies of
|
||||
%% this application are on the code path. Equivalent to
|
||||
%% ensure(?Module).
|
||||
ensure() ->
|
||||
ensure(?MODULE).
|
||||
|
||||
%% @spec get_base_dir(Module) -> string()
|
||||
%% @doc Return the application directory for Module. It assumes Module is in
|
||||
%% a standard OTP layout application in the ebin or src directory.
|
||||
get_base_dir(Module) ->
|
||||
{file, Here} = code:is_loaded(Module),
|
||||
filename:dirname(filename:dirname(Here)).
|
||||
|
||||
%% @spec get_base_dir() -> string()
|
||||
%% @doc Return the application directory for this application. Equivalent to
|
||||
%% get_base_dir(?MODULE).
|
||||
get_base_dir() ->
|
||||
get_base_dir(?MODULE).
|
||||
|
||||
%% @spec local_path([string()], Module) -> string()
|
||||
%% @doc Return an application-relative directory from Module's application.
|
||||
local_path(Components, Module) ->
|
||||
filename:join([get_base_dir(Module) | Components]).
|
||||
|
||||
%% @spec local_path(Components) -> string()
|
||||
%% @doc Return an application-relative directory for this application.
|
||||
%% Equivalent to local_path(Components, ?MODULE).
|
||||
local_path(Components) ->
|
||||
local_path(Components, ?MODULE).
|
|
@ -0,0 +1,78 @@
|
|||
%% @author icarus75 <bart.vandermeerssche@flukso.net>
|
||||
%% @copyright 2009-2010 flukso.net
|
||||
%% @doc Flukso webmachine_resource.
|
||||
|
||||
-module(flukso_resource).
|
||||
-export([init/1, allowed_methods/2, malformed_request/2, content_types_provided/2, to_json/2]).
|
||||
|
||||
-include_lib("webmachine/include/webmachine.hrl").
|
||||
|
||||
init([]) ->
|
||||
{ok, undefined}.
|
||||
|
||||
allowed_methods(ReqData, State) ->
|
||||
{['GET'], ReqData, State}.
|
||||
|
||||
malformed_request(ReqData, State) ->
|
||||
{RrdSensor, ValidSensor} = rrd_sensor(wrq:path_info(sensor, ReqData)),
|
||||
{RrdTime, ValidInterval} = rrd_time(wrq:get_qs_value("interval", ReqData)),
|
||||
{RrdFactor, ValidUnit} = rrd_factor(wrq:get_qs_value("unit", ReqData)),
|
||||
|
||||
{case {ValidSensor, ValidInterval, ValidUnit} of
|
||||
{true, true, true} -> false;
|
||||
_ -> true
|
||||
end,
|
||||
ReqData, {RrdSensor , RrdTime, RrdFactor}}.
|
||||
|
||||
content_types_provided(ReqData, State) ->
|
||||
{[{"application/json", to_json}], ReqData, State}.
|
||||
|
||||
to_json(ReqData, State) ->
|
||||
{RrdSensor , RrdTime, RrdFactor} = State,
|
||||
|
||||
case wrq:path_info(interval, ReqData) of
|
||||
"night" -> Path = "var/data/night/";
|
||||
_Interval -> Path = "var/data/base/"
|
||||
end,
|
||||
|
||||
case erlrrd:fetch(erlrrd:c([[Path, [RrdSensor|".rrd"]], "AVERAGE", ["-s",RrdTime]])) of
|
||||
{ok, Response} ->
|
||||
Filtered = [re:split(X, "[:][ ]", [{return,list}]) || [X] <- Response, string:str(X, ":") == 11],
|
||||
Datapoints = [[list_to_integer(X), round(list_to_float(Y) * RrdFactor)] || [X, Y] <- Filtered, string:len(Y) /= 3],
|
||||
Nans = [[list_to_integer(X), list_to_binary(Y)] || [X, Y] <- Filtered, string:len(Y) == 3],
|
||||
Final = lists:merge(Datapoints, Nans),
|
||||
{mochijson2:encode(Final), ReqData, State};
|
||||
|
||||
{error, Reason} ->
|
||||
{{halt, 404}, ReqData, State}
|
||||
end.
|
||||
|
||||
rrd_sensor(Sensor) ->
|
||||
case re:run(Sensor, "[0-9a-f]+", []) of
|
||||
{match, [{0,32}]} -> {Sensor, true};
|
||||
_ -> {false, false}
|
||||
end.
|
||||
|
||||
rrd_time(Interval) ->
|
||||
Intervals = [{"hour", "end-1h"},
|
||||
{"day", "end-1d"},
|
||||
{"month", "end-30d"},
|
||||
{"year", "end-1y"},
|
||||
{"night", "end-30d"}],
|
||||
|
||||
case lists:keyfind(Interval, 1, Intervals) of
|
||||
false -> {false, false};
|
||||
{_Interval, RrdTime} -> {RrdTime, true}
|
||||
end.
|
||||
|
||||
rrd_factor(Unit) ->
|
||||
Units = [{"watt", 3600},
|
||||
{"kwhperyear", 31536},
|
||||
{"eurperyear", 5676},
|
||||
{"audperyear", 5991}],
|
||||
|
||||
case lists:keyfind(Unit, 1, Units) of
|
||||
false -> {false, false};
|
||||
{_Unit, RrdFactor} -> {RrdFactor, true}
|
||||
end.
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
%% @author author <author@example.com>
|
||||
%% @copyright YYYY author.
|
||||
|
||||
%% @doc Supervisor for the flukso application.
|
||||
|
||||
-module(flukso_sup).
|
||||
-author('author <author@example.com>').
|
||||
|
||||
-behaviour(supervisor).
|
||||
|
||||
%% External exports
|
||||
-export([start_link/0, upgrade/0]).
|
||||
|
||||
%% supervisor callbacks
|
||||
-export([init/1]).
|
||||
|
||||
%% @spec start_link() -> ServerRet
|
||||
%% @doc API for starting the supervisor.
|
||||
start_link() ->
|
||||
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
||||
|
||||
%% @spec upgrade() -> ok
|
||||
%% @doc Add processes if necessary.
|
||||
upgrade() ->
|
||||
{ok, {_, Specs}} = init([]),
|
||||
|
||||
Old = sets:from_list(
|
||||
[Name || {Name, _, _, _} <- supervisor:which_children(?MODULE)]),
|
||||
New = sets:from_list([Name || {Name, _, _, _, _, _} <- Specs]),
|
||||
Kill = sets:subtract(Old, New),
|
||||
|
||||
sets:fold(fun (Id, ok) ->
|
||||
supervisor:terminate_child(?MODULE, Id),
|
||||
supervisor:delete_child(?MODULE, Id),
|
||||
ok
|
||||
end, ok, Kill),
|
||||
|
||||
[supervisor:start_child(?MODULE, Spec) || Spec <- Specs],
|
||||
ok.
|
||||
|
||||
%% @spec init([]) -> SupervisorTree
|
||||
%% @doc supervisor callback.
|
||||
init([]) ->
|
||||
Ip = case os:getenv("WEBMACHINE_IP") of false -> "127.0.0.1"; Any -> Any end,
|
||||
{ok, Dispatch} = file:consult(filename:join(
|
||||
[filename:dirname(code:which(?MODULE)),
|
||||
"..", "priv", "dispatch.conf"])),
|
||||
WebConfig = [
|
||||
{ip, Ip},
|
||||
{port, 9999},
|
||||
{log_dir, "var/log"},
|
||||
{dispatch, Dispatch}],
|
||||
Web = {webmachine_mochiweb,
|
||||
{webmachine_mochiweb, start, [WebConfig]},
|
||||
permanent, 5000, worker, dynamic},
|
||||
Processes = [Web],
|
||||
{ok, {{one_for_one, 10, 10}, Processes}}.
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
cd `dirname $0`
|
||||
exec erl -smp auto +K true -sname flukso -setcookie mycookie -pa $PWD/ebin $PWD/deps/*/ebin $PWD/deps/*/deps/*/ebin -boot start_sasl -s reloader -s flukso
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
cd `dirname $0`
|
||||
exec erl -detached -smp auto +K true -sname flukso -setcookie mycookie -pa $PWD/ebin $PWD/deps/*/ebin $PWD/deps/*/deps/*/ebin -boot start_sasl -s reloader -s flukso
|
|
@ -0,0 +1 @@
|
|||
../../../public/sites/all/modules/logger/data
|
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
default
|
|
@ -0,0 +1,2 @@
|
|||
b3575cbf3376171f0f040609a7a49cdb9979b480 85
|
||||
b3575cbf3376171f0f040609a7a49cdb9979b480 default
|
Binary file not shown.
|
@ -0,0 +1,2 @@
|
|||
[paths]
|
||||
default = http://bitbucket.org/justin/webmachine/
|
|
@ -0,0 +1,2 @@
|
|||
revlogv1
|
||||
store
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue