using System;
using System.IO;
using System.Threading;
using System.Web.Services.Protocols;
using System.Xml;
using VMware.vma;
using VMware.sysimage;

namespace vmclone1
	{
	/// <summary>
	/// vmclone1 - create and customize clones of specified virtual machine
	/// Uses VMware Virtual Infrastructure SDK
	/// Code is from VMware SDK except where noted - requires vmaService_proxy.cs
	/// To compile: csc vmaService_proxy.cs vmclone1.cs
	/// Syntax: vmclone1 <source vm hostname> <clone config file>
	/// Format of clone config file: each line contains 1 clone's hostname, IP address,
	/// datastore separated by one or more spaces
	/// example:
	/// w2k31 192.168.1.11 [SAN1]
	/// w2k32 192.168.1.12 [SAN2]
	/// </summary>

public class VmaClient
	{
	public enum VmaClientState
		{
		Connected,
		Disconnected,
		}

	protected vmaService vma_;
	protected VmaClientState state_;
	public event VmaClientEventHandler AfterDisconnect;

	public VmaClient()
	{
	state_ = VmaClientState.Disconnected;
	// Manage bad certificates our way:
	//System.Net.ServicePointManager.CertificatePolicy = new CertPolicy();
	}

	/// <summary>
	/// Creates an instance of the VMA proxy and establishes a connection
	/// </summary>
	/// <param name="username"></param>
	/// <param name="password"></param>

	public void Connect(string url, string username, string password)
	{
	if (vma_ != null)
	{
	Disconnect();
	}
	vma_ = new vmaService();
	vma_.Url = url;
	vma_.CookieContainer = new System.Net.CookieContainer();
	vma_.Login(username, password);
	state_ = VmaClientState.Connected;
	}

	public vmaService Vma
	{
	get
		{
		return vma_;
		}
	}

	public VmaClientState State
	{
	get
		{
		return state_;
		}
	}

	/// <summary>
		/// Disconnects the VmaClient
		/// </summary>
	public void Disconnect()
	{
	state_ = VmaClientState.Disconnected;
	if (vma_ != null)
	{
		vma_.Dispose();
		vma_ = null;
		if (AfterDisconnect != null)
			{
			AfterDisconnect(this, new VmaClientEventArgs());
			}
		}
	}

	public string ResolvePath(string path) // Added by DJ
	{
	return vma_.ResolvePath(path);
	}

	public ViewContents GetContents(string handle)
	{
	return vma_.GetContents(handle);
	}

	public ViewInfo GetInfo(string handle) // Added by DJ
	{
	return vma_.GetInfo(handle);
	}

	//Added by DJ
	public ViewContents MigrateVM(string vm, string host, Level priority, string dataLocator)
	{
	return vma_.MigrateVM(vm, host, priority, dataLocator);
	}

	public ViewContents CloneVM(string srcHandle, string parentHandle, string destHostHandle,
	string name, string datastore, object customization, bool autopoweron)
	{
	return vma_.CloneVM(srcHandle, parentHandle, destHostHandle, name, datastore,
	customization, autopoweron);
	}
	public class VmaClientEventArgs : System.EventArgs
	{ }

	public delegate void VmaClientEventHandler(object sender, VmaClientEventArgs e);

	// vmclone1 - create and customize clones of specified virtual machine
	// Author: Dave Jaffe
	// Last modified: 9/1/05
	// Copyright Dell 2005
	// Syntax: vmclone1 <source vm hostname> <clone config file>
	// Format of clone config file: each line contains 1 clone's hostname, IP address,
	// datastore separated by one or more spaces
	// example:
	// w2k31 192.168.1.11 [SAN1]
	// w2k32 192.168.1.12 [SAN2]

	static void Main(string[] args)
		{
		int i, j, line_no, i_clone, n_clones;
		ViewContents VC;
		ViewInfo VI;
		Host host;
		VirtualMachine vm;
		Task task;
		Container c;
		Item[] items;
		string[] hostnames = new string[10];
		string srcHandle=null, current_hostname = null, parentHandle = null, destHostHandle = null;
		DateTime task_qt = System.DateTime.Now;
		TimeZone ctz = System.TimeZone.CurrentTimeZone;

		string clone_line;
		int MAX_CLONES = 100;
		string[] destVMName = new string[MAX_CLONES];
		string[] destIPAddress = new string[MAX_CLONES];
		string[] datastore = new string[MAX_CLONES];

		// Check input
		if (args.Length < 2)
			{
			Console.WriteLine("Syntax: vmclone1 <source vm hostname> <clone config file>");
			return;
			}

	// Parse input file
	string srcVMName = args[0];
	string config_file_name = args[1];
	if (File.Exists(config_file_name))
		{
		StreamReader sr = new StreamReader(config_file_name);
		line_no = 0;
		while ((clone_line = sr.ReadLine()) != null)
		{
		// Break each line into separate words; handle case where >1 space between words
		string[] p = new string[20];
		p = clone_line.Split(null);
		i=0;
		while (p[i++].Trim() == "") ;
		destVMName[line_no] = p[i-1];
		while (p[i++].Trim() == "") ;
		destIPAddress[line_no] = p[i-1];
		while (p[i++].Trim() == "") ;
		datastore[line_no] = p[i-1];
		++line_no;
		}
	n_clones = line_no;
	}
	else
	{
	Console.WriteLine("Cannot open config file " + config_file_name);
	return;
	}

	// Establish connection to Virtual Center
	string url = "http://localhost:8088";
	string username = "vcadmin";
	string password = "password";

	VmaClient vma = new VmaClient();

	Console.WriteLine("Logging in...");
	vma.Connect(url, username, password);

	// Create and customize clones
		for (i_clone=0; i_clone < n_clones; i_clone++)
			{
			Console.WriteLine("\nCloning {0} from {1} with IP= {2} and datastore= {3}",
			destVMName[i_clone], srcVMName, destIPAddress[i_clone], datastore[i_clone]);
			// Find which host srcVMName is on; get srcHandle, destHostHandle, parentHandle
			string hosthandle = vma.ResolvePath("/host");
			VC = vma.GetContents(hosthandle);
			c = (Container) VC.body;
			items = c.item;
			for (i=0; i<items.Length; i++)
				{
				hostnames[i] = items[i].name;
				VC = vma.GetContents(items[i].key);
				host = (Host) VC.body;
				if (host.vm != null)
					{
					//Console.WriteLine("Discovered host: {0} {1} VMs", hostnames[i], host.vm.Length);
					for (j=0; j<host.vm.Length; j++)
						{
						VC = vma.GetContents(host.vm[j]);
						vm = (VirtualMachine) VC.body;
						if (vm.info.name == srcVMName)
							{
							//Console.WriteLine("Source VM {0} is on host {1}", srcVMName, hostnames[i]);
							srcHandle = host.vm[j];
							// Make destination host same as source host
							destHostHandle = vma.ResolvePath("/host/" + hostnames[i]);
							VI = vma.GetInfo(srcHandle);
							parentHandle = VI.parent;
							current_hostname = hostnames[i];
							} // End if (vm.info.name == srcVMName)
						} // End for j<host.vm.Length
					} // End if (host.vm != null)
				} // End for (i=0; i<items.Length; i++)

		// Set up customization object
		Autoprep autoprep = new Autoprep();

		Sysprep sysprep = new Sysprep();

		GuiUnattended guiUnattended = new GuiUnattended();
		guiUnattended.TimeZone = "020"; // US Central Time
		guiUnattended.AutoLogon = false;
		guiUnattended.AutoLogonSpecified = true;
		sysprep.GuiUnattended = guiUnattended;

		LicenseFilePrintData licenseFilePrintData = new LicenseFilePrintData();
		licenseFilePrintData.AutoMode = "PerServer";
		licenseFilePrintData.AutoModeSpecified = true;
		licenseFilePrintData.AutoUsers = 5;
		licenseFilePrintData.AutoUsersSpecified = true;
		sysprep.LicenseFilePrintData = licenseFilePrintData;
		sysprep.LicenseFilePrintDataSpecified = true;

		UserData userData = new UserData();
		userData.FullName = "Test User";
		userData.OrgName = "Dell";
		userData.ComputerName = destVMName[i_clone];
		userData.ProductID = "ABCDE-FGHIJ-KLMNO-PQRYS-UVWXY";
		userData.ProductIDSpecified = true;
		sysprep.UserData = userData;
		Identification id = new Identification();
		id.JoinWorkgroup = "WORKGROUP";
		id.JoinWorkgroupSpecified = true;
		sysprep.Identification = id;

		Adapters adapters = new Adapters();
		Adapter[] tmp = new Adapter[1];
		tmp[0] = new Adapter();
		tmp[0].MACAddress = "MAC00"; // This is just a tag for the script, not the real MAC
		tmp[0].MACAddressSpecified = true;
		tmp[0].UseDHCP = false;
		tmp[0].UseDHCPSpecified = true;
		tmp[0].IPAddress = destIPAddress[i_clone];
		tmp[0].IPAddressSpecified = true;
		tmp[0].SubnetMask = "255.255.255.0";
		tmp[0].SubnetMaskSpecified = true;

		DNSServers[] dnstmp = new DNSServers[1];
		dnstmp[0] = new DNSServers();
		string[] dnstmp_strarray = new string[1];
		dnstmp_strarray[0] = "192.168.1.10";
		dnstmp[0].DNSServer = dnstmp_strarray;
		tmp[0].DNSServers = dnstmp;

		Gateways[] gateways = new Gateways[1];
		Gateway[] gateway = new Gateway[1];
		gateways[0] = new Gateways();
		gateway[0] = new Gateway();
		gateway[0].datavalue = "192.168.1.1";
		gateway[0].CostMetric = 1;
		gateways[0].Gateway = gateway;
		tmp[0].Gateways = gateways;

		adapters.adapter = tmp;
		autoprep.adapters = adapters;
		autoprep.adaptersSpecified = true;

		autoprep.sysprep = sysprep;
		autoprep.sysprepSpecified = true;

		//Console.WriteLine("srcHandle= {0}, parentHandle= {1}, destHostHandle= {2}, " +
		// "destVMName= {3}, datastore= {4}", srcHandle, parentHandle, destHostHandle,
		// destVMName[i_clone], datastore[i_clone]);

	// Note: failure of Clone operation doesn't always generate Exception!
	try
		{
		VC = vma.CloneVM(srcHandle, parentHandle, destHostHandle, destVMName[i_clone],
		datastore[i_clone], autoprep, true);
		}
	catch (Exception e)
		{
		Console.WriteLine("Error in Clone operation: " + e.Message);
		return;
		}

	// Allow time for Virtual Center to start operation
	Thread.Sleep(10000);

	if (VC == null)
		{
		Console.WriteLine("ViewContents returned from CloneVM is null");
		}
	else
		{
		if (VC.body == null)
			{
			Console.WriteLine("VC.body is null");
			}
		else
			{
			task = (Task) VC.body;
			// Note: task generated from VC = vma.CloneVM does not always have accurate
			// currentState (so it can't be used to track progress) but it does have accurate
			// queueTime, so use queueTime to look up task from array of tasks
			//Console.WriteLine("task: {0} % completed: {1} {2} {3}",
			// task.operationName, task.percentCompleted, task.currentState, task.queueTime);
			task_qt = task.queueTime;
			}
		}

	string taskhandle = vma.ResolvePath("/task");

	do
		{
		VC = vma.GetContents(taskhandle);
		c = (Container) VC.body;
		items = c.item;
		i=0;
		do
			{
			VC = vma.GetContents(items[i++].key);
			task = (Task) VC.body;
			} while (task.queueTime != task_qt);

		// Need to explicitly account for Daylight Savings Time due to .NET problem (not VMware
		// bug!)
		Console.WriteLine
			("task: {0} target VM: {1} % completed: {2} Status: {3} Started: {4} Now: {5}",
			task.operationName, destVMName[i_clone], task.percentCompleted, task.currentState,
			task.queueTime.AddHours(ctz.IsDaylightSavingTime(task.queueTime) ? ?
			1:0).ToLongTimeString(),
			System.DateTime.Now.ToLongTimeString());
		Thread.Sleep(10000);
		} while((task.currentState.ToString() != "completed") && (task.currentState.ToString() !="failed"));

		if (task.currentState.ToString() == "failed")
			Console.WriteLine("Clone {0} failed", destVMName[i_clone]);
		else
			Console.WriteLine("Clone {0} finished", destVMName[i_clone]);
		} // End Main for loop
	vma.Disconnect();
	} // End Main
} // End public class VmaClient
			} // End namespace vmclone1
