diff options
Diffstat (limited to 'lib/os_mon/src')
-rw-r--r-- | lib/os_mon/src/Makefile | 3 | ||||
-rw-r--r-- | lib/os_mon/src/cpu_sup.erl | 30 | ||||
-rw-r--r-- | lib/os_mon/src/disksup.erl | 100 | ||||
-rw-r--r-- | lib/os_mon/src/memsup.erl | 5 | ||||
-rw-r--r-- | lib/os_mon/src/os_mon.app.src | 6 | ||||
-rw-r--r-- | lib/os_mon/src/os_mon_mib.erl | 251 |
6 files changed, 117 insertions, 278 deletions
diff --git a/lib/os_mon/src/Makefile b/lib/os_mon/src/Makefile index fc2eb22393..923a31f290 100644 --- a/lib/os_mon/src/Makefile +++ b/lib/os_mon/src/Makefile @@ -34,8 +34,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/os_mon-$(VSN) # ---------------------------------------------------- # Target Specs # ---------------------------------------------------- -MODULES= disksup memsup cpu_sup os_mon os_mon_mib os_sup os_mon_sysinfo \ - nteventlog +MODULES= disksup memsup cpu_sup os_mon os_sup os_mon_sysinfo nteventlog INCLUDE=../include CSRC=../c_src diff --git a/lib/os_mon/src/cpu_sup.erl b/lib/os_mon/src/cpu_sup.erl index e758b63d19..ba2d89313e 100644 --- a/lib/os_mon/src/cpu_sup.erl +++ b/lib/os_mon/src/cpu_sup.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2016. All Rights Reserved. +%% Copyright Ericsson AB 1997-2018. 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. @@ -220,17 +220,21 @@ code_change(_OldVsn, State, _Extra) -> %% internal functions %%---------------------------------------------------------------------- -get_uint32_measurement(Request, #internal{os_type = {unix, linux}}) -> - {ok,F} = file:open("/proc/loadavg",[read,raw]), - {ok,D} = file:read_line(F), - ok = file:close(F), - {ok,[Load1,Load5,Load15,_PRun,PTotal],_} = io_lib:fread("~f ~f ~f ~d/~d", D), - case Request of - ?avg1 -> sunify(Load1); - ?avg5 -> sunify(Load5); - ?avg15 -> sunify(Load15); - ?ping -> 4711; - ?nprocs -> PTotal +get_uint32_measurement(Request, #internal{port = P, os_type = {unix, linux}}) -> + case file:open("/proc/loadavg",[read,raw]) of + {ok,F} -> + {ok,D} = file:read_line(F), + ok = file:close(F), + {ok,[Load1,Load5,Load15,_PRun,PTotal],_} = io_lib:fread("~f ~f ~f ~d/~d", D), + case Request of + ?avg1 -> sunify(Load1); + ?avg5 -> sunify(Load5); + ?avg15 -> sunify(Load15); + ?ping -> 4711; + ?nprocs -> PTotal + end; + {error,_} -> + port_server_call(P, Request) end; get_uint32_measurement(Request, #internal{port = P, os_type = {unix, Sys}}) when Sys == sunos; @@ -244,7 +248,7 @@ get_uint32_measurement(Request, #internal{os_type = {unix, Sys}}) when Sys == ir %% Get the load average using uptime. %% "8:01pm up 2 days, 22:12, 4 users, load average: 0.70, 0.58, 0.43" D = os:cmd("uptime") -- "\n", - Avg = lists:reverse(hd(string:tokens(lists:reverse(D), ":"))), + Avg = lists:reverse(hd(string:lexemes(lists:reverse(D), ":"))), {ok, [L1, L5, L15], _} = io_lib:fread("~f, ~f, ~f", Avg), case Request of ?avg1 -> sunify(L1); diff --git a/lib/os_mon/src/disksup.erl b/lib/os_mon/src/disksup.erl index 044604b000..5118d807e1 100644 --- a/lib/os_mon/src/disksup.erl +++ b/lib/os_mon/src/disksup.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-2018. 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. @@ -32,7 +32,7 @@ terminate/2, code_change/3]). %% Other exports --export([format_status/2]). +-export([format_status/2, parse_df/2]). -record(state, {threshold, timeout, os, diskdata = [],port}). @@ -294,8 +294,8 @@ check_disks_solaris("", _Threshold) -> check_disks_solaris("\n", _Threshold) -> []; check_disks_solaris(Str, Threshold) -> - case io_lib:fread("~s~d~d~d~d%~s", Str) of - {ok, [_FS, KB, _Used, _Avail, Cap, MntOn], RestStr} -> + case parse_df(Str, posix) of + {ok, {KB, Cap, MntOn}, RestStr} -> if Cap >= Threshold -> set_alarm({disk_almost_full, MntOn}, []); @@ -308,14 +308,102 @@ check_disks_solaris(Str, Threshold) -> check_disks_solaris(skip_to_eol(Str),Threshold) end. +%% @private +%% @doc Predicate to take a word from the input string until a space or +%% a percent '%' sign (the Capacity field is followed by a %) +parse_df_is_not_space($ ) -> false; +parse_df_is_not_space($%) -> false; +parse_df_is_not_space(_) -> true. + +%% @private +%% @doc Predicate to take spaces away from string. Stops on a non-space +parse_df_is_space($ ) -> true; +parse_df_is_space(_) -> false. + +%% @private +%% @doc Predicate to consume remaining characters until end of line. +parse_df_is_not_eol($\r) -> false; +parse_df_is_not_eol($\n) -> false; +parse_df_is_not_eol(_) -> true. + +%% @private +%% @doc Trims leading non-spaces (the word) from the string then trims spaces. +parse_df_skip_word(Input) -> + Remaining = lists:dropwhile(fun parse_df_is_not_space/1, Input), + lists:dropwhile(fun parse_df_is_space/1, Remaining). + +%% @private +%% @doc Takes all non-spaces and then drops following spaces. +parse_df_take_word(Input) -> + {Word, Remaining0} = lists:splitwith(fun parse_df_is_not_space/1, Input), + Remaining1 = lists:dropwhile(fun parse_df_is_space/1, Remaining0), + {Word, Remaining1}. + +%% @private +%% @doc Takes all non-spaces and then drops the % after it and the spaces. +parse_df_take_word_percent(Input) -> + {Word, Remaining0} = lists:splitwith(fun parse_df_is_not_space/1, Input), + %% Drop the leading % or do nothing + Remaining1 = case Remaining0 of + [$% | R1] -> R1; + _ -> Remaining0 % Might be no % or empty list even + end, + Remaining2 = lists:dropwhile(fun parse_df_is_space/1, Remaining1), + {Word, Remaining2}. + +%% @private +%% @doc Given a line of 'df' POSIX/SUSv3 output split it into fields: +%% a string (mounted device), 4 integers (kilobytes, used, available +%% and capacity), skip % sign, (optionally for susv3 can also skip IUsed, IFree +%% and ICap% fields) then take remaining characters as the mount path +-spec parse_df(string(), posix | susv3) -> + {error, parse_df} | {ok, {integer(), integer(), list()}, string()}. +parse_df(Input0, Flavor) -> + %% Format of Posix/Linux df output looks like Header + Lines + %% Filesystem 1024-blocks Used Available Capacity Mounted on + %% udev 2467108 0 2467108 0% /dev + Input1 = parse_df_skip_word(Input0), % skip device path field + {KbStr, Input2} = parse_df_take_word(Input1), % take Kb field + Input3 = parse_df_skip_word(Input2), % skip Used field + Input4 = parse_df_skip_word(Input3), % skip Avail field + + % take Capacity% field; drop a % sign following the capacity + {CapacityStr, Input5} = parse_df_take_word_percent(Input4), + + %% Format of OS X/SUSv3 df looks similar to POSIX but has 3 extra columns + %% Filesystem 1024-blocks Used Available Capacity iused ifree %iused Mounted + %% /dev/disk1 243949060 2380 86690680 65% 2029724 37555 0% / + Input6 = case Flavor of + posix -> Input5; + susv3 -> % there are 3 extra integers we want to skip + Input5a = parse_df_skip_word(Input5), % skip IUsed field + Input5b = parse_df_skip_word(Input5a), % skip IFree field + %% skip the value of ICap + '%' field + {_, Input5c} = parse_df_take_word_percent(Input5b), + Input5c + end, + + % path is the remaining string till end of line + {MountPath, Input7} = lists:splitwith(fun parse_df_is_not_eol/1, Input6), + % Trim the newlines + Remaining = lists:dropwhile(fun(X) -> not parse_df_is_not_eol(X) end, + Input7), + try + Kb = erlang:list_to_integer(KbStr), + Capacity = erlang:list_to_integer(CapacityStr), + {ok, {Kb, Capacity, MountPath}, Remaining} + catch error:badarg -> + {error, parse_df} + end. + % Parse per SUSv3 specification, notably recent OS X check_disks_susv3("", _Threshold) -> []; check_disks_susv3("\n", _Threshold) -> []; check_disks_susv3(Str, Threshold) -> - case io_lib:fread("~s~d~d~d~d%~d~d~d%~s", Str) of - {ok, [_FS, KB, _Used, _Avail, Cap, _IUsed, _IFree, _ICap, MntOn], RestStr} -> + case parse_df(Str, susv3) of + {ok, {KB, Cap, MntOn}, RestStr} -> if Cap >= Threshold -> set_alarm({disk_almost_full, MntOn}, []); diff --git a/lib/os_mon/src/memsup.erl b/lib/os_mon/src/memsup.erl index 0a9a883390..b69d657aa7 100644 --- a/lib/os_mon/src/memsup.erl +++ b/lib/os_mon/src/memsup.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-2018. 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. @@ -701,11 +701,12 @@ get_os_wordsize_with_uname() -> "sparc64" -> 64; "amd64" -> 64; "ppc64" -> 64; + "ppc64le" -> 64; "s390x" -> 64; _ -> 32 end. -clean_string(String) -> lists:flatten(string:tokens(String,"\r\n\t ")). +clean_string(String) -> lists:flatten(string:lexemes(String,[[$\r,$\n]|"\n\t "])). %%--Replying to pending clients----------------------------------------- diff --git a/lib/os_mon/src/os_mon.app.src b/lib/os_mon/src/os_mon.app.src index 8be94f65d4..6c9b0d7576 100644 --- a/lib/os_mon/src/os_mon.app.src +++ b/lib/os_mon/src/os_mon.app.src @@ -21,7 +21,7 @@ {application, os_mon, [{description, "CPO CXC 138 46"}, {vsn, "%VSN%"}, - {modules, [os_mon, os_mon_mib, os_sup, + {modules, [os_mon, os_sup, disksup, memsup, cpu_sup, os_mon_sysinfo, nteventlog]}, {registered, [os_mon_sup, os_mon_sysinfo, disksup, memsup, cpu_sup, os_sup_server]}, @@ -31,6 +31,4 @@ {start_memsup, true}, {start_os_sup, false}]}, {mod, {os_mon, []}}, - {runtime_dependencies, ["stdlib-2.0","snmp-4.25.1","sasl-2.4", - "otp_mibs-1.0.9","mnesia-4.12","kernel-3.0", - "erts-6.0"]}]}. + {runtime_dependencies, ["stdlib-2.0","sasl-2.4","kernel-3.0","erts-6.0"]}]}. diff --git a/lib/os_mon/src/os_mon_mib.erl b/lib/os_mon/src/os_mon_mib.erl deleted file mode 100644 index 9b5d2fbba6..0000000000 --- a/lib/os_mon/src/os_mon_mib.erl +++ /dev/null @@ -1,251 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1996-2016. 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. -%% -%% %CopyrightEnd% -%% --module(os_mon_mib). -%%%----------------------------------------------------------------- -%%% Description: This module implements the OS-MON-MIB. -%%% The tables are implemented as shadow tables with the module -%%% snmp_shadow_table. Here the update functions are implemented. -%%%----------------------------------------------------------------- - --include("../../otp_mibs/include/OTP-MIB.hrl"). - -%% API --export([load/1, unload/1]). - -%% Deprecated API --export([init/1, stop/1]). - --deprecated([{init,1,eventually}, - {stop,1,eventually}]). - -%% SNMP instrumentation --export([load_table/1, load_table/3, disk_table/1, disk_table/3, - mem_sys_mark/1, mem_proc_mark/1, disk_threshold/1]). - -%% SNMP shadow functions --export([update_load_table/0, update_disk_table/0]). - -%% Exported for internal use via rpc --export([get_load/1, get_disks/1]). - -%% Shadow tables --record(loadTable, { - loadErlNodeName, - loadSystemTotalMemory, - loadSystemUsedMemory, - loadLargestErlProcess, - loadLargestErlProcessUsedMemory, - loadCpuLoad, - loadCpuLoad5, - loadCpuLoad15, - loadOsWordsize, - loadSystemTotalMemory64, - loadSystemUsedMemory64, - loadLargestErlProcessUsedMemory64}). - --record(diskTable, - {key, diskDescr, diskKBytes, diskCapacity}). - -%% Shadow argument macros --define(loadShadowArgs, - {loadTable, string, record_info(fields, loadTable), 5000, - fun os_mon_mib:update_load_table/0}). - --define(diskShadowArgs, - {diskTable, {integer, integer}, record_info(fields, diskTable), 5000, - fun os_mon_mib:update_disk_table/0}). - -%% Misc --record(diskAlloc, {diskDescr, diskId}). - -%%%========================================================================= -%%% API -%%%========================================================================= - -%%------------------------------------------------------------------------- -%% load(Agent) -> ok | {error, Reason} -%% Agent - pid() | atom() -%% Reason - term() -%% Description: Loads the OTP-OS-MON-MIB -%%------------------------------------------------------------------------- -load(Agent) -> - MibDir = filename:join(code:priv_dir(os_mon), "mibs"), - snmpa:load_mibs(Agent, [filename:join(MibDir, "OTP-OS-MON-MIB")]). - -%%------------------------------------------------------------------------- -%% unload(Agent) -> ok | {error, Reason} -%% Agent - pid() | atom() -%% Reason - term() -%% Description: Unloads the OTP-OS-MON-MIB -%%------------------------------------------------------------------------- -unload(Agent) -> - snmpa:unload_mibs(Agent, ["OTP-OS-MON-MIB"]). - -%% To be backwards compatible -init(Agent) -> - load(Agent). -stop(Agent) -> - unload(Agent). - -%%%========================================================================= -%%% SNMP instrumentation -%%%========================================================================= -load_table(Op) -> - snmp_shadow_table:table_func(Op, ?loadShadowArgs). -load_table(Op, RowIndex, Cols) -> - snmp_shadow_table:table_func(Op, RowIndex, Cols, ?loadShadowArgs). - -disk_table(new) -> - Tab = diskAlloc, - Storage = ram_copies, - case lists:member(Tab, mnesia:system_info(tables)) of - true -> - case mnesia:table_info(Tab, storage_type) of - unknown -> - {atomic, ok}=mnesia:add_table_copy(Tab, node(), Storage); - Storage -> - catch delete_all(Tab) - end; - false -> - Nodes = [node()], - Props = [{type, set}, - {attributes, record_info(fields, diskAlloc)}, - {local_content, true}, - {Storage, Nodes}], - {atomic, ok} = mnesia:create_table(Tab, Props) - - end, - Rec = #diskAlloc{diskDescr = next_index, diskId = 1}, - ok = mnesia:dirty_write(Rec), - snmp_shadow_table:table_func(new, ?diskShadowArgs). - -disk_table(Op, RowIndex, Cols) -> - snmp_shadow_table:table_func(Op, RowIndex, Cols, ?diskShadowArgs). - -mem_sys_mark(get) -> - {value, memsup:get_sysmem_high_watermark()}; -mem_sys_mark(_) -> - ok. - -mem_proc_mark(get) -> - {value, memsup:get_procmem_high_watermark()}; -mem_proc_mark(_) -> - ok. - -disk_threshold(get) -> - {value, disksup:get_almost_full_threshold()}; -disk_threshold(_) -> - ok. - -%%%========================================================================= -%%% SNMP shadow functions -%%%========================================================================= -update_load_table() -> - delete_all(loadTable), - lists:foreach( - fun(Node) -> - case rpc:call(Node, os_mon_mib, get_load, [Node]) of - Load when is_record(Load,loadTable) -> - ok = mnesia:dirty_write(Load); - _Else -> - ok - end - end, [node() | nodes()]). - - -update_disk_table() -> - delete_all(diskTable), - node_update_disk_table( - otp_mib:erl_node_table(get_next, [], [?erlNodeName,?erlNodeOutBytes])). - -%%%======================================================================== -%%% Exported for internal use via rpc -%%%======================================================================== -get_load(Node) -> - {Total, Allocated, PidString, PidAllocated} = case memsup:get_memory_data() of - {MemTot, MemAlloc, undefined} -> {MemTot, MemAlloc, "undefined", 0}; - {MemTot, MemAlloc, {Pid, PidMem}} -> {MemTot, MemAlloc, pid_to_str(Pid), PidMem} - end, - OsWordsize = case memsup:get_os_wordsize() of - WS when is_integer(WS) -> WS; - _ -> 0 - end, - #loadTable{ - loadErlNodeName = atom_to_list(Node), - loadSystemTotalMemory = mask_int32(Total), - loadSystemUsedMemory = mask_int32(Allocated), - loadLargestErlProcess = PidString, - loadLargestErlProcessUsedMemory = mask_int32(PidAllocated), - loadCpuLoad = get_cpu_load(avg1), - loadCpuLoad5 = get_cpu_load(avg5), - loadCpuLoad15 = get_cpu_load(avg15), - loadOsWordsize = OsWordsize, - loadSystemTotalMemory64 = Total, - loadSystemUsedMemory64 = Allocated, - loadLargestErlProcessUsedMemory64 = PidAllocated - }. - -mask_int32(Value) -> Value band ((1 bsl 32) - 1). - -get_disks(NodeId) -> - element(1, - lists:mapfoldl( - fun({Descr, KByte, Capacity}, DiskId) -> - {#diskTable{key = {NodeId, DiskId}, - diskDescr = Descr, - diskKBytes = KByte, - diskCapacity = Capacity}, - DiskId + 1} - end, 1, disksup:get_disk_data())). - - -%%%======================================================================== -%%% Internal functions -%%%======================================================================== -node_update_disk_table([_, endOfTable]) -> - ok; - -node_update_disk_table([{[?erlNodeName | IndexList], NodeStr}, _]) -> - Disks = rpc:call(list_to_atom(NodeStr), os_mon_mib, get_disks, - IndexList), - lists:foreach(fun(Disk) -> - mnesia:dirty_write(Disk) - end, Disks), - node_update_disk_table(otp_mib:erl_node_table(get_next, - IndexList, - [?erlNodeName, - ?erlNodeOutBytes])). - -get_cpu_load(X) when X == avg1; X == avg5; X == avg15 -> - case erlang:round(apply(cpu_sup, X, [])/2.56) of - Large when Large > 100 -> - 100; - Load -> - Load - end. - -delete_all(Name) -> delete_all(mnesia:dirty_first(Name), Name). -delete_all('$end_of_table', _Name) -> done; -delete_all(Key, Name) -> - Next = mnesia:dirty_next(Name, Key), - ok = mnesia:dirty_delete({Name, Key}), - delete_all(Next, Name). - -pid_to_str(Pid) -> lists:flatten(io_lib:format("~w", [Pid])). |