jonnystorm / snmp-elixir

An SNMP client library for Elixir
Mozilla Public License 2.0
33 stars 12 forks source link

Erlang function_clause error while SNMP setting an octet-string #48

Closed fbettag closed 4 years ago

fbettag commented 4 years ago

Hi there,

i am trying to set a label for an APC power bar port (its easier for documentation). So i am trying to set the value "newlabel" for PDU Port 1 on 10.4.20.11.

iex(1)> creds = Test.SNMP.credentials                               
%SNMP.CommunityCredential{community: 'testcommunity', sec_model: :v1, version: :v1}
iex(2)> Test.SNMP.APC.set_port_label(creds, "10.4.20.11", 1, "newlabel")
[debug] Will register agent snmp://10.4.20.11 with target [197, 16, 229, 7, 48, 247, 54, 229, 42, 198, 123, 7, 161, 31, 27, 197, 96, 184, 137, 9] and config [engine_id: [128, 0, 0, 0, 6], address: [10, 4, 20, 11], port: 161, tdomain: :transportDomainUdpIpv4, community: 'testcommunity', sec_model: :v1, version: :v1].
[error] [ :snmp : manager : :snmpm_mpd : #PID<0.406.0> ] 
MPD: USER ERROR: failed encoding pdu: (pdu: {:pdu, :"set-request", 362240452, :noError, 0, [{:varbind, [1, 3, 6, 1, 4, 1, 318, 1, 1, 4, 5, 2, 1, 3, 1], :undefined, "newlabel", :undefined}]}, community: 'testcp,,imoty'): 
{:function_clause, [{:snmp_pdus, :enc_value, [:undefined, "newlabel"], [file: 'snmp_pdus.erl', line: 660]}, {:snmp_pdus, :enc_VarBind_attributes, 1, [file: 'snmp_pdus.erl', line: 657]}, {:snmp_pdus, :enc_varbind, 1, [file: 'snmp_pdus.erl', line: 650]}, {:lists, :map, 2, [file: 'lists.erl', line: 1239]}, {:snmp_pdus, :enc_VarBindList, 1, [file: 'snmp_pdus.erl', line: 645]}, {:snmp_pdus, :enc_pdu2, 1, [file: 'snmp_pdus.erl', line: 607]}, {:snmp_pdus, :enc_pdu, 2, [file: 'snmp_pdus.erl', line: 597]}, {:snmpm_mpd, :generate_v1_v2c_msg, 4, [file: 'snmpm_mpd.erl', line: 597]}, {:snmpm_net_if, :handle_send_pdu, 6, [file: 'snmpm_net_if.erl', line: 934]}, {:snmpm_net_if, :maybe_handle_send_pdu_mt, 6, [file: 'snmpm_net_if.erl', line: 923]}, {:snmpm_net_if, :handle_cast, 2, [file: 'snmpm_net_if.erl', line: 537]}, {:gen_server, :try_dispatch, 4, [file: 'gen_server.erl', line: 637]}, {:gen_server, :handle_msg, 6, [file: 'gen_server.erl', line: 711]}, {:proc_lib, :init_p_do_apply, 3, [file: 'proc_lib.erl', line: 249]}]}

[error] Send failed: {:function_clause, [{:snmp_pdus, :enc_value, [:undefined, "newlabel"], [file: 'snmp_pdus.erl', line: 660]}, {:snmp_pdus, :enc_VarBind_attributes, 1, [file: 'snmp_pdus.erl', line: 657]}, {:snmp_pdus, :enc_varbind, 1, [file: 'snmp_pdus.erl', line: 650]}, {:lists, :map, 2, [file: 'lists.erl', line: 1239]}, {:snmp_pdus, :enc_VarBindList, 1, [file: 'snmp_pdus.erl', line: 645]}, {:snmp_pdus, :enc_pdu2, 1, [file: 'snmp_pdus.erl', line: 607]}, {:snmp_pdus, :enc_pdu, 2, [file: 'snmp_pdus.erl', line: 597]}, {:snmpm_mpd, :generate_v1_v2c_msg, 4, [file: 'snmpm_mpd.erl', line: 597]}, {:snmpm_net_if, :handle_send_pdu, 6, [file: 'snmpm_net_if.erl', line: 934]}, {:snmpm_net_if, :maybe_handle_send_pdu_mt, 6, [file: 'snmpm_net_if.erl', line: 923]}, {:snmpm_net_if, :handle_cast, 2, [file: 'snmpm_net_if.erl', line: 537]}, {:gen_server, :try_dispatch, 4, [file: 'gen_server.erl', line: 637]}, {:gen_server, :handle_msg, 6, [file: 'gen_server.erl', line: 711]}, {:proc_lib, :init_p_do_apply, 3, [file: 'proc_lib.erl', line: 249]}]}
{:error,
 {:function_clause,
  [
    {:snmp_pdus, :enc_value, [:undefined, "newlabel"],
     [file: 'snmp_pdus.erl', line: 660]},
    {:snmp_pdus, :enc_VarBind_attributes, 1, [file: 'snmp_pdus.erl', line: 657]},
    {:snmp_pdus, :enc_varbind, 1, [file: 'snmp_pdus.erl', line: 650]},
    {:lists, :map, 2, [file: 'lists.erl', line: 1239]},
    {:snmp_pdus, :enc_VarBindList, 1, [file: 'snmp_pdus.erl', line: 645]},
    {:snmp_pdus, :enc_pdu2, 1, [file: 'snmp_pdus.erl', line: 607]},
    {:snmp_pdus, :enc_pdu, 2, [file: 'snmp_pdus.erl', line: 597]},
    {:snmpm_mpd, :generate_v1_v2c_msg, 4, [file: 'snmpm_mpd.erl', line: 597]},
    {:snmpm_net_if, :handle_send_pdu, 6, [file: 'snmpm_net_if.erl', line: 934]},
    {:snmpm_net_if, :maybe_handle_send_pdu_mt, 6,
     [file: 'snmpm_net_if.erl', line: 923]},
    {:snmpm_net_if, :handle_cast, 2, [file: 'snmpm_net_if.erl', line: 537]},
    {:gen_server, :try_dispatch, 4, [file: 'gen_server.erl', line: 637]},
    {:gen_server, :handle_msg, 6, [file: 'gen_server.erl', line: 711]},
    {:proc_lib, :init_p_do_apply, 3, [file: 'proc_lib.erl', line: 249]}
  ]}}

the code in question is below. getting the octet-string label works like a charm. (as opposed to their Unsigned32 and Integer32 (as written in the other ticket).


  @label_oid [1, 3, 6, 1, 4, 1, 318, 1, 1, 4, 5, 2, 1, 3]
  def get_port_label(credentials, host, pdu_port) do
    oid = [%{oid: @label_oid ++ [pdu_port]}]
    result = %{uri: URI.parse("snmp://#{host}"), credential: credentials, varbinds: oid}
    with [%{:value => state}] <- SNMP.request(result) do
      state
    end
  end

  def set_port_label(credentials, host, pdu_port, label) do
    oid = [%{oid: (@label_oid ++ [pdu_port]), value: label}]
    result = %{uri: URI.parse("snmp://#{host}"), credential: credentials, varbinds: oid}
    with [{:ok, %{value: new_label}}] <- SNMP.request(result) do
      new_label
    end
  end

Thank you very much in advance

jonnystorm commented 4 years ago

At the moment, we're using OTP's :snmpm.sync_set/5 to handle SNMPSET. According to that documentation,

When var_and_val() is {oid(), value()}, the manager makes an educated guess based on the loaded mibs.

which holds, here, as this is precisely the format we pass now. Still, none of the code cited by the trace seems to check MIBs for type information.

I think that, particularly in cases where obtaining the requisite MIB is unreasonable or impossible, we need to accept type hints from the varbind input. I'll add this and let you know once it's available.

fbettag commented 4 years ago

thanks! i am not using any mibs really, i just have the oids as macros up top and use @bla_oid <> [port_id] or whatever to concat them. :)

jonnystorm commented 4 years ago

@fbettag Try again while using one of the value_type() atoms accepted by :snmpm. In the case that you're setting an octet string, try the :s shorthand type, or feel free to use :"OCTET STRING" if it provides better readability.

Let me know how it goes!

fbettag commented 4 years ago

Hi there, sorry i was on vacation for a week :)

I tested the new code, and while :s works like a charm, using :"OCTET STRING" yields:

[error] Unexpected result: {:error, {:invalid_value_type, :"OCTET STRING"}}

fbettag commented 4 years ago

and thank you very much! this helps me a lot!

jonnystorm commented 4 years ago

Yes, it seems I misinterpreted the OTP code. In particular, this function is responsible for interpreting the type field in varbinds:

var_and_value_to_varbind({Oid, Type, Value}, MiniMIB) ->
    Oid2 = flatten_oid(Oid, MiniMIB),
    #varbind{oid          = Oid2,
       variabletype = char_to_type(Type),
       value        = Value};
var_and_value_to_varbind({Oid, Value}, MiniMIB) ->
    Oid2 = flatten_oid(Oid, MiniMIB),
    #varbind{oid          = Oid2,
       variabletype = oid2type(Oid2, MiniMIB),
       value        = Value}.

char_to_type(i) ->
    'INTEGER';
char_to_type(u) ->
    'Unsigned32';
char_to_type(g) -> % Gauge, Gauge32
    'Unsigned32';
char_to_type(b) ->
    'BITS';
char_to_type(ip) ->
    'IpAddress';
char_to_type(ia) ->
    'IpAddress';
char_to_type(op) ->
    'Opaque';
char_to_type(c32) ->
    'Counter32';
char_to_type(c64) ->
    'Counter64';
char_to_type(tt) ->
    'TimeTicks';
char_to_type(o) ->
    'OBJECT IDENTIFIER';
char_to_type(s) ->
    'OCTET STRING';
char_to_type(C) ->
    throw({error, {invalid_value_type, C}}).

Incidentally, this only handles the short form atom value types rather than the long form SNMP types. Sorry for misleading you!

fbettag commented 4 years ago

Absolutely not a problem :) thank you