#region License Information /* HeuristicLab * Copyright (C) 2002-2011 Heuristic and Evolutionary Algorithms Laboratory (HEAL) * * This file is part of HeuristicLab. * * HeuristicLab is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * HeuristicLab is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with HeuristicLab. If not, see . */ #endregion using System; using System.Collections.Generic; using System.Linq; using System.ServiceModel; using HeuristicLab.Services.Hive.Common; using HeuristicLab.Services.Hive.Common.DataTransfer; using HeuristicLab.Services.Hive.Common.ServiceContracts; namespace HeuristicLab.Services.Hive { /// /// Implementation of the Hive service (interface ). /// We need 'IgnoreExtensionDataObject' Attribute for the slave to work. /// [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, IgnoreExtensionDataObject = true)] public class HiveService : IHiveService { private DataAccess.IHiveDao dao { get { return ServiceLocator.Instance.HiveDao; } } private IAuthenticationManager authen { get { return ServiceLocator.Instance.AuthenticationManager; } } private IAuthorizationManager author { get { return ServiceLocator.Instance.AuthorizationManager; } } private ITransactionManager trans { get { return ServiceLocator.Instance.TransactionManager; } } private ILifecycleManager lifecycleManager { get { return ServiceLocator.Instance.LifecycleManager; } } private IUserManager userManager { get { return ServiceLocator.Instance.UserManager; } } private HeartbeatManager heartbeatManager { get { return ServiceLocator.Instance.HeartbeatManager; } } #region Job Methods public Guid AddJob(Job job, JobData jobData, IEnumerable resourceIds) { authen.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client); return trans.UseTransaction(() => { job.Id = dao.AddJob(job); jobData.JobId = job.Id; jobData.LastUpdate = DateTime.Now; foreach (Guid slaveGroupId in resourceIds) { dao.AssignJobToResource(job.Id, slaveGroupId); } dao.AddJobData(jobData); dao.UpdateJobState(job.Id, JobState.Waiting, null, userManager.CurrentUserId, null); return jobData.JobId; }, false, true); } public Guid AddChildJob(Guid parentJobId, Job job, JobData jobData) { authen.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client); return trans.UseTransaction(() => { job.ParentJobId = parentJobId; return AddJob(job, jobData, dao.GetAssignedResources(parentJobId).Select(x => x.Id)); }, false, true); } public Job GetJob(Guid jobId) { authen.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client, HiveRoles.Slave); author.AuthorizeForJob(jobId, Permission.Read); return dao.GetJob(jobId); } public IEnumerable GetJobs() { authen.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client); var jobs = dao.GetJobs(x => true); foreach (var job in jobs) author.AuthorizeForJob(job.Id, Permission.Read); return jobs; } public IEnumerable GetLightweightJobs(IEnumerable jobIds) { authen.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client); var jobs = dao.GetJobs(x => jobIds.Contains(x.JobId)).Select(x => new LightweightJob(x)).ToArray(); foreach (var job in jobs) author.AuthorizeForJob(job.Id, Permission.Read); return jobs; } public IEnumerable GetLightweightChildJobs(Guid? parentJobId, bool recursive, bool includeParent) { authen.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client); var jobs = GetChildJobs(parentJobId, recursive, includeParent).Select(x => new LightweightJob(x)).ToArray(); foreach (var job in jobs) author.AuthorizeForJob(job.Id, Permission.Read); return jobs; } public IEnumerable GetLightweightExperimentJobs(Guid experimentId) { authen.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client); author.AuthorizeForExperiment(experimentId, Permission.Read); return dao.GetJobs(x => x.HiveExperimentId == experimentId).Select(x => new LightweightJob(x)).ToArray(); } public JobData GetJobData(Guid jobId) { authen.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client, HiveRoles.Slave); author.AuthorizeForJob(jobId, Permission.Read); return dao.GetJobData(jobId); } public void UpdateJob(Job job) { authen.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client, HiveRoles.Slave); author.AuthorizeForJob(job.Id, Permission.Full); trans.UseTransaction(() => { dao.UpdateJob(job); }); } public void UpdateJobData(Job job, JobData jobData) { authen.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client, HiveRoles.Slave); author.AuthorizeForJob(job.Id, Permission.Full); author.AuthorizeForJob(jobData.JobId, Permission.Full); //trans.UseTransaction(() => { // cneumuel: try without transaction jobData.LastUpdate = DateTime.Now; dao.UpdateJob(job); dao.UpdateJobData(jobData); //}, false, true); } public void DeleteJob(Guid jobId) { authen.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client, HiveRoles.Slave); author.AuthorizeForJob(jobId, Permission.Full); trans.UseTransaction(() => { dao.DeleteJob(jobId); }); } public void DeleteChildJobs(Guid parentJobId) { authen.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client, HiveRoles.Slave); author.AuthorizeForJob(parentJobId, Permission.Full); trans.UseTransaction(() => { var jobs = GetChildJobs(parentJobId, true, false); foreach (var job in jobs) { dao.DeleteJob(job.Id); dao.DeleteJobData(job.Id); }; }); } public Job UpdateJobState(Guid jobId, JobState jobState, Guid? slaveId, Guid? userId, string exception) { authen.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client, HiveRoles.Slave); author.AuthorizeForJob(jobId, Permission.Full); return trans.UseTransaction(() => { Job job = dao.UpdateJobState(jobId, jobState, slaveId, userId, exception); if (job.Command.HasValue && job.Command.Value == Command.Pause && job.State == JobState.Paused) { job.Command = null; } else if (job.Command.HasValue && job.Command.Value == Command.Abort && job.State == JobState.Aborted) { job.Command = null; } else if (job.Command.HasValue && job.Command.Value == Command.Stop && job.State == JobState.Aborted) { job.Command = null; } else if (jobState == JobState.Paused && !job.Command.HasValue) { // slave paused and uploaded the job (no user-command) -> set waiting. job = dao.UpdateJobState(jobId, JobState.Waiting, slaveId, userId, exception); } dao.UpdateJob(job); return job; }); } public IEnumerable GetJobsByResourceId(Guid resourceId) { authen.AuthenticateForAnyRole(HiveRoles.Administrator); var jobs = trans.UseTransaction(() => dao.GetJobsByResourceId(resourceId)); foreach(var job in jobs) author.AuthorizeForJob(job.Id, Permission.Read); return jobs; } #endregion #region Job Control Methods public void StopJob(Guid jobId) { authen.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client, HiveRoles.Slave); author.AuthorizeForJob(jobId, Permission.Full); trans.UseTransaction(() => { var job = dao.GetJob(jobId); if (job.State == JobState.Calculating || job.State == JobState.Transferring) { job.Command = Command.Stop; dao.UpdateJob(job); } else { if (job.State != JobState.Aborted && job.State != JobState.Finished && job.State != JobState.Failed) { job = UpdateJobState(jobId, JobState.Aborted, null, null, string.Empty); } } }); } public void PauseJob(Guid jobId) { authen.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client, HiveRoles.Slave); author.AuthorizeForJob(jobId, Permission.Full); trans.UseTransaction(() => { var job = dao.GetJob(jobId); if (job.State == JobState.Calculating || job.State == JobState.Transferring) { job.Command = Command.Pause; dao.UpdateJob(job); } else { job = UpdateJobState(jobId, JobState.Paused, null, null, string.Empty); } }); } public void RestartJob(Guid jobId) { authen.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client, HiveRoles.Slave); author.AuthorizeForJob(jobId, Permission.Full); trans.UseTransaction(() => { Job job = dao.UpdateJobState(jobId, JobState.Waiting, null, userManager.CurrentUserId, string.Empty); job.Command = null; dao.UpdateJob(job); }); } #endregion #region HiveExperiment Methods public HiveExperiment GetHiveExperiment(Guid id) { authen.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client); author.AuthorizeForExperiment(id, Permission.Read); var hiveExperiment = dao.GetHiveExperiments(x => x.HiveExperimentId == id && (x.OwnerUserId == userManager.CurrentUserId || x.HiveExperimentPermissions.Count(hep => hep.Permission != Permission.NotAllowed && hep.GrantedUserId == userManager.CurrentUserId) > 0) ).FirstOrDefault(); if (hiveExperiment != null) { hiveExperiment.Permission = dao.GetPermissionForExperiment(hiveExperiment.Id, userManager.CurrentUserId); hiveExperiment.OwnerUsername = userManager.GetUserById(hiveExperiment.OwnerUserId).UserName; } return hiveExperiment; } public IEnumerable GetHiveExperiments() { authen.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client); var hiveExperiments = dao.GetHiveExperiments(x => x.OwnerUserId == userManager.CurrentUserId || x.HiveExperimentPermissions.Count(hep => hep.Permission != Permission.NotAllowed && hep.GrantedUserId == userManager.CurrentUserId) > 0); foreach (var he in hiveExperiments) { author.AuthorizeForExperiment(he.Id, Permission.Read); he.Permission = dao.GetPermissionForExperiment(he.Id, userManager.CurrentUserId); he.OwnerUsername = userManager.GetUserById(he.OwnerUserId).UserName; } return hiveExperiments; } public IEnumerable GetAllHiveExperiments() { authen.AuthenticateForAnyRole(HiveRoles.Administrator); var hiveExperiments = dao.GetHiveExperiments(x => true); foreach (var he in hiveExperiments) { // no authorization here, since this method is admin-only! (admin is allowed to read all jobs) he.Permission = dao.GetPermissionForExperiment(he.Id, userManager.CurrentUserId); he.OwnerUsername = userManager.GetUserById(he.OwnerUserId).UserName; } return hiveExperiments; } public Guid AddHiveExperiment(HiveExperiment hiveExperimentDto) { authen.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client); return trans.UseTransaction(() => { hiveExperimentDto.OwnerUserId = userManager.CurrentUserId; hiveExperimentDto.DateCreated = DateTime.Now; return dao.AddHiveExperiment(hiveExperimentDto); }); } public void UpdateHiveExperiment(HiveExperiment hiveExperimentDto) { authen.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client); author.AuthorizeForExperiment(hiveExperimentDto.Id, Permission.Full); trans.UseTransaction(() => { dao.UpdateHiveExperiment(hiveExperimentDto); }); } public void DeleteHiveExperiment(Guid hiveExperimentId) { authen.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client); author.AuthorizeForExperiment(hiveExperimentId, Permission.Full); trans.UseTransaction(() => { dao.DeleteHiveExperiment(hiveExperimentId); // child jobs will be deleted by db-trigger }); } #endregion #region HiveExperimentPermission Methods public void GrantPermission(Guid hiveExperimentId, Guid grantedUserId, Permission permission) { authen.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client); trans.UseTransaction(() => { HiveExperiment he = dao.GetHiveExperiment(hiveExperimentId); if (he == null) throw new FaultException(new FaultReason("Could not find hiveExperiment with id " + hiveExperimentId)); Permission perm = dao.GetPermissionForExperiment(he.Id, userManager.CurrentUserId); if (perm != Permission.Full) throw new FaultException(new FaultReason("Not allowed to grant permissions for this experiment")); dao.SetHiveExperimentPermission(hiveExperimentId, userManager.CurrentUserId, grantedUserId, permission); }); } public void RevokePermission(Guid hiveExperimentId, Guid grantedUserId) { authen.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client); trans.UseTransaction(() => { HiveExperiment he = dao.GetHiveExperiment(hiveExperimentId); if (he == null) throw new FaultException(new FaultReason("Could not find hiveExperiment with id " + hiveExperimentId)); Permission perm = dao.GetPermissionForExperiment(he.Id, userManager.CurrentUserId); if (perm != Permission.Full) throw new FaultException(new FaultReason("Not allowed to grant permissions for this experiment")); dao.SetHiveExperimentPermission(hiveExperimentId, userManager.CurrentUserId, grantedUserId, Permission.NotAllowed); }); } public IEnumerable GetHiveExperimentPermissions(Guid hiveExperimentId) { authen.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client); return trans.UseTransaction(() => { Permission currentUserPermission = dao.GetPermissionForExperiment(hiveExperimentId, userManager.CurrentUserId); if (currentUserPermission != Permission.Full) throw new FaultException(new FaultReason("Not allowed to list permissions for this experiment")); return dao.GetHiveExperimentPermissions(x => x.HiveExperimentId == hiveExperimentId); }); } public bool IsAllowedPrivileged() { authen.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client); return authen.IsInRole(HiveRoles.IsAllowedPrivileged); } #endregion #region Login Methods public void Hello(Slave slaveInfo) { authen.AuthenticateForAnyRole(HiveRoles.Slave); trans.UseTransaction(() => { var slave = dao.GetSlave(slaveInfo.Id); if (slave == null) { dao.AddSlave(slaveInfo); } else { var dbSlave = dao.GetSlave(slaveInfo.Id); dbSlave.Name = slaveInfo.Name; dbSlave.Description = slaveInfo.Description; dbSlave.Cores = slaveInfo.Cores; dbSlave.CpuArchitecture = slaveInfo.CpuArchitecture; dbSlave.CpuSpeed = slaveInfo.CpuSpeed; dbSlave.FreeCores = slaveInfo.FreeCores; dbSlave.FreeMemory = slaveInfo.FreeMemory; dbSlave.Memory = slaveInfo.Memory; dbSlave.OperatingSystem = slaveInfo.OperatingSystem; dbSlave.LastHeartbeat = DateTime.Now; dbSlave.SlaveState = SlaveState.Idle; // don't update those properties: dbSlave.IsAllowedToCalculate, dbSlave.ParentResourceId dao.UpdateSlave(dbSlave); } }); } public void GoodBye(Guid slaveId) { authen.AuthenticateForAnyRole(HiveRoles.Slave); trans.UseTransaction(() => { var slave = dao.GetSlave(slaveId); if (slave != null) { slave.SlaveState = SlaveState.Offline; dao.UpdateSlave(slave); } }); } #endregion #region Heartbeat Methods public List Heartbeat(Heartbeat heartbeat) { authen.AuthenticateForAnyRole(HiveRoles.Slave); TriggerLifecycle(false); return trans.UseTransaction(() => heartbeatManager.ProcessHeartbeat(heartbeat)); } #endregion #region Plugin Methods public Guid AddPlugin(Plugin plugin, List pluginDatas) { authen.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client); return trans.UseTransaction(() => { plugin.UserId = userManager.CurrentUserId; plugin.DateCreated = DateTime.Now; var existing = dao.GetPlugins(x => x.Hash != null).Where(x => x.Hash.SequenceEqual(plugin.Hash)); if (existing.Count() > 0) { // a plugin already exists. throw new FaultException(new PluginAlreadyExistsFault(existing.Single().Id)); } Guid pluginId = dao.AddPlugin(plugin); foreach (PluginData pluginData in pluginDatas) { pluginData.PluginId = pluginId; dao.AddPluginData(pluginData); } return pluginId; }); } public Plugin GetPlugin(Guid pluginId) { authen.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client, HiveRoles.Slave); return dao.GetPlugin(pluginId); } public Plugin GetPluginByHash(byte[] hash) { authen.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client, HiveRoles.Slave); return dao.GetPlugins(x => x.Hash == hash).FirstOrDefault(); } // note: this is a possible security problem, since a client is able to download all plugins, which may contain proprietary code (which can be disassembled) // change so that only with GetPluginByHash it is possible to download plugins public IEnumerable GetPlugins() { authen.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client, HiveRoles.Slave); return dao.GetPlugins(x => x.Hash != null); } public IEnumerable GetPluginDatas(List pluginIds) { authen.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client, HiveRoles.Slave); var pluginDatas = new List(); return trans.UseTransaction(() => { foreach (Guid guid in pluginIds) { pluginDatas.AddRange(dao.GetPluginDatas(x => x.PluginId == guid).ToList()); } return pluginDatas; }); } public void DeletePlugin(Guid pluginId) { authen.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client, HiveRoles.Slave); dao.DeletePlugin(pluginId); } #endregion #region Slave Methods public Guid AddSlave(Slave slave) { authen.AuthenticateForAnyRole(HiveRoles.Administrator); return trans.UseTransaction(() => dao.AddSlave(slave)); } public Guid AddSlaveGroup(SlaveGroup slaveGroup) { authen.AuthenticateForAnyRole(HiveRoles.Administrator); return trans.UseTransaction(() => dao.AddSlaveGroup(slaveGroup)); } public Slave GetSlave(Guid slaveId) { authen.AuthenticateForAnyRole(HiveRoles.Administrator); return dao.GetSlave(slaveId); } public SlaveGroup GetSlaveGroup(Guid slaveGroupId) { authen.AuthenticateForAnyRole(HiveRoles.Administrator); return dao.GetSlaveGroup(slaveGroupId); } public IEnumerable GetSlaves() { authen.AuthenticateForAnyRole(HiveRoles.Administrator); return dao.GetSlaves(x => true); } public IEnumerable GetSlaveGroups() { authen.AuthenticateForAnyRole(HiveRoles.Administrator); return dao.GetSlaveGroups(x => true); } public void UpdateSlave(Slave slave) { authen.AuthenticateForAnyRole(HiveRoles.Administrator); trans.UseTransaction(() => { dao.UpdateSlave(slave); }); } public void UpdateSlaveGroup(SlaveGroup slaveGroup) { authen.AuthenticateForAnyRole(HiveRoles.Administrator); trans.UseTransaction(() => { dao.UpdateSlaveGroup(slaveGroup); }); } public void DeleteSlave(Guid slaveId) { authen.AuthenticateForAnyRole(HiveRoles.Administrator); trans.UseTransaction(() => { dao.DeleteSlave(slaveId); }); } public void DeleteSlaveGroup(Guid slaveGroupId) { authen.AuthenticateForAnyRole(HiveRoles.Administrator); trans.UseTransaction(() => { dao.DeleteSlaveGroup(slaveGroupId); }); } public void AddResourceToGroup(Guid slaveGroupId, Guid resourceId) { authen.AuthenticateForAnyRole(HiveRoles.Administrator); trans.UseTransaction(() => { var resource = dao.GetResource(resourceId); resource.ParentResourceId = slaveGroupId; dao.UpdateResource(resource); }); } public void RemoveResourceFromGroup(Guid slaveGroupId, Guid resourceId) { authen.AuthenticateForAnyRole(HiveRoles.Administrator); trans.UseTransaction(() => { var resource = dao.GetResource(resourceId); resource.ParentResourceId = null; dao.UpdateResource(resource); }); } public Guid GetResourceId(string resourceName) { authen.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client); return trans.UseTransaction(() => { var resource = dao.GetResources(x => x.Name == resourceName).FirstOrDefault(); if (resource != null) { return resource.Id; } else { return Guid.Empty; } }); } public void TriggerLifecycle(bool force) { authen.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Slave); // use a serializable transaction here to ensure not two threads execute this simultaniously (mutex-lock would not work since IIS may use multiple AppDomains) trans.UseTransaction(() => { DateTime lastCleanup = dao.GetLastCleanup(); if (force || DateTime.Now - lastCleanup > TimeSpan.FromSeconds(59)) { dao.SetLastCleanup(DateTime.Now); lifecycleManager.Cleanup(); } }, true); } #endregion #region Downtime Methods public Guid AddDowntime(Downtime downtime) { authen.AuthenticateForAnyRole(HiveRoles.Administrator); return trans.UseTransaction(() => dao.AddDowntime(downtime)); } public void DeleteDowntime(Guid downtimeId) { authen.AuthenticateForAnyRole(HiveRoles.Administrator); trans.UseTransaction(() => { dao.DeleteDowntime(downtimeId); }); } public void UpdateDowntime(Downtime downtime) { authen.AuthenticateForAnyRole(HiveRoles.Administrator); trans.UseTransaction(() => { dao.UpdateDowntime(downtime); }); } public IEnumerable GetDowntimesForResource(Guid resourceId) { authen.AuthenticateForAnyRole(HiveRoles.Administrator); return trans.UseTransaction(() => dao.GetDowntimes(x => x.ResourceId == resourceId)); } #endregion #region User Methods public string GetUsernameByUserId(Guid userId) { authen.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client); var user = ServiceLocator.Instance.UserManager.GetUserById(userId); if (user != null) return user.UserName; else return null; } public Guid GetUserIdByUsername(string username) { authen.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client); var user = ServiceLocator.Instance.UserManager.GetUserByName(username); return user != null ? (Guid)user.ProviderUserKey : Guid.Empty; } #endregion #region Helper Methods private IEnumerable GetChildJobs(Guid? parentJobId, bool recursive, bool includeParent) { var jobs = new List(dao.GetJobs(x => parentJobId == null ? !x.ParentJobId.HasValue : x.ParentJobId.Value == parentJobId)); if (recursive) { var childs = new List(); foreach (var job in jobs) { childs.AddRange(GetChildJobs(job.Id, recursive, false)); } jobs.AddRange(childs); } if (includeParent) jobs.Add(GetJob(parentJobId.Value)); return jobs; } #endregion } }