#Author: Rodrigo de Souza Couto
#E-mail:souza@gta.ufrj.br
#Instituion: Grupo de Teleinformatica e Automacao - PEE/COPPE - DEL/POLI - UFRJ
#Description:An object of the MeasureInterfaceDefault class stores information about Vm throughput
#This object must call a measure daemon, in order to store information
#and must be called by an upper application in order to gather information.

#Falta tratar, se tiver, questoes de tempo (olhar codigo antigo)

import socket
import os
import sys
import time
import pickle
import threading
from statistics import *

class MeasureThread(threading.Thread):
    def run(self):
        self.object.runMeasureClient()
	self.object.setStatus(False)
    def __init__(self,object):
        threading.Thread.__init__(self)
        self.object = object

class MeasureInterface():
	def __init__(self,host,port,nrSamples='',resultfile='',roundNumber='',numberResultSamplesDiscarded=0):
		#Initialize atributtes
		self.nicThroughput = {}
		self.nicInterest = []
		self.vmAssociatedNic = {}
		self.vmThroughput = {}
		self.vmMeasureCollectedStatus = {}
		self.resultFile = resultfile
		self.resultLine = {}
		self.roundNumber = roundNumber
		self.numberResultSamplesDiscarded = numberResultSamplesDiscarded
		self.nrSamples = nrSamples
		self.sampleNumber = 1
		self.setStatus(False)
		#Try to connect to the server
		self.serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
		connectionStatus = False
		while (connectionStatus == False):	
			try:
				self.serverSocket.connect((host, port))
				connectionStatus = True
			except:
				connectionStatus = False

	#Methods used by the upper applications

	#Add a virtual machine
	def addVirtualMachine(self,vmName,listNic=[]):
		#Check if the vm already exists
		if not(vmName in self.vmAssociatedNic):
			self.__setVmAssociatedNicList(vmName,listNic)
			self.__setVmMeasureCollectedStatus(vmName,True)
			self.__setVmThroughputMeasure(vmName,-1)
			for nicName in listNic:
				self.__appendNicInterest(nicName)
				self.__setNicThroughput(nicName,0)
		else:
			return {'004': "Virtual Machine already exists"}
		return {'000': "OK"}

	#Delete a virtual machine

	def delVirtualMachine(self,vmName):
		#Check if the vm exists
		if vmName in self.vmAssociatedNic:
			listNic = self.__getVmAssociatedNicList(vmName)
			#Delete all the nics entries corresponding to the vms
			for nicName in listNic:
				self.__delNicInterest(nicName)
				self.__delNicThroughput(nicName)
			#Delete the vm to the vmassociatedniclist and from the measures
			self.__delVmAssociatedNicList(vmName)
			self.__delVmThroughputMeasure(vmName)
		else:
			return {'003': "Virtual Machine does not exist"}
		return {'000': "OK"}

	#Add a nic to the list of nic of interest. This method return a dictionary with the error code and its message
	def addNicInterest(self,vmName,nicName):
		#Check if the virtual machine exists
		if (vmName in self.vmAssociatedNic):
			#Check if the nic is already registered
			if not(nicName in self.nicInterest): 
				self.__appendNicInterest(nicName)
				self.__appendNicVmAssociatedNicList(vmName,nicName)
				self.__setNicThroughput(nicName,0)
			else:
				nicList = self.__getVmAssociatedNicList(vmName)
				#Check if the nic is already set for this machine
				if (nicName in nicList):
					return {'001': "Nic already set for this virtual machine"}
				else:
					return {'002': "This Nic is already set for another virtual machine"}
		else:
			return {'003': "Virtual Machine does not exist"}
		return {'000': "OK"}

	#Delete the nic from the nic of interest
	def delNicInterest(self,vmName,nicName):
		#Check if the nic is registered
		if nicName in self.nicInterest: 
			self.__delNicInterest(nicName)
			self.__delNicVmAssociatedNicList(vmName,nicName)
			self.__delNicThroughput(nicName)
		else:
			return {'005': "Nic does not exist"}
		return {'000': "OK"}
			
	#Gather Vm Throughput
	def gatherVmThroughputMeasure(self,vmName):
		return self.__getVmThroughputMeasure(vmName)

	#Show Vm Throughput. This method returns the throughput of a VM only for display purposes
	def showVmThroughput(self,vmName):
		throughput = self.vmThroughput[str(vmName)]
		return throughput

	#Print the niclist (vm's and their corresponding nics)
	def printNicsInterest(self):
		for vm in self.vmAssociatedNic:
			print "-------------------------------------------- \n"
			print "Machine: " + str(vm) + " " + str(self.vmThroughput[str(vm)]) + " \n"
			print "Nic List: \n" 
			for nic in (self.__getVmAssociatedNicList(vm)):
				print str(nic) + " " + str(self.__getNicThroughput(nic)) + " " + "\n"
			print "-------------------------------------------- \n"

	#Start the measure client
	def startMeasure(self):
		MeasureThread(self).start()
		self.setStatus(True)

	#Gather information from socket by running the measure client (Must be execute by the thread class)
	def runMeasureClient(self):
		if (self.nrSamples != ''):
			while (self.sampleNumber <= int(self.nrSamples)):		
				data = self.serverSocket.recv(1024)
				nicDictionary = pickle.loads(data)
				for vm in self.vmAssociatedNic:
					throughput = 0
					nicList = self.__getVmAssociatedNicList(vm)
					for nic in nicList:
						if nic in nicDictionary:
							nicThroughput = nicDictionary[str(nic)]
							throughput += nicThroughput
							self.__setNicThroughput(nic,nicThroughput)
					self.__setVmThroughputMeasure(vm,throughput)
					if (self.resultFile != ''):
						line = str(self.sampleNumber) + " " + str(throughput)
						self.__appendLineResultFile(vm,line)
				self.sampleNumber += 1
			self.writeResultFile()
		else:
			while 1:		
				data = self.serverSocket.recv(1024)
				nicDictionary = pickle.loads(data)
				for vm in self.vmAssociatedNic:
					throughput = 0
					nicList = self.__getVmAssociatedNicList(vm)
					for nic in nicList:
						if nic in nicDictionary:
							nicThroughput = nicDictionary[str(nic)]
							throughput += nicThroughput
							self.__setNicThroughput(nic,nicThroughput)
					self.__setVmThroughputMeasure(vm,throughput)

	#Set The Status of the Measure Client
	def setStatus(self,status):
		self.status = status

	#Set The Status of the Measure Client	
	def getStatus(self):
		return self.status

	#Get the measure collected Status of a Vm
	def getVmMeasureCollectedStatus(self,vmName):
		return self.vmMeasureCollectedStatus[str(vmName)]
			

	
	#Private methods

	#Append new line to the result file
	def __appendLineResultFile(self,vmName,line):
		if vmName in self.resultLine:
			self.resultLine[str(vmName)] = self.resultLine[str(vmName)] + line + "\n"
		else:
			self.resultLine[str(vmName)] = line + "\n"

	#Write result file
	def writeResultFile(self):
		vmID = 1
		for vm in self.resultLine:
			#Write the file with the timeline of the experiment
			fileName = str(self.resultFile)
			#Specficy the name of the file in which the timeline will appear
			roundNumberFileName = fileName + "_" + str(vm) + "_TimeLine"+ str(self.roundNumber)
			resultFile = open( roundNumberFileName ,'w')
			resultFile.write(self.resultLine[str(vm)])
			resultFile.close()
			#Write the statistics to a file
			#The result file is the same for all running VMs
			#For each VM will be assigned an ID
			#The ID relationship is especified in the VMIDs file
			statistic = Statistics(roundNumberFileName,self.numberResultSamplesDiscarded)
			output = open(str(fileName + "_Average"),'a')
			output.write(str(vmID) + " " + str(statistic.average) + "\n")
			output.close()
			output = open(str(fileName + "_RMSE"),'a')
			output.write(str(vmID) + " " + str(statistic.rmse) + "\n")
			output.close()
			output = open(str(fileName + "_VMIDs"),'a')
			output.write(str(vmID) + " " + str(vm) + "\n")
			output.close()
			vmID += 1 

	#Append an entry to nicInterestlist
	def __appendNicInterest(self,nicList):
		self.nicInterest.append(nicList) 
	#Delete a nic from the list of nic of interest
	def __delNicInterest(self,nicName):
		self.nicInterest.remove(nicName) 
	#Set the dictionary that associate a list of nics to a Vm
	def __setVmAssociatedNicList(self,vmName,listNic):
		self.vmAssociatedNic[str(vmName)] = listNic
	#Get the nics associated to a Vm
	def __getVmAssociatedNicList(self,vmName):
		return self.vmAssociatedNic[str(vmName)]
	#Append a nic to the AssociatedNiclist
	def __appendNicVmAssociatedNicList(self,vmName,nicName):
		self.vmAssociatedNic[str(vmName)].append(nicName)
	#Delete a nic from the AssociatedNiclist
	def __delNicVmAssociatedNicList(self,vmName,nicName):
		self.vmAssociatedNic[str(vmName)].remove(nicName)
	#Delete the vm entry of the AssociatedNicList
	def __delVmAssociatedNicList(self,vmName):
		del self.vmAssociatedNic[str(vmName)]
	#Set the dictionary with the throughput of each interface
	def __setNicThroughput(self,nicName,txBytes):
		self.nicThroughput[nicName] = int(txBytes)
	#Get the throughput of an interface	
	def __getNicThroughput(self,nicName):
		return self.nicThroughput[nicName]
	#Remove the nic of the the nic throughput record
	def __delNicThroughput(self,nicName):
		del self.nicThroughput[nicName]	 
	#Set the measure collected Status of a Vm. The collected status indicates if the controller has already read the measure.
	def __setVmMeasureCollectedStatus(self,vmName,status):
		self.vmMeasureCollectedStatus[str(vmName)] = status
	#Del the entry of the measure collected Status for a Vm
	def __delVmMeasureCollectedStatus(self,vmName):
		del self.vmMeasureCollectedStatus[str(vmName)]	
	#Set the throughput of a Vm and set the collected status to false. -1 int throughput indicates system initalization
	def __setVmThroughputMeasure(self,vmName,throughput):
		if (throughput != -1):
			self.vmThroughput[str(vmName)] = throughput
			self.__setVmMeasureCollectedStatus(vmName,False)
		else:
			self.vmThroughput[str(vmName)] = 0
			self.__setVmMeasureCollectedStatus(vmName,True)
	#Get the throughput of a Vm and set the collected status to True
	def __getVmThroughputMeasure(self,vmName):
		if self.getVmMeasureCollectedStatus(vmName) == True :
			return -1
		self.__setVmMeasureCollectedStatus(vmName,True)
		return self.vmThroughput[str(vmName)]
	#Del the entry of the throughput measure for a Vm
	def __delVmThroughputMeasure(self,vmName):
		del self.vmThroughput[str(vmName)]
		self.__delVmMeasureCollectedStatus(vmName)	
		

"""
	Error codes

	000 - "OK"
	001 - "Nic already set for this virtual machine"
	002 - "This Nic is already set for another virtual machine"
	003 - "Virtual Machine does not exist"
	004 - "Virtual Machine already exists"
	005 - "Nic does not exist"

"""		
