#!/usr/bin/env python2
try:
	import sys, os
	######### set defaultt c3 path ########################
        try:
                def_path = os.environ[ 'C3_PATH' ]
        except KeyError:
                def_path = '/opt/c3-4'
        #######################################################

	sys.path.append( def_path )
	import c3_com_obj, c3_file_obj, socket, re

	######## constants ####################################
	help_info = """c3 version 4.0
	Usage: cnum [OPTIONS] [MACHINE DEFINTIONS] node_name

		--help -h	= this screen
		--file -f       = file containing list of ip's if
				  one is not supplied then
				  /etc/c3.conf will be used
		--all		= search all clusters
		
		machine definitions are in the form of
		clusterName:"""

	backslash = re.compile( r"\\" )
	#######################################################

        ######## check for arguments  #########################
        if len(sys.argv) == 1:
                print help_info
                sys.exit()
        #######################################################


	######### et default options  #########################
	head_node = 0			#execute only on head node
	filename = "/etc/c3.conf"	#default config file
	defusername = ""		#default user name
	#######################################################


	######### internal variables ##########################
	cluster_from_file = {}
	name_to_find = []
	all = 0
	file_set = 0
	#######################################################



	######### parse command line ##########################

	# first create one large string of the command 
	command_line_list = sys.argv[1:]
	command_line_string = ''
	for item in command_line_list:
		command_line_string = '%s %s' % (command_line_string, item)

	# object used to parse command line
	c3_command = c3_com_obj.c3_command_line( command_line_string )
	 
	# get first option
	try:
		option = c3_command.get_opt()
		while option: # while more options
			if (option == '-f') or (option == '--file'): # alternate config file
				if not file_set:
					filename = c3_command.get_opt_string()
					file_set = 1
				else:
					print "only one file name can be specified."
					sys.exit()

			elif option == '-h' or option == "--help": # print help info
				print help_info
				sys.exit()

			elif option == '--all':
				all = 1

			else: # a catch all, option supplied not valid
				print "option ", option, " is not valid"
				sys.exit()
			option = c3_command.get_opt()
	except c3_com_obj.end_of_option: #quit parsing options
		pass

	# create cluster object from command line 
	try:
		clusters = c3_com_obj.c3_cluster_list()
		if all == 0:
	 		clusters = c3_command.get_clusters()
		else:
			c3_command.get_clusters()

		while(1):
			name_to_find.append( c3_command.get_opt_string() )
	except c3_com_obj.end_of_opt_string:
		pass
	except c3_com_obj.bad_string:
		print help_info
		sys.exit()
	 
	#######################################################


	######### test if ssh or rsh ##########################
	try:
		transport = os.environ[ 'C3_RSH' ]
	except KeyError:
		transport = 'ssh'
	#######################################################
	######### set default user name #######################
	try:
		defusername = os.environ[ 'C3_USER' ]
	except KeyError:
		defusername = os.environ['USER']
	#######################################################
        ######### set filename  ##########################
        if not file_set:
                try:
                        filename = os.environ[ 'C3_CONF' ]
                except KeyError:
                        filename = '/etc/c3.conf'
        #######################################################


	######### make cluster list object from file ##########
	try: # open file & initialize file parser
		file = c3_file_obj.cluster_def( filename )
	except IOError:
		print "error opening file: ", filename
		sys.exit()

	try:
		file.get_next_cluster() # set the default cluster (first cluster in list)
		try:
			if clusters.clusters[0]== "/default":
				clusters.clusters[0]=file.get_cluster_name()
				clusters.node[file.get_cluster_name()] = clusters.node["/default"]
				del clusters.node["/default"]
				clusters.username[file.get_cluster_name()] = clusters.username["/default"]
		except IndexError: #will be thrown if --all is specified
			pass

		while(1): #throws exception when no more clusters
			c_name = file.get_cluster_name() #name of cluster being parsed
			
			if all == 1:
				clusters.clusters.append( c_name )
				clusters.node[c_name]=[]
				clusters.node[c_name].append ("" )
			if c_name in clusters.clusters:		
				cluster_from_file[c_name] = {}
				cluster_from_file[c_name]['external'] = file.get_external_head_node()
				cluster_from_file[c_name]['internal'] = file.get_internal_head_node()
				cluster_from_file[c_name]['nodes'] = [] #list of node names from file
				if cluster_from_file[c_name]['external']: #if a direct cluster
					index = 0
					try:
						while(1): # build list of nodes
							node_obj = file.get_next_node()
							cluster_from_file[c_name]['nodes'].append( c3_file_obj.node_obj() )
							cluster_from_file[c_name]['nodes'][index] = node_obj
							index = index + 1
					except c3_file_obj.end_of_cluster:
						pass
			file.get_next_cluster() #repeat untill no more clusters
	except c3_file_obj.no_more_clusters:
		pass
	except c3_file_obj.parse_error, error:
		print error.description
		print "somewhere around ", error.last
		sys.exit()

	#######################################################


	######### execute command on each node in cluster



	# there are two main groups, local and remote clusters
	# in each of those groups there are direct and indirect
	# modes, that is every node specified or a "link". A link
	# on a local cluster is of course invalid.

	# right now the only way I know how to check if a cluster
	# is local is to use a "gethostbyname" and compare it
	# to the head node names (both internel and externel).
	# right now that is acceptable as many tools require 
	# the function to work correctly (ssh being one of them)
	# my want to think about a better way.

	#host_ip = socket.gethostbyname( socket.gethostname() )

	if clusters.clusters[0] ==  "/default": # if default cluster set correct cluster name
		clusters.clusters[0] = default_cluster
		clusters.node[default_cluster] = clusters.node["/default"]
		del clusters.node["/default"]

	pid_list_outer = []
	for cluster in clusters.clusters:
		
		pid = os.fork()
		if pid == 0:

			############ get machine names #############################
			try: 
				local_ip = socket.gethostbyname( socket.gethostname() )
			except socket.error:
				print "Can not resolve local hostname"
				sys.exit()
			try:
				int_ip = socket.gethostbyname( cluster_from_file[cluster]['internal'] )
			except socket.error:
				int_ip = ""
			except KeyError:
				print "Cluster ", cluster, " is not in you configuration file."
				continue
			try:
				ext_ip = socket.gethostbyname( cluster_from_file[cluster]['external'] )
			except socket.error:
				ext_ip = ""
			except TypeError:
				ext_ip = int_ip
			############################################################
			try:
				if clusters.username[cluster] == '/default':
					username = defusername
				else:
					username = clusters.username[cluster]
			except KeyError: # if --all is specified
				username = defusername
			
			if cluster_from_file[cluster]['external']: # if a direct cluster
				if ext_ip == local_ip or int_ip == local_ip: # if a local cluster
						print "nodes from cluster: ", cluster
						if ext_ip == "": # error conditions (just don't execute current cluster)
							print "Can not resolve ", cluster_from_file[cluster]['external']
						elif int_ip == "":
							print "can not resolve ", cluster_from_file[cluster]['internal']

						elif clusters.node[cluster][0] != "" : #range specified on command line
							index = 0
							try:
								for node in clusters.node[cluster]: #for each cluster specified on the command line
									node_name = cluster_from_file[cluster]['nodes'][node].name
									if node_name in name_to_find:
										print node_name + " is at index %d" % index + " in cluster " + cluster
									index = index + 1
							except IndexError:
								pass
						else:  # no range specified on command line, do all nodes
							index = 0
							for node in cluster_from_file[cluster]['nodes']:  # for each node in cluster
								if node.name in name_to_find:
									print node.name + " is at index %d" % index + " in cluster " + cluster
								index = index + 1
				else: # remote cluster
					if ext_ip == "": # error condition
						print "Can not resolve ", cluster_from_file[cluster]['external']
						sys.stdout.flush()
					else:
						print "remote cluster: ", cluster
						if clusters.node[cluster][0] != "" : #range specified on command line
							index = 0
							try:
								for node in clusters.node[cluster]: #for each node specified on command line
									node_name = cluster_from_file[cluster]['nodes'][node].name #add cluster to list
									if node_name in name_to_find:
										print node_name + " is at index %d" % index + " in cluster " + cluster
									index = index + 1
							except IndexError:
								pass
						else:  # no range specified on command line, do all nodes
							index = 0
							for node in cluster_from_file[cluster]['nodes']: # for each node in cluster
								if node.name in name_to_find:
									print node.name + " is at index %d" % index + " in cluster " + cluster
								index = index + 1
			else: # indirect clusters
				if int_ip == local_ip:  # can not have a indirect local cluster since if your default cluster
							# is local you would generate a circular reference
					print "error local cluster"
					sys.stdout.flush()
				else: # remote indirect cluster
					if int_ip == "": # error condition
						print "Can not resolve hostname ", cluster_from_file[cluster]['internal']
						sys.stdout.flush()
					else:
						node_list = "" # generate new node list from the command line
						if clusters.node[cluster][0] != "" : #range specified on command line
							node_list = ":%d" % clusters.node[cluster].pop(0)

							for node in clusters.node[cluster]:
								node_list = node_list + ", %d" % node	
						name_list =""
						for name in name_to_find:
							name_list = name_list + " %s" %  name
						# execute command on remote machine
						print "local name for cluster: ", cluster
						string_to_execute = transport + " " + username + "@" + int_ip + " " + def_path + "/cnum " + name_list
						os.system( string_to_execute )
			sys.stdout.flush()
			os._exit(1)
		pid_list_outer.append( pid )
	for pid in pid_list_outer:
		os.waitpid( pid, 0 )
except KeyboardInterrupt:
	print "Keyboard interrupt\n"

