26 October 2009

Quick Guide to SNMP and Mnesia

There is a good documentation on SNMP, Mnesia and MIB Writing, but it's impossible to find any good document that will guide you step-by-step to understanding howto create SNMP System which you can fill from Mnesia. So let's start ...

Imagine, you need to write database for some company, and one of the tables, let's name it empTable (keep in mind that name of the table in Mnesia and name of the table in MIB-file must be the same) contains such attributes: Id, Name, Phone Number.

First, we are going to write small MIB file w/ name EmpMIB.mib:



EmpMIB DEFINITIONS ::= BEGIN

IMPORTS
DisplayString, RowStatus FROM SNMPv2-TC
OBJECT-GROUP FROM SNMPv2-CONF
OBJECT-TYPE, MODULE-IDENTITY, enterprises FROM SNMPv2-SMI;

empMib MODULE-IDENTITY
LAST-UPDATED "200910261250"
ORGANIZATION "Riot"
CONTACT-INFO "E-mail: dmitriy.budashny@gmail.com
Dmitriy Budashny"
DESCRIPTION
"Module for employees"
::= { enterprises 66666 }


emp OBJECT IDENTIFIER ::= { empMib 5 }

empTable OBJECT-TYPE
SYNTAX SEQUENCE OF EmpEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"A table with information about employees."
::= { emp 1}

empEntry OBJECT-TYPE
SYNTAX EmpEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
""
INDEX { empDepNo, empName }
::= { empTable 1 }

EmpEntry ::=
SEQUENCE {
empDepNo INTEGER,
empName DisplayString,
empTelNo DisplayString,
empStatus RowStatus
}

empDepNo OBJECT-TYPE
SYNTAX INTEGER ( 1..65535 )
MAX-ACCESS read-write
STATUS current
DESCRIPTION
"Number of department"
::= { empEntry 1 }

empName OBJECT-TYPE
SYNTAX DisplayString
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"The name of the employee"
::= { empEntry 2 }

empTelNo OBJECT-TYPE
SYNTAX DisplayString
MAX-ACCESS read-write
STATUS current
DESCRIPTION
"The phone number"
::= { empEntry 3 }

empStatus OBJECT-TYPE
SYNTAX RowStatus
MAX-ACCESS read-write
STATUS current
DESCRIPTION
"Status ?!"
::= { empEntry 4 }

empGroups OBJECT IDENTIFIER ::= { empMib 6 }

empGroup OBJECT-GROUP
OBJECTS { empDepNo, empName, empTelNo, empStatus }
STATUS current
DESCRIPTION
"A collection of objects providing employee information"
::= { empGroups 1 }


END


Also we need company.erl file:



-module(company).

-export([init/0, insert_record/1]).

init() ->
mnesia:create_table([{name, empTable},
{snmp, [{key, {integer, string}}]},
{attributes, [key, telno, row_status]}]).

insert_record(Record) ->
Fun = fun() ->
mnesia:write(Record)
end,
mnesia:transaction(Fun).

Then we will create config files for snmp agent and snmp manager:


snmp/agent/conf/agent.conf:

{intAgentIpAddress, [127,0,0,1]}.
{intAgentUDPPort, 4000}.
{snmpEngineID, "agent's engine"}.
{snmpEngineMaxMessageSize, 484}.

snmp/agent/conf/community.conf:

{"public", "public", "initial", "", ""}.

snmp/agent/conf/standard.conf:

{sysName, "SNMP Quick Start HowTo Demo"}.
{sysDescr, "Erlang/OTP Agent"}.
{sysContact, "vances@motivity.ca"}.
{sysLocation, "5th Floor machine room, rack 21A"}.
{sysObjectID, [3,6,1,4,1,193,19]}. % {ericsson otp}
{sysServices, 72}.
{snmpEnableAuthenTraps, enabled}.

snmp/agent/conf/vacm.conf:

{vacmViewTreeFamily, "restricted", [1,3,6,1], included, null}.%% vacmViewTreeFamilyTable in the SNMP-VIEW-BASED-ACM-MIB.
{vacmSecurityToGroup, v2c, "initial", "initial"}.
{vacmSecurityToGroup, usm, "initial", "initial"}.
{vacmAccess, "initial", "", any, noAuthNoPriv, exact, "restricted", "", "restricted"}.
{vacmAccess, "initial", "", usm, authNoPriv, exact, "internet", "internet", "internet"}.
{vacmAccess, "initial", "", usm, authPriv, exact, "internet", "internet", "internet"}.
{vacmViewTreeFamily, "internet", [1,3,6,1], included, null}.
{vacmViewTreeFamily, "restricted", [1,3,6,1], included, null}.

snmp/manager/conf/agents.conf:

{"simple_user", "otp agent", "public", [127,0,0,1], 4000, "agent's engine",
infinity, 484, v2, v2c, "initial", noAuthNoPriv}.

snmp/manager/conf/manager.conf:

{port, 5000}.
{address, [127,0,0,1]}.
{engine_id, "manager's engine"}.
{max_message_size, 484}.

snmp/manager/conf/users.conf:

{"simple_user", snmpm_user_default, undefined}.

And now the real fun begins:


dym@enfer:~/tmp/proga/erlang/mnesia_snmp$ mkdir EmpDB
dym@enfer:~/tmp/proga/erlang/mnesia_snmp$ erl -sname ms@enfer -mnesia dir '"./EmpDB"' -config snmp/
Erlang R13B02 (erts-5.7.3) [source] [smp:2:2] [rq:2] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.7.3 (abort with ^G)
(ms@enfer)1> mnesia:create_schema([node()]).
ok
(ms@enfer)2> mnesia:start().
ok
(ms@enfer)3> c(company).
{ok,company}
(ms@enfer)4> company:init().
{atomic,ok}
(ms@enfer)5> application:start(snmp).

=ERROR REPORT==== 26-Oct-2009::15:46:52 ===
** Configuration error: [FRAMEWORK-MIB]: missing context.conf file => generating a default file

=INFO REPORT==== 26-Oct-2009::15:46:52 ===
[ snmp : agent : snmp_user_based_sm_mib : <0.95.0> ]
USM: Incomplete configuration. Generating empty usm.conf.
ok
(ms@enfer)6> application:which_applications().
[{snmp,"SNMP CXC 138 13","4.13.5"},
{mnesia,"MNESIA CXC 138 12","4.4.11"},
{stdlib,"ERTS CXC 138 10","1.16.3"},
{kernel,"ERTS CXC 138 10","2.13.3"}]

So you see, applications mnesia and snmp are running


(ms@enfer)7> snmpc:compile("EmpMIB", [{db, mnesia}]).
{ok,"./EmpMIB.bin"}
(ms@enfer)8> snmpa:load_mibs(["EmpMIB"]).
ok

As of now our table is empty, but we are going to change this:


(ms@enfer)9> company:insert_record({empTable, {1,"Dmitriy Budashny"}, "1201", 1}).
{atomic,ok}

Let's open another console:


dym@enfer:~$ snmpwalk -c public -v 2c localhost:4000 .1.3.6.1.4.1.66666
SNMPv2-SMI::enterprises.66666.5.1.1.1.1.16.68.109.105.116.114.105.121.32.66.117.100.97.115.104.110.121 = INTEGER: 1
SNMPv2-SMI::enterprises.66666.5.1.1.2.1.16.68.109.105.116.114.105.121.32.66.117.100.97.115.104.110.121 = STRING: "Dmitriy Budashny"
SNMPv2-SMI::enterprises.66666.5.1.1.3.1.16.68.109.105.116.114.105.121.32.66.117.100.97.115.104.110.121 = STRING: "1201"
SNMPv2-SMI::enterprises.66666.5.1.1.4.1.16.68.109.105.116.114.105.121.32.66.117.100.97.115.104.110.121 = INTEGER: 1

And then we'll add another guy with phone number 1305:


(ms@enfer)10> company:insert_record({empTable, {2,"Andrew Sidlyarenko"}, "1305", 1}).
{atomic,ok}

Run snmpwalk again:


dym@enfer:~$ snmpwalk -c public -v 2c localhost:4000 .1.3.6.1.4.1.66666
SNMPv2-SMI::enterprises.66666.5.1.1.1.1.16.68.109.105.116.114.105.121.32.66.117.100.97.115.104.110.121 = INTEGER: 1
SNMPv2-SMI::enterprises.66666.5.1.1.1.2.18.65.110.100.114.101.119.32.83.105.100.108.121.97.114.101.110.107.111 = INTEGER: 2
SNMPv2-SMI::enterprises.66666.5.1.1.2.1.16.68.109.105.116.114.105.121.32.66.117.100.97.115.104.110.121 = STRING: "Dmitriy Budashny"
SNMPv2-SMI::enterprises.66666.5.1.1.2.2.18.65.110.100.114.101.119.32.83.105.100.108.121.97.114.101.110.107.111 = STRING: "Andrew Sidlyarenko"
SNMPv2-SMI::enterprises.66666.5.1.1.3.1.16.68.109.105.116.114.105.121.32.66.117.100.97.115.104.110.121 = STRING: "1201"
SNMPv2-SMI::enterprises.66666.5.1.1.3.2.18.65.110.100.114.101.119.32.83.105.100.108.121.97.114.101.110.107.111 = STRING: "1305"
SNMPv2-SMI::enterprises.66666.5.1.1.4.1.16.68.109.105.116.114.105.121.32.66.117.100.97.115.104.110.121 = INTEGER: 1
SNMPv2-SMI::enterprises.66666.5.1.1.4.2.18.65.110.100.114.101.119.32.83.105.100.108.121.97.114.101.110.107.111 = INTEGER: 1

PS: It's not complete guide about Mnesia or SNMP or MIBs. If you want more, I can recommend you some books/articles/sites:


Total Pageviews

Followers