#!/usr/bin/python3
# -*- coding: utf-8 -*-
#
# NorNet Setup
# Copyright (C) 2013-2024 by Thomas Dreibholz
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# 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/>.
#
# Contact: dreibh@simula.no

import re
import os

from ipaddress import ip_address, IPv4Address, IPv4Interface, IPv6Address, IPv6Interface

# NorNet
from NorNetTools         import *
from NorNetConfiguration import *
from NorNetAPI           import *
from NorNetProviderSetup import *
from NorNetNodeSetup     import *
from SysSetupCommons     import *


# ###### Write NTP configuration file ##############################################
def writeNTPConfiguration(outputName, nodeIndex, siteIndex, defaultProvider, ntpServerList):
   outputFile = codecs.open(outputName, 'w', 'utf-8')

   outputFile.write('# ====== Drift File ======\n')
   outputFile.write('driftfile /var/lib/ntp/ntp.drift\n\n')

   outputFile.write('# ====== Statistics ======\n')
   outputFile.write('statsdir /var/log/ntpstats/\n')
   outputFile.write('filegen loopstats file loopstats type day enable\n')
   outputFile.write('filegen peerstats file peerstats type day enable\n')
   outputFile.write('filegen clockstats file clockstats type day enable\n\n')

   outputFile.write('# ====== Generic Access Restrictions ======\n')
   outputFile.write('restrict default ignore\n')
   if len(ntpServerList) != 0:
      for version in [ 4, 6 ]:
         fullNorNetNetwork = makeNorNetIP(0, 0, 0, version)
         outputFile.write('restrict ' + str(fullNorNetNetwork.ip) + ' mask ' + str(fullNorNetNetwork.netmask) + ' nomodify\n')
   outputFile.write('restrict 127.0.0.1\n')
   outputFile.write('restrict ::1\n\n')

   outputFile.write('# ====== Fudge Clock ======\n')
   outputFile.write('server 127.127.1.0\n')
   outputFile.write('fudge 127.127.1.0 stratum 10\n\n')

   outputFile.write('# ====== NTP server ======\n')
   if len(ntpServerList) == 0:
      for version in [ 6 ]:
         ntpServer = makeNorNetIP(defaultProvider, siteIndex, NorNet_NodeIndex_Tunnelbox, version)
         outputFile.write('server    ' + str(ntpServer.ip) + '\n')
         outputFile.write('restrict  ' + str(ntpServer.ip) + '\n')
   else:
      for ntpServer in ntpServerList:
         outputFile.write('server    ' + ntpServer + '\n')
         outputFile.write('restrict  ' + ntpServer + '\n')

   outputFile.close()



# ###### Main program #######################################################
if ((len(sys.argv) < 10) or (sys.argv[4] != '-site') or (sys.argv[6] != '-interface') or (sys.argv[8] != '-providers')):
   error('Usage: ' + sys.argv[0] + ' variant FQDN node_index -site site_index -interface interface_name -providers default_provider[,provider2,...] [-controlbox] [-bridge interface] [-ntp server[,server2,...]]')

variant    = sys.argv[1]
hostName   = getHostnameFromFQDN(sys.argv[2])
domainName = getDomainFromFQDN(sys.argv[2])
nodeIndex  = int(sys.argv[3])
if ((nodeIndex < 1) or (nodeIndex > 255)):
   error('Bad node index!')
siteIndex = int(sys.argv[5])
if ((siteIndex < 1) or (siteIndex > 255)):
   error('Bad site index!')
interfaceName = sys.argv[7]
controlBoxMode  = False
bridgeInterface = None
ntpServers      = None
if len(sys.argv) > 10:
    i = 10
    while i < len(sys.argv):
       if sys.argv[i] == '-controlbox':
          controlBoxMode = True
       elif sys.argv[i] == '-bridge':
          if i + 1 < len(sys.argv):
             bridgeInterface = sys.argv[i + 1]
             i = i + 1
          else:
             error('No bridge interface given!')
       elif sys.argv[i] == '-ntp':
          if i + 1 < len(sys.argv):
             ntpServers = sys.argv[i + 1]
             i = i + 1
          else:
             error('No NTP servers given!')
       else:
          error('Bad argument: ' + sys.argv[i])
       i = i + 1

loadNorNetConfiguration(False)

defaultProvider = -1
providerList    = []
for providerName in sys.argv[9].split(','):
   providerIndex = -1
   try:
      providerIndex = int(providerName)
   except:
      for p in NorNet_ProviderList:
         if ((NorNet_ProviderList[p][0] == providerName) or
             (NorNet_ProviderList[p][1] == providerName)):
            providerIndex = p
            break
   if ((providerIndex <= 0) or (providerIndex > 255)):
      error("Bad provider " + providerName)
   if len(providerList) == 0:
      defaultProvider = providerIndex
   providerList.append(providerIndex)

ntpServerList = []
if ntpServers != None:
   for ntpServer in ntpServers.split(','):
      success = False
      for type in [ socket.AF_INET6, socket.AF_INET ]:
         try:
            result = socket.getaddrinfo (ntpServer, 123, type, socket.SOCK_DGRAM)
            for resultItem in result:
               ntpServerList.append(str(ip_address(resultItem[4][0])))
               success = True
         except:
            continue
      if success == False:
         error('Unable to resolve NTP server name: ' + ntpServer)
   ntpServerList = sorted(set(ntpServerList))

print(('Node:        ' + hostName + ' . ' + domainName))
print(('Node Index:  ' + str(nodeIndex)))
print(('Site Index:  ' + str(siteIndex)))
print(('Interface:   ' + interfaceName))
print(('Bridge:      ' + str(bridgeInterface)))
print(('Providers:   ' + str(providerList)))
print(('NTP Servers: ' + str(ntpServerList)))


writeInterfaceConfiguration('.new', variant, interfaceName, controlBoxMode,
                            hostName, domainName, nodeIndex, siteIndex, providerList, defaultProvider,
                            bridgeInterface)
writeHosts('hosts.new', hostName, domainName)
if variant != 'FreeBSD':
   writeHostname('hostname.new', hostName, domainName)
writeProxyConfiguration('.new', domainName, variant, controlBoxMode)
writeNTPConfiguration('ntp.new', nodeIndex, siteIndex, defaultProvider, ntpServerList)
writeSysctlConfiguration('sysctl.new', interfaceName)

# Create AutoFS configuration only if we are not on the file server itself!
# (otherwise: just add empty AutoFS configuration)
weAreOnFileServer = ((siteIndex == NorNet_SiteIndex_FileSrv) and
                     (nodeIndex == NorNet_NodeIndex_FileSrv))
makeAutoFSConfiguration(weAreOnFileServer, siteIndex, nodeIndex, True)
