#!/usr/bin/env python3
#
# Copyright (C) 2018 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
#

import sys
import os
import re
import syslog as sl

from vyos.config import Config
from vyos.util import call
from vyos import ConfigError

from vyos import airbag
airbag.enable()

arp_cmd = '/usr/sbin/arp'

def get_config():
  c = Config()
  if not c.exists('protocols static arp'):
    return None

  c.set_level('protocols static')
  config_data = {}
  
  for ip_addr in c.list_nodes('arp'):
    config_data.update(
        {
          ip_addr : c.return_value('arp ' + ip_addr + ' hwaddr')
        }
    )

  return config_data

def generate(c):
  c_eff = Config()
  c_eff.set_level('protocols static')
  c_eff_cnf = {}
  for ip_addr in c_eff.list_effective_nodes('arp'):
    c_eff_cnf.update(
        {
          ip_addr : c_eff.return_effective_value('arp ' + ip_addr + ' hwaddr')
        }
    )

  config_data = {
    'remove'  : [],
    'update'  : {}
  }
  ### removal
  if c == None:
    for ip_addr in c_eff_cnf:
      config_data['remove'].append(ip_addr)
  else:
    for ip_addr in c_eff_cnf:
      if not ip_addr in c or c[ip_addr] == None:
        config_data['remove'].append(ip_addr)

  ### add/update
  if c != None:
    for ip_addr in c:
      if not ip_addr in c_eff_cnf:
        config_data['update'][ip_addr] = c[ip_addr]
      if  ip_addr in c_eff_cnf:
        if c[ip_addr] != c_eff_cnf[ip_addr] and c[ip_addr] != None:
          config_data['update'][ip_addr] = c[ip_addr]

  return config_data

def apply(c):
  for ip_addr in c['remove']:
    sl.syslog(sl.LOG_NOTICE, "arp -d " + ip_addr)
    call(f'{arp_cmd} -d {ip_addr} >/dev/null 2>&1')

  for ip_addr in c['update']:
    sl.syslog(sl.LOG_NOTICE, "arp -s " + ip_addr + " " + c['update'][ip_addr])
    updated = c['update'][ip_addr]
    call(f'{arp_cmd} -s {ip_addr} {updated}')


if __name__ == '__main__':
  try:
    c = get_config()
    ## syntax verification is done via cli
    config = generate(c)
    apply(config)
  except ConfigError as e:
    print(e)
    sys.exit(1)