/// <summary> /// start service host by serviceInfo /// </summary> /// <param name="serviceInfo"></param> /// <returns></returns> public static int OpenService(ServiceInfo serviceInfo) { void SetEnvironment() { if (serviceInfo.Environment == null) { TraceHelper.TraceVerbose(Utility.GetJobId(), "No environment fond."); return; } foreach (var env in serviceInfo.Environment) { Environment.SetEnvironmentVariable(env.Key, env.Value); TraceHelper.TraceVerbose(Utility.GetJobId(), $"Set environment {env.Key}={env.Value}."); } } async Task <string[]> DownloadDependFiles() { if (serviceInfo.DependFiles != null && serviceInfo.DependFiles.Any()) { var downloadTasks = serviceInfo.DependFiles.Select(fileInfo => StandaloneDataClient.DownloadFileFromSasAsync( fileInfo.Value, Path.Combine(Path.Combine(Path.GetTempPath(), "ExcelOffloading"), Path.GetFileName(fileInfo.Key)))); return(await Task.WhenAll(downloadTasks)); } else { TraceHelper.TraceVerbose(Utility.GetJobId(), "No depend files fond."); return(new string[0]); } } Environment.SetEnvironmentVariable(Constant.JobIDEnvVar, serviceInfo.JobId.ToString()); Environment.SetEnvironmentVariable(Constant.TaskIDEnvVar, serviceInfo.TaskId.ToString()); Environment.SetEnvironmentVariable(Constant.CoreIdsEnvVar, serviceInfo.CoreId.ToString()); Environment.SetEnvironmentVariable(Constant.ServiceConfigFileNameEnvVar, serviceInfo.FileName); if (!string.IsNullOrEmpty(serviceInfo.RegistrationPath)) { Environment.SetEnvironmentVariable(Constant.RegistryPathEnv, serviceInfo.RegistrationPath); } else { Environment.SetEnvironmentVariable(Constant.RegistryPathEnv, Path.Combine(Environment.CurrentDirectory, defaultRegistryPath)); } TraceHelper.TraceVerbose(Utility.GetJobId(), $"Set {Constant.RegistryPathEnv} to {Environment.GetEnvironmentVariable(Constant.RegistryPathEnv)}"); // use default values for following environment variables Environment.SetEnvironmentVariable(Constant.ProcNumEnvVar, "1"); Environment.SetEnvironmentVariable(Constant.NetworkPrefixEnv, Constant.EnterpriseNetwork); Environment.SetEnvironmentVariable(Constant.ServiceInitializationTimeoutEnvVar, "60000"); Environment.SetEnvironmentVariable(Constant.CancelTaskGracePeriodEnvVar, "15"); Environment.SetEnvironmentVariable(Constant.ServiceConfigMaxMessageEnvVar, "65536"); Environment.SetEnvironmentVariable(Constant.ServiceConfigServiceOperatonTimeoutEnvVar, "86400000"); // the local host process won't be preempted by the scheduler Environment.SetEnvironmentVariable(Constant.EnableMessageLevelPreemptionEnvVar, bool.FalseString); SetEnvironment(); var dependFilePath = DownloadDependFiles().GetAwaiter().GetResult(); Environment.SetEnvironmentVariable(Constant.DataServiceSharedFileEnvVar, string.Join(";", dependFilePath)); #if DEBUG #region For EndpointNotFoundException test try { string strWaitPeriod = ConfigurationManager.AppSettings["Test_WaitPeriodBeforeStartup"]; if (!string.IsNullOrEmpty(strWaitPeriod)) { int waitPeriodInMilliSecond = int.Parse(strWaitPeriod); Console.Error.WriteLine("Debug: waiting {0} ms before startup service", waitPeriodInMilliSecond); Thread.Sleep(waitPeriodInMilliSecond); } } catch (Exception) { // do nothing } #endregion #endif jobId = Utility.GetJobId(); string serviceConfigFullPath; bool onAzure = SoaHelper.IsOnAzure(); TraceHelper.TraceInfo( jobId, "OnAzure = {0}", onAzure); bool bOpenDummy = false; try { string serviceConfigFileName = Environment.GetEnvironmentVariable(Constant.ServiceConfigFileNameEnvVar); // exit if no such env var if (string.IsNullOrEmpty(serviceConfigFileName)) { bOpenDummy = true; Console.Error.WriteLine(StringTable.ServiceConfigFileNameNotSpecified); return(ErrorCode.ServiceHost_ServiceConfigFileNameNotSpecified); } if (onAzure) { string localCacheFolder = Utility.GetServiceLocalCacheFullPath(); serviceConfigFullPath = Path.Combine(localCacheFolder, serviceConfigFileName); } else { serviceConfigFullPath = GetServiceInfo(serviceConfigFileName); } if (!File.Exists(serviceConfigFullPath)) { bOpenDummy = true; Console.Error.WriteLine(StringTable.CantFindServiceRegistrationFile, serviceConfigFullPath); return(ErrorCode.ServiceHost_ServiceRegistrationFileNotFound); } TraceHelper.TraceInfo( jobId, "ServiceConfigFullPath = {0}", serviceConfigFullPath); ServiceRegistration registration; string assemblyFullPath; int errorCode = Utility.GetServiceRegistration(serviceConfigFullPath, onAzure, out registration, out assemblyFullPath); if (errorCode != ErrorCode.Success) { bOpenDummy = true; return(errorCode); } // Open the host in another application domain AppDomain domain = CreateNewServiceDomain(serviceConfigFullPath, assemblyFullPath); using (CcpServiceHostWrapper host = CreateInstanceFromAndUnwrap <CcpServiceHostWrapper>( domain, Assembly.GetExecutingAssembly().Location, serviceConfigFullPath, onAzure, ServiceHostRuntimeConfiguration.Standalone)) { host.Initialize(); host.Run(); TraceHelper.TraceInfo( jobId, "Sleep..."); if (ServiceHostRuntimeConfiguration.Standalone) { // Endless listening, till service info deleted. while (true) { lock (ServiceInfo.s_lock) { if (SvcHostMgmtRestServer.Info == null) { TraceHelper.TraceInfo( jobId, "Close service host!"); host.Dispose(); return(0); } } Thread.Sleep(1000); } } else { // Endless waiting, till it's being killed Thread.Sleep(Timeout.Infinite); } } } catch (Exception e) { Console.Error.WriteLine(e.Message); TraceHelper.TraceError( jobId, e.ToString()); // Failed to open service, fall back to open an dummy service. bOpenDummy = true; } finally { if (bOpenDummy) { OpenDummyService(onAzure); } } return(0); }
/// <summary> /// <para> /// Initializes session parameters and calls open session. Only for cluster computation. /// </para> /// </summary> /// <param name="headNode"> /// <para>Name of cluster head node</para> /// </param> /// <param name="remoteWorkbookPath"> /// <para>Workbook location relative to compute node</para> /// </param> /// <param name="minResources"> /// <para>minimum number of resources requested</para> /// </param> /// <param name="maxResources"> /// <para>Maximum number of resources required</para> /// </param> /// <param name="resourceType"> /// <para>Name of resource requested (core, node, or socket)</para> /// </param> /// <param name="jobTemplate"> /// <para> Name of the job template to be used </para> /// </param> /// <param name="serviceName"> /// <para> Name of the service to use </para> /// </param> /// <param name="jobName"> /// <para>Specify the job name</para> /// </param> /// <param name="projectName"> /// <para>Specify the project name</para> /// </param> /// <param name="transportScheme"> /// <para>The transport scheme (Http or NetTcp)</para> /// </param> /// <param name="useAzureQueue"> /// <para>Specify if Azure storage queue is used (True or False)</para> /// </param> /// <param name="username"> /// <para>Specify the user name</para> /// </param> /// <param name="password"> /// <para>Specify the password</para> /// </param> /// <param name="jobPriority"> /// <para>Specify the job priority</para> /// </param> /// <returns> /// <para> ID of opened session </para> /// </returns> public int OpenSession(string headNode, string remoteWorkbookPath, [Optional] object minResources, [Optional] object maxResources, [Optional] object resourceType, [Optional] string jobTemplate, [Optional] string serviceName, [Optional] string jobName, [Optional] string projectName, [Optional] string transportScheme, [Optional] object useAzureQueue, [Optional] string username, [Optional] string password, [Optional] object jobPriority) { // Check if workbook path has been provided (ExcelClient initialize called) if (!this.initialized) { Tracing.WriteDebugTextError(Tracing.ComponentId.ExcelClient, Resources.ExcelClientOpenSessionBeforeInit); throw new InvalidOperationException(Resources.ExcelClientOpenSessionBeforeInit); } SessionStartInfo info; bool useWebApi = false; if (headNode.StartsWith("d:", StringComparison.OrdinalIgnoreCase)) { // HACK: using head node string to pass actual parameters var param = headNode.Split('?'); string serviceRegDirectory = null; string[] computeNodeIpList = null; string storageCredential = null; string[] dependFiles = null; bool CheckParameterSwitch(string parameter, string switchStr) { return(parameter.StartsWith(switchStr, StringComparison.OrdinalIgnoreCase)); } foreach (var p in param) { if (CheckParameterSwitch(p, "d:")) { serviceRegDirectory = p.Substring(p.IndexOf(":") + 1); } else if (CheckParameterSwitch(p, "c:")) { computeNodeIpList = p.Substring(p.IndexOf(":") + 1).Split(','); } else if (CheckParameterSwitch(p, "s:")) { storageCredential = p.Substring(p.IndexOf(":") + 1); } else if (CheckParameterSwitch(p, "f:")) { dependFiles = p.Substring(p.IndexOf(":") + 1).Split(','); } } if (serviceRegDirectory == null || computeNodeIpList == null) { throw new ArgumentNullException(); } // Standalone mode if (string.IsNullOrEmpty(serviceName)) { info = new SessionStartInfo(Microsoft.Hpc.Excel.ExcelClient.SERVICE, serviceRegDirectory, null, computeNodeIpList); } else { info = new SessionStartInfo(serviceName, serviceRegDirectory, null, computeNodeIpList); } if (dependFiles != null && dependFiles.Any()) { // Start data client related logic // TODO: move logic into ExcelClient try { if (string.IsNullOrEmpty(storageCredential)) { throw new ArgumentNullException(nameof(storageCredential)); } StandaloneDataClient dataClient = new StandaloneDataClient(storageCredential); var sasTokens = dataClient.UploadFilesAsync(dependFiles).GetAwaiter().GetResult(); if (sasTokens.Length != dependFiles.Length) { throw new InvalidOperationException($"Number of sas token ({sasTokens.Length}) does not equal to depend files ({dependFiles.Length})"); } var depFileInfo = new Dictionary <string, string>(); for (int i = 0; i != sasTokens.Length; ++i) { depFileInfo[Path.GetFileName(dependFiles[i])] = sasTokens[i]; } info.DependFilesStorageInfo = depFileInfo; remoteWorkbookPath = Path.GetFileName(remoteWorkbookPath); } catch (Exception ex) { Tracing.SoaTrace( XlTraceLevel.Error, "error when uploading files {0}", ex.ToString()); throw; } // End data client related logic } info.UseInprocessBroker = true; info.IsNoSession = true; } else { // If https prefix from head node name useWebApi = Microsoft.Hpc.Excel.ExcelClient.TryRemoveWebApiPrefix(ref headNode); // If the service name is provided, use it rather than the default in ExcelClient if (string.IsNullOrEmpty(serviceName)) { info = new SessionStartInfo(headNode, Microsoft.Hpc.Excel.ExcelClient.SERVICE); } else { info = new SessionStartInfo(headNode, serviceName); } } if (useWebApi) { info.TransportScheme = TransportScheme.WebAPI; } if (!string.IsNullOrEmpty(transportScheme)) { if (transportScheme.Equals("Http", StringComparison.InvariantCultureIgnoreCase)) { info.TransportScheme = TransportScheme.Http; } else if (transportScheme.Equals("NetTcp", StringComparison.InvariantCultureIgnoreCase)) { info.TransportScheme = TransportScheme.NetTcp; } else { throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.ExcelClient_InvalidTransportScheme, transportScheme), "transportScheme"); } } if (!(useAzureQueue == null || System.Reflection.Missing.Value.Equals(useAzureQueue) || DBNull.Value.Equals(useAzureQueue))) { bool useAQ; if (bool.TryParse(useAzureQueue.ToString(), out useAQ)) { info.UseAzureQueue = useAQ; } else { throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.ExcelClient_InvalidBoolParamValue, "useAzureQueue", useAzureQueue), "useAzureQueue"); } } if (!string.IsNullOrEmpty(username)) { info.Username = username; } if (!string.IsNullOrEmpty(password)) { info.Password = password; } if (!string.IsNullOrEmpty(jobName)) { info.ServiceJobName = jobName; } if (!string.IsNullOrEmpty(projectName)) { info.Project = projectName; } // If jobPriority is specified, try to parse it into an integer. if (!(jobPriority == null || System.Reflection.Missing.Value.Equals(jobPriority) || DBNull.Value.Equals(jobPriority))) { int priority; if (int.TryParse(jobPriority.ToString(), out priority)) { info.SessionPriority = priority; } else { throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.ExcelClient_InvalidMinCores, jobPriority), "minResources"); } } // If minResources is specified, try to parse it into an integer. if (!(minResources == null || System.Reflection.Missing.Value.Equals(minResources) || DBNull.Value.Equals(minResources))) { int minUnits; if (int.TryParse(minResources.ToString(), out minUnits)) { info.MinimumUnits = minUnits; } else { throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.ExcelClient_InvalidMinCores, minResources), "minResources"); } } // If maxResources is specified, try to parse it into an integer if (!(maxResources == null || System.Reflection.Missing.Value.Equals(maxResources) || DBNull.Value.Equals(maxResources))) { int maxUnits; if (int.TryParse(maxResources.ToString(), out maxUnits)) { info.MaximumUnits = maxUnits; } else { throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.ExcelClient_InvalidMaxCores, maxResources), "maxResources"); } } // If job template is specified, set it if (!string.IsNullOrEmpty(jobTemplate)) { info.JobTemplate = jobTemplate; } // Set resource type (defaults to core) if (!(resourceType == null || System.Reflection.Missing.Value.Equals(resourceType) || DBNull.Value.Equals(resourceType))) { int resIndex; if (int.TryParse(resourceType.ToString(), out resIndex)) { info.SessionResourceUnitType = ResourceToSessionUnitType((Microsoft.Hpc.Excel.Com.SessionUnitType)resIndex); } else { throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.ExcelClient_InvalidResType, resourceType), "resourceType"); } } // Set interface mode to UI to ensure that Excel asks for credentials in the UI when not cached SessionBase.SetInterfaceMode(false, new IntPtr(this.client.Driver.App.Hwnd)); return(this.client.OpenSession(info, remoteWorkbookPath)); }