[erlang-questions] Setting virtual IP via Erlang

Claes Wikstrom klacke@REDACTED
Fri Dec 8 00:54:23 CET 2006


t ty wrote:
> Hello,
> 
> I need to failover a virtual IP b/w machines purely in Erlang avoiding
> heartbeat/stonith.
> 
> Klacke gave an answer to this question in 2002
> http://www1.erlang.org/ml-archive/erlang-questions/200212/msg00049.html
> 

I've got linux-only in erlang + os:cmd + /sbin/ip +  /sbin/arping
that does this.

It's always going to be very OS specific

Here is one good function:


%% Send four unsolicited / gratuitous ARPs on interface Ifc

garp(Ifc, IP) ->
     garp(Ifc, IP, 4, 250).


%% Send <Count> unsolicited / gratuitous ARPs for <IP> on interface
%% <Ifc> with <Delay> milliseconds in between
garp(Ifc, IP, Count, Delay) when Count>0 ->
     Loop = fun (1, F) ->
                    send_garps(Ifc, IP);
                (N, F) ->
                    send_garps(Ifc, IP),
                    timer:sleep(Delay),
                    F(N-1, F)
            end,
     spawn(fun () ->
                   Loop(Count, Loop)
           end).


%% Use this function to send garps
send_garps(Ifc, IP) ->
     send_garps(Ifc, IP, 1, 0).

%%
%% arping -q -c <N> -w <Delay> -U -I <Ifc> -s <IP> <IP>
%%
%% Note: don't send more than one through this interface - it ties up the
%% isd_rtarp program in a loop.
%%
send_garps(Ifc, IP, N, Delay) ->
     IPStr = ipcalc:format(IP),
     Cmd = io_lib:format("sudo /sbin/arping -q -c ~p -w ~p -U -I ~s -s ~s ~s\n",
                         [N, Delay, Ifc, IPStr, IPStr]),
     case os:cmd(Cmd) of
         [] ->
             ok;
         ErrStr ->
             error_logger:format("output from vip:garp/4 : ~s~n", [ErrStr])
     end.






os_cmd(Str) ->
     case os:cmd(Str) of
         [] ->
             ok;
         Err ->
             error_logger:format("CMD ~s failed with ~n~s~n",
                                 [Str, Err])
     end.






-----------------

and here is how to bring up a VIP



%% This is version two of this code which
%% will bring up the VIp on the primary iface (i.e flip addresses)
%% This is due to racoon not working very well with
%% secondary interfaces

vipup(IP, NumBitsInMask, IfName, DefGw) ->
     OldStr = string:tokens(
             os:cmd(["ip addr ls ", IfName,
                     " | grep inet | head -n 1 | awk '{print $2 \" \" $4}'"]),
             " \n"),
     case OldStr of
         [OldIp, OldBcast] ->
             %% Remove old address completely
             os_cmd(["sudo ip addr del ", OldIp,
                     " dev ", IfName]),

             %% bring up VIP
             os_cmd(io_lib:format("sudo ip addr add ~s/~w broadcast ~s"
                                  " dev ~s",
                                  [ipcalc:format(IP), NumBitsInMask,
                                   OldBcast, IfName])),

             %% and bring up the old addr on a secondary iface
             os_cmd(io_lib:format("sudo ip addr add ~s broadcast ~s"
                                  " dev ~s label ~s:orig",
                                  [OldIp, OldBcast,
                                   IfName, IfName])),


             %% since we for a short while didn't have any IP
             %% at all, we lost our defualt gw,
             os_cmd(io_lib:format("sudo route add default gw ~s",
                                  [ipcalc:format(DefGw)])),


             NewIp = lists:flatten(
                       io_lib:format("~s/~w", [ipcalc:format(IP), NumBitsInMask])),


             %% The port command will flip back to the old IP
             PortCmd = "/usr/bin/sudo " ++ code:priv_dir(kdb) ++ "/autoifdown.sh " +
+
                 OldIp ++ " " ++ NewIp ++ " " ++
                 OldBcast ++ " " ++ IfName ++ " " ++ ipcalc:fformat(DefGw),

             io:format("Cmd=~s~n", [PortCmd]),
             garp(IfName, IP),

             error_logger:format("Brought up IP ~s/~w on ~s~n",
                                 [ipcalc:format(IP), NumBitsInMask,IfName]),

             %% Now start the vip auto-downer
             %% which possibly also has to stop racoon

             open_port({spawn, PortCmd}, [stream])
     end.



-- and then the autoifdown script

#!/bin/sh



oldip=$1
newip=$2
bcast=$3
iface=$4
defgw=$5


read
echo "Autobring VIP down " 1>&2
echo "" 1>&2

ip addr del ${newip} dev ${iface}
ip addr add ${oldip} broadcast ${bcast} dev ${iface}
route add default gw ${defgw}

##ip addr flush dev eth0 label eth0:svip











More information about the erlang-questions mailing list