Exemple #1
0
 public ServerFilesystemInfo(ServerFilesystemInfo copy)
 {
     _filesystem = copy.Filesystem;
     _online     = copy.Online;
     _freeBytes  = copy.FreeBytes;
     _totalBytes = copy.TotalBytes;
 }
        /// <summary>
        /// Gets the first filesystem in lower tier for storage purpose.
        /// </summary>
        /// <param name="filesystem">The current filesystem</param>
        /// <returns></returns>
        public ServerFilesystemInfo GetLowerTierFilesystemForStorage(ServerFilesystemInfo filesystem)
        {
            lock (SyncLock)
            {
                List <FilesystemTierEnum> lowerTiers = FindLowerTierFilesystems(filesystem);
                if (lowerTiers == null || lowerTiers.Count == 0)
                {
                    return(null);
                }

                List <ServerFilesystemInfo> list = new List <ServerFilesystemInfo>();
                foreach (FilesystemTierEnum tier in lowerTiers)
                {
                    list.AddRange(_tierInfo[tier]);
                }
                CollectionUtils.Remove(list, fs => !fs.Writeable);
                list = CollectionUtils.Sort(list, FilesystemSorter.SortByFreeSpace);
                ServerFilesystemInfo lowtierFilesystem = CollectionUtils.FirstElement(list);
                if (lowtierFilesystem == null)
                {
                    return(null);
                }
                return(new ServerFilesystemInfo(lowtierFilesystem));//return a copy
            }
        }
        /// <summary>
        /// Checks for a storage location for the study in the database, and creates a new location
        /// in the database if it doesn't exist.
        /// </summary>
        /// <param name="partition">The partition where the study is being sent to</param>
        /// <param name="created"></param>
        /// <param name="studyDate"></param>
        /// <param name="studyInstanceUid"></param>
        /// <param name="syntax"></param>
        /// <param name="updateContext">The update context to create the study on</param>
        /// <returns>A <see cref="StudyStorageLocation"/> instance.</returns>
        public StudyStorageLocation GetOrCreateWritableStudyStorageLocation(string studyInstanceUid, string studyDate, TransferSyntax syntax, IUpdateContext updateContext, ServerPartition partition, out bool created)
        {
            created = false;

            StudyStorageLocation location;

            try
            {
                GetWritableStudyStorageLocation(partition.Key, studyInstanceUid, StudyRestore.True,
                                                StudyCache.True, out location);
                return(location);
            }
            catch (StudyNotFoundException)
            {
            }

            FilesystemSelector   selector   = new FilesystemSelector(Instance);
            ServerFilesystemInfo filesystem = selector.SelectFilesystem();

            if (filesystem == null)
            {
                throw new NoWritableFilesystemException();
            }

            IInsertStudyStorage          locInsert   = updateContext.GetBroker <IInsertStudyStorage>();
            InsertStudyStorageParameters insertParms = new InsertStudyStorageParameters
            {
                ServerPartitionKey = partition.GetKey(),
                StudyInstanceUid   = studyInstanceUid,
                Folder             =
                    ResolveStorageFolder(partition.Key, studyInstanceUid, studyDate,
                                         updateContext, false
                                         /* set to false for optimization because we are sure it's not in the system */),
                FilesystemKey       = filesystem.Filesystem.GetKey(),
                QueueStudyStateEnum = QueueStudyStateEnum.Idle
            };

            if (syntax.LosslessCompressed)
            {
                insertParms.TransferSyntaxUid = syntax.UidString;
                insertParms.StudyStatusEnum   = StudyStatusEnum.OnlineLossless;
            }
            else if (syntax.LossyCompressed)
            {
                insertParms.TransferSyntaxUid = syntax.UidString;
                insertParms.StudyStatusEnum   = StudyStatusEnum.OnlineLossy;
            }
            else
            {
                insertParms.TransferSyntaxUid = syntax.UidString;
                insertParms.StudyStatusEnum   = StudyStatusEnum.Online;
            }

            location = locInsert.FindOne(insertParms);
            created  = true;

            return(location);
        }
        /// <summary>
        /// Find lower tier filesystems.
        /// </summary>
        /// <param name="filesystem"></param>
        /// <returns></returns>
        private List <FilesystemTierEnum> FindLowerTierFilesystems(ServerFilesystemInfo filesystem)
        {
            List <FilesystemTierEnum> lowerTiers = new List <FilesystemTierEnum>();

            foreach (FilesystemTierEnum tier in _tierInfo.Keys)
            {
                if (tier.Enum > filesystem.Filesystem.FilesystemTierEnum.Enum)
                {
                    lowerTiers.Add(tier);
                }
            }

            return(lowerTiers);
        }
Exemple #5
0
        public static int SortByFreeSpace(ServerFilesystemInfo fs1, ServerFilesystemInfo fs2)
        {
            Platform.CheckForNullReference(fs1, "fs1");
            Platform.CheckForNullReference(fs2, "fs2");
            Platform.CheckForNullReference(fs1.Filesystem, "fs1.Filesystem");
            Platform.CheckForNullReference(fs2.Filesystem, "fs2.Filesystem");

            if (fs1 == fs2)
                return 0;

            if (fs1.Filesystem.FilesystemTierEnum.Enum.Equals(fs2.Filesystem.FilesystemTierEnum.Enum))
            {
                // descending order on available size.. smaller margin means less available space
                return fs2.HighwaterMarkMargin.CompareTo(fs1.HighwaterMarkMargin);
            }
            else
            {
                // ascending order on tier
                return fs1.Filesystem.FilesystemTierEnum.Enum.CompareTo(fs2.Filesystem.FilesystemTierEnum.Enum);
            }
        }
        /// <summary>
        /// Helper method to verify if the specified <see cref="StudyStorageLocation"/>
        /// is writable and throw <see cref="FilesystemNotWritableException"/> if it is not.
        /// </summary>
        /// <param name="location"></param>
        public void EnsureStorageLocationIsWritable(StudyStorageLocation location)
        {
            ServerFilesystemInfo fs = GetFilesystemInfo(location.FilesystemKey);

            if (!fs.Enable)
            {
                throw new FilesystemNotWritableException(fs.Filesystem.FilesystemPath)
                      {
                          Reason = "It is disabled"
                      }
            }
            ;

            if (!fs.Online)
            {
                throw new FilesystemNotWritableException(fs.Filesystem.FilesystemPath)
                      {
                          Reason = "It is offline/unreachable"
                      }
            }
            ;

            if (fs.ReadOnly)
            {
                throw new FilesystemNotWritableException(fs.Filesystem.FilesystemPath)
                      {
                          Reason = "It is read-only"
                      }
            }
            ;

            if (fs.Full)
            {
                throw new FilesystemNotWritableException(fs.Filesystem.FilesystemPath)
                      {
                          Reason = "It is full"
                      }
            }
            ;
        }
Exemple #7
0
        public static int SortByFreeSpace(ServerFilesystemInfo fs1, ServerFilesystemInfo fs2)
        {
            Platform.CheckForNullReference(fs1, "fs1");
            Platform.CheckForNullReference(fs2, "fs2");
            Platform.CheckForNullReference(fs1.Filesystem, "fs1.Filesystem");
            Platform.CheckForNullReference(fs2.Filesystem, "fs2.Filesystem");

            if (fs1 == fs2)
            {
                return(0);
            }

            if (fs1.Filesystem.FilesystemTierEnum.Enum.Equals(fs2.Filesystem.FilesystemTierEnum.Enum))
            {
                // descending order on available size.. smaller margin means less available space
                return(fs2.HighwaterMarkMargin.CompareTo(fs1.HighwaterMarkMargin));
            }
            else
            {
                // ascending order on tier
                return(fs1.Filesystem.FilesystemTierEnum.Enum.CompareTo(fs2.Filesystem.FilesystemTierEnum.Enum));
            }
        }
Exemple #8
0
        public ServerFilesystemInfo SelectFilesystem()
        {
            IList <ServerFilesystemInfo> list       = new List <ServerFilesystemInfo>(_monitor.GetFilesystems());
            IList <ServerFilesystemInfo> writableFS = CollectionUtils.Select(list, delegate(ServerFilesystemInfo fs) { return(fs.Writeable); });

            StringBuilder log = new StringBuilder();

            if (writableFS == null || writableFS.Count == 0)
            {
                log.AppendLine("No writable storage found");
                foreach (ServerFilesystemInfo fs in list)
                {
                    log.AppendLine(string.Format("\t{0} : {1}", fs.Filesystem.Description, fs.StatusString));
                }
                Platform.Log(LogLevel.Warn, log.ToString());
                return(null);
            }

            writableFS = CollectionUtils.Sort(writableFS, FilesystemSorter.SortByFreeSpace);
            ServerFilesystemInfo selectedFS = CollectionUtils.FirstElement(writableFS);

            Platform.CheckForNullReference(selectedFS, "selectedFS");
            return(selectedFS);
        }
        /// <summary>
        /// Load filesystem information from the database.
        /// </summary>
        private void LoadFilesystems()
        {
            bool changed = false;

            lock (SyncLock)
            {
                try
                {
                    List <FilesystemTierEnum> tiers = FilesystemTierEnum.GetAll();

                    // sorted by enum values
                    tiers.Sort((tier1, tier2) => tier1.Enum.CompareTo(tier2.Enum));

                    _tierInfo = new TierInfo();

                    foreach (FilesystemTierEnum tier in tiers)
                    {
                        _tierInfo.Add(tier, new List <ServerFilesystemInfo>());
                    }

                    using (IReadContext read = _store.OpenReadContext())
                    {
                        IFilesystemEntityBroker  filesystemSelect = read.GetBroker <IFilesystemEntityBroker>();
                        FilesystemSelectCriteria criteria         = new FilesystemSelectCriteria();
                        IList <Filesystem>       filesystemList   = filesystemSelect.Find(criteria);

                        foreach (Filesystem filesystem in filesystemList)
                        {
                            if (_filesystemList.ContainsKey(filesystem.Key))
                            {
                                if ((filesystem.HighWatermark != _filesystemList[filesystem.Key].Filesystem.HighWatermark) ||
                                    (filesystem.LowWatermark != _filesystemList[filesystem.Key].Filesystem.LowWatermark))
                                {
                                    Platform.Log(LogLevel.Info, "Watermarks have changed for filesystem {0}, Low: {1}, High: {2}",
                                                 filesystem.Description, filesystem.LowWatermark, filesystem.HighWatermark);
                                }
                                _filesystemList[filesystem.Key].Filesystem = filesystem;
                                _tierInfo[filesystem.FilesystemTierEnum].Add(_filesystemList[filesystem.Key]);
                            }
                            else
                            {
                                ServerFilesystemInfo info = new ServerFilesystemInfo(filesystem);
                                _filesystemList.Add(filesystem.Key, info);
                                _tierInfo[filesystem.FilesystemTierEnum].Add(info);
                                info.LoadFreeSpace();
                                changed = true;
                            }
                        }
                    }
                    if (changed && _changedListener != null)
                    {
                        EventsHelper.Fire(_changedListener, this, new FilesystemChangedEventArgs(this));
                    }
                }
                catch (Exception ex)
                {
                    Platform.Log(LogLevel.Error, ex,
                                 "Exception has occurred while updating the filesystem list from the datbase. Retry later");
                }
            }
        }
        /// <summary>
        /// Returns a value indicating whether the filesystem with the specified key
        /// is writable.
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public bool IsWritable(ServerEntityKey key)
        {
            ServerFilesystemInfo fs = GetFilesystemInfo(key);

            return(fs.Writeable);
        }
 public ServerFilesystemInfo(ServerFilesystemInfo copy)
 {
     _filesystem = copy.Filesystem;
     _online = copy.Online;
     _freeBytes = copy.FreeBytes;
     _totalBytes = copy.TotalBytes;
 }
		private void PurgeStudies(Model.ServiceLock item, ServerFilesystemInfo fs)
		{
			Platform.Log(LogLevel.Info, "Starting query for Filesystem Purge candidates on '{0}'.",
						 fs.Filesystem.Description);
			try
			{
				DoStudyPurge(fs, item);
			}
			catch (Exception e)
			{
				Platform.Log(LogLevel.Error, e, "Unexpected exception when processing StudyDelete records.");
			}

			Platform.Log(LogLevel.Info, "{0} studies have been scheduled for purging from filesystem '{1}'", 
				_studiesPurged, fs.Filesystem.Description);
		}
		private void MigrateStudies(Model.ServiceLock item, ServerFilesystemInfo fs)
		{
			ServerFilesystemInfo newFilesystem = FilesystemMonitor.Instance.GetLowerTierFilesystemForStorage(fs);
			if (newFilesystem == null)
			{
				Platform.Log(LogLevel.Warn,
				             "No writable storage in lower tiers. Tier-migration for '{0}' is disabled at this time.",
				             fs.Filesystem.Description);
				return;
			}

			Platform.Log(LogLevel.Info, "Starting Tier Migration from {0}", fs.Filesystem.Description);

			try
			{
				DoStudyMigrate(fs, item);
			}
			catch (Exception e)
			{
				Platform.Log(LogLevel.Error, e, "Unexpected exception when scheduling tier-migration.");
			}

			Platform.Log(LogLevel.Info, "{0} studies have been scheduled for migration from filesystem '{1}'",
			             _studiesMigrated, fs.Filesystem.Description);
		}
		/// <summary>
		/// Do the actual Study migration.
		/// </summary>
		/// <param name="fs">The filesystem</param>
		/// <param name="item">The ServiceLock item being processed.</param>
        private void DoStudyMigrate( ServerFilesystemInfo fs, Model.ServiceLock item)
        {
            FilesystemQueueTypeEnum type = FilesystemQueueTypeEnum.TierMigrate;

            while (_bytesToRemove > 0)
            {
				TimeSpanStatistics queryTime = new TimeSpanStatistics("Query Time");
				queryTime.Start();
                IList<FilesystemQueue> list = GetFilesystemQueueCandidates(item, Platform.Time, type, false);
				queryTime.End();
				Platform.Log(LogLevel.Info,
							 "{1:0.0} MBs needs to be removed from '{0}'. Found {2} studies that can be migrated in {3} seconds",
							 fs.Filesystem.Description, _bytesToRemove / (1024 * 1024),
							 list.Count,
							 queryTime.Value.TotalSeconds);
				if (list.Count > 0)
                {
                    ProcessStudyMigrateCandidates(list);
					if (CancelPending) break;
                }
                else
                {
                    break;
                }
            }
        }
		/// <summary>
		/// Do the actual StudyPurge
		/// </summary>
		/// <param name="item"></param>
		/// <param name="fs">The filesystem being worked on.</param>
		private void DoStudyPurge(ServerFilesystemInfo fs, Model.ServiceLock item)
		{
			DateTime deleteTime = Platform.Time;
			FilesystemQueueTypeEnum type = FilesystemQueueTypeEnum.PurgeStudy;

			while (_bytesToRemove > 0)
			{
				Platform.Log(LogLevel.Debug,
							 "{1:0.0} MBs needs to be removed from '{0}'. Querying for studies that can be purged",
							 fs.Filesystem.Description, _bytesToRemove / (1024 * 1024));
				IList<FilesystemQueue> list =
					GetFilesystemQueueCandidates(item, deleteTime, type, false);

				if (list.Count > 0)
				{
					ProcessStudyPurgeCandidates(list);
					if (CancelPending) break;
				}
				else
				{
					// No candidates
					break;
				}
			}
		}
        /// <summary>
        /// Updates the 'State' of the filesystem associated with the 'FilesystemDelete' <see cref="ServiceLock"/> item
        /// </summary>
        /// <param name="item"></param>
        /// <param name="fs"></param>
        private static void UpdateState(Model.ServiceLock item, ServerFilesystemInfo fs)
        {
            FilesystemState state = null;
            if (item.State != null && item.State.DocumentElement!=null)
            {
                //load from datatabase
                state = XmlUtils.Deserialize<FilesystemState>(item.State.DocumentElement);
            }

            if (state == null)
                state = new FilesystemState();

            if (fs.AboveHighWatermark)
            {
                // we don't want to generate alert if the filesystem is offline or not accessible.
                if (fs.Online && (fs.Readable || fs.Writeable))
                {
                    TimeSpan ALERT_INTERVAL = TimeSpan.FromMinutes(ServiceLockSettings.Default.HighWatermarkAlertInterval);

                    if (state.AboveHighWatermarkTimestamp == null)
                        state.AboveHighWatermarkTimestamp = Platform.Time;

                    TimeSpan elapse = (state.LastHighWatermarkAlertTimestamp != null) ? Platform.Time - state.LastHighWatermarkAlertTimestamp.Value : Platform.Time - state.AboveHighWatermarkTimestamp.Value;

                    if (elapse.Duration() >= ALERT_INTERVAL)
                    {
                        ServerPlatform.Alert(AlertCategory.System, AlertLevel.Warning, "Filesystem",
                                             AlertTypeCodes.LowResources, null, TimeSpan.Zero,
                                             SR.AlertFilesystemAboveHW,
                                             fs.Filesystem.Description,
                                             TimeSpanFormatter.Format(Platform.Time - state.AboveHighWatermarkTimestamp.Value, true));


                        state.LastHighWatermarkAlertTimestamp = Platform.Time;
                    }
                }
                else
                {
                    state.AboveHighWatermarkTimestamp = null;
                    state.LastHighWatermarkAlertTimestamp = null;
                }
            }
            else
            {
                state.AboveHighWatermarkTimestamp = null;
                state.LastHighWatermarkAlertTimestamp = null;
            }


            XmlDocument stateXml = new XmlDocument();
            stateXml.AppendChild(stateXml.ImportNode(XmlUtils.Serialize(state), true));

            IPersistentStore store = PersistentStoreRegistry.GetDefaultStore();
            using (IUpdateContext ctx = store.OpenUpdateContext(UpdateContextSyncMode.Flush))
            {
                ServiceLockUpdateColumns columns = new ServiceLockUpdateColumns();
                columns.State = stateXml;

                IServiceLockEntityBroker broker = ctx.GetBroker<IServiceLockEntityBroker>();
                broker.Update(item.GetKey(), columns);
                ctx.Commit();
            }
        }
		private bool ArchiveLogs(ServerFilesystemInfo archiveFs)
		{
			string archivePath = Path.Combine(archiveFs.Filesystem.FilesystemPath, "AlertLog");

			DateTime cutOffTime = Platform.Time.Date.AddDays(ServiceLockSettings.Default.AlertCachedDays*-1);
			AlertSelectCriteria criteria = new AlertSelectCriteria();
			criteria.InsertTime.LessThan(cutOffTime);
			criteria.InsertTime.SortAsc(0);

			using (ServerExecutionContext context = new ServerExecutionContext())
			{
				IAlertEntityBroker broker = context.ReadContext.GetBroker<IAlertEntityBroker>();

				ImageServerLogWriter<Alert> writer = new ImageServerLogWriter<Alert>(archivePath, "Alert");

				List<ServerEntityKey> keyList = new List<ServerEntityKey>(500);
				try
				{
					broker.Find(criteria, delegate(Alert result)
					                      	{
					                      		keyList.Add(result.Key);

					                      		// If configured, don't flush to disk.  We just delete the contents of keyList below.
					                      		if (!ServiceLockSettings.Default.AlertDelete)
					                      		{
					                      			if (writer.WriteLog(result, result.InsertTime))
					                      			{
					                      				// The logs been flushed, delete the log entries cached.
					                      				using (
					                      					IUpdateContext update =
					                      						PersistentStoreRegistry.GetDefaultStore().OpenUpdateContext(UpdateContextSyncMode.Flush)
					                      					)
					                      				{
					                      					IApplicationLogEntityBroker updateBroker =
					                      						update.GetBroker<IApplicationLogEntityBroker>();
					                      					foreach (ServerEntityKey key in keyList)
					                      						updateBroker.Delete(key);
					                      					update.Commit();
					                      				}
					                      				keyList = new List<ServerEntityKey>();
					                      			}
					                      		}
					                      	});

					writer.FlushLog();

					if (keyList.Count > 0)
					{
						using (
							IUpdateContext update =
								PersistentStoreRegistry.GetDefaultStore().OpenUpdateContext(UpdateContextSyncMode.Flush))
						{
							IAlertEntityBroker updateBroker = update.GetBroker<IAlertEntityBroker>();
							foreach (ServerEntityKey key in keyList)
								updateBroker.Delete(key);
							update.Commit();
						}
					}
				}
				catch (Exception e)
				{
					Platform.Log(LogLevel.Error, e, "Unexpected exception when purging Alert log files.");
					writer.Dispose();
					return false;
				}
				writer.Dispose();

				return true;
			}
		}
		private bool ArchiveLogs(ServerFilesystemInfo archiveFs)
		{
			try
			{
				using (ServerExecutionContext context = new ServerExecutionContext())
				{
					string archivePath = Path.Combine(archiveFs.Filesystem.FilesystemPath, "ApplicationLog");

					ApplicationLogSelectCriteria criteria = new ApplicationLogSelectCriteria();
					criteria.Timestamp.SortAsc(0);
					IApplicationLogEntityBroker broker = context.ReadContext.GetBroker<IApplicationLogEntityBroker>();
					ApplicationLog firstLog = broker.FindOne(criteria);
					if (firstLog == null)
						return true;

					DateTime currentCutOffTime = firstLog.Timestamp.AddMinutes(5);

					int cachedDays = ServiceLockSettings.Default.ApplicationLogCachedDays;
					if (cachedDays < 0) cachedDays = 0;

					DateTime cutOffTime = Platform.Time.Date.AddDays(cachedDays*-1);

					if (currentCutOffTime > cutOffTime)
						return true;

					using (
						ImageServerLogWriter<ApplicationLog> writer =
							new ImageServerLogWriter<ApplicationLog>(archivePath, "ApplicationLog"))
					{
						while (currentCutOffTime < cutOffTime)
						{
							if (!ArchiveTimeRange(writer, currentCutOffTime))
							{
								writer.FlushLog();
								return false;
							}
							currentCutOffTime = currentCutOffTime.AddMinutes(5);
						}

						// Now flush the last potential 5 minutes.
						if (!ArchiveTimeRange(writer, cutOffTime))
						{
							writer.FlushLog();
							return false;
						}

						writer.FlushLog();
					}

					return true;
				}
			}
			catch (Exception e)
			{
				Platform.Log(LogLevel.Error, e, "Unexpected exception when writing log file.");
				return false;
			}
		}