# Copyright (C) 2007 SPARTA, Inc.
# This software is licensed under the GPLv3 license, included in
# ./GPLv3-LICENSE.txt in the source distribution

from seer import CIDR
from seerplatform import execAndRead, pipeIn
import re
import sys
import socket
import logging

class MyState(object):

	def __init__(self):
		self.LoadEID()
		self.LoadControlInfo()
		self.LoadIfConfig()
		self.LoadTopoFile()


	def LoadEID(self):
		""" Load the nickname file to get the node, experiment and project names """
		try:
			file = open('/var/emulab/boot/swapper', 'r')
			self.swapper = file.readline().strip()
			file.close()

			file = open('/var/emulab/boot/nickname', 'r')
			line = file.readline()
			file.close()

			(self.node, self.exp, self.proj) = line.strip().split('.')
			self.eid = self.proj+"/"+self.exp
			self.controlip = socket.gethostbyname(line.strip())
		except IOError, e:
			logging.error("Can't load node info from /var/emulab/boot/nickname: %s" % (e))


	def LoadControlInfo(self):
		""" Load the control IP address and IF name files """
		myif = open('/var/emulab/boot/controlif', 'r')
		self.controlif = myif.readline().strip()
		myif.close()


	def LoadIfConfig(self):
		""" Load all of the interface info from emulab/boot/tmcc/ifconfig """
		self.iplist = {}
		self.iflist = {}
		name = inet = mask = mac = ''

		iffile = pipeIn('/usr/local/etc/emulab/tmcc ifconfig')

		# Split into lines
		for line in iffile.readlines():
			# Split into tokens
			for piece in line.split():
				# It is now key=val
				keyval = piece.split('=')
				if keyval[0] == 'INET':
					inet = keyval[1]
				elif keyval[0] == 'MASK':
					mask = keyval[1]
				elif keyval[0] == 'MAC':
					mac = keyval[1]

			if (sys.platform == 'cygwin'):
				name = execAndRead("/usr/seer/bin/ip2pcapif %s" % (inet)).strip()
			else:
				name = execAndRead("/usr/local/etc/emulab/findif %s" % (mac)).strip()

			self.iplist[inet] = (name, mac, mask)
			self.iflist[name] = (inet, mac, mask)

		iffile.close()

	def GetIPList(self):
		""" Return the list of experimental IP addresses for this node """
		return self.iplist.keys()

	def GetIFList(self):
		""" Return the list of experimental interfaces for this node """
		return self.iflist.keys()

	def GetIfInfo(self, ip):
		""" Return the interface info for a given IP address """
		return self.iplist.get(ip)


	def LoadTopoFile(self):
		"""
		# nodes: vname,links
		control,link5:10.1.1.3
		node1,link5:10.1.1.2
		# lans: vname,mask,cost
		link5,255.255.255.0,1
		"""
		readtype = 0
		ips = {}
		nodes = {}
		interfaces = {}
		lanbases = {}
		lans = {}

		topomap = open('/var/emulab/boot/topomap', 'r');
		for line in topomap.readlines():
			if line.startswith('# nodes'):
				readtype = 1
				continue
			if line.startswith('# lans'):
				readtype = 2
				continue

			if (readtype == 1):
				details = re.split('[, \n]+', line.strip())
				node = details.pop(0)
				nodes[node] = {}
				for link in details:
					if ':' not in link: continue
					(linkname, linkip) = link.split(':')
					nodes[node][linkname] = linkip
					ips[linkip] = node
					if linkname not in interfaces: # Is there a nicer way to do this, like automagically
						interfaces[linkname] = {}
					interfaces[linkname][node] = linkip
					lanbases[linkname] = linkip # overwrites

			elif (readtype == 2):
				(linkname, netmask, cost) = line.split(',')	
				lans[linkname] = CIDR(basestr=lanbases[linkname], maskstr=netmask)

		topomap.close()
		
		## Sanity check the assigned IP addresses and their subnetmasks 
		for l in interfaces:
			for node in interfaces[l]:
				# Check to see if all the IPs in this subnet fit given the subnetmask
				c = CIDR(basestr=interfaces[l][node], maskstr=lans[l].maskstr)
				if (c.basestr != lans[l].basestr):
					logging.error("Some of the defined subnets are not valid (%s)." % (l))
					logging.error("\t%s\n\t%s\n" % (interfaces[l], lans[l]))

		self.topoips = ips  # ips[ip] = node
		self.nodes = nodes	# nodes[node][linklan] = IP
		self.interfaces = interfaces # interfaces[linklan][node] = IP
		self.lans = lans # lans[linklan] = CIDR for subnet
		

	def GetNodeForIP(self, ip):
		""" Lookup the node that is linked to this IP address """
		return self.topoips.get(ip)

	def GetControlNameForIP(self, ip):
		""" Return control plane hostname """
		return ".".join([self.topoips.get(ip), self.exp, self.proj])

	def GetIPForNode(self, node):
		""" To make sure everyone gets the same IP, we revert to DNS lookups """
		try:
			return socket.gethostbyname(node)
		except Exception, e:
			return None


# Small test if running this file directly
if __name__ == "__main__":
	x = MyState()
	print x.__dict__
