From 0387f8e33e2132019fcf07c91b54ccde35a7ffc1 Mon Sep 17 00:00:00 2001 From: ta264 Date: Fri, 7 Aug 2020 22:13:01 +0100 Subject: [PATCH] New: Calibre root folder health check --- .../Checks/CalibreRootFolderCheck.cs | 128 ++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/NzbDrone.Core/HealthCheck/Checks/CalibreRootFolderCheck.cs diff --git a/src/NzbDrone.Core/HealthCheck/Checks/CalibreRootFolderCheck.cs b/src/NzbDrone.Core/HealthCheck/Checks/CalibreRootFolderCheck.cs new file mode 100644 index 000000000..609100562 --- /dev/null +++ b/src/NzbDrone.Core/HealthCheck/Checks/CalibreRootFolderCheck.cs @@ -0,0 +1,128 @@ +using System; +using System.IO; +using System.Linq; +using NLog; +using NzbDrone.Common.Disk; +using NzbDrone.Common.EnvironmentInfo; +using NzbDrone.Common.Extensions; +using NzbDrone.Core.Books.Calibre; +using NzbDrone.Core.Datastore.Events; +using NzbDrone.Core.Download.Clients; +using NzbDrone.Core.RemotePathMappings; +using NzbDrone.Core.RootFolders; + +namespace NzbDrone.Core.HealthCheck.Checks +{ + [CheckOn(typeof(ModelEvent))] + [CheckOn(typeof(ModelEvent))] + public class CalibreRootFolderCheck : HealthCheckBase + { + private readonly IDiskProvider _diskProvider; + private readonly IRootFolderService _rootFolderService; + private readonly ICalibreProxy _calibreProxy; + private readonly Logger _logger; + private readonly IOsInfo _osInfo; + + public CalibreRootFolderCheck(IDiskProvider diskProvider, + IRootFolderService rootFolderService, + ICalibreProxy calibreProxy, + IOsInfo osInfo, + Logger logger) + { + _diskProvider = diskProvider; + _rootFolderService = rootFolderService; + _calibreProxy = calibreProxy; + _logger = logger; + _osInfo = osInfo; + } + + public override HealthCheck Check() + { + var rootFolders = _rootFolderService.All().Where(x => x.IsCalibreLibrary); + + foreach (var folder in rootFolders) + { + try + { + var calibreIsLocal = folder.CalibreSettings.Host == "127.0.0.1" || folder.CalibreSettings.Host == "localhost"; + + var files = _calibreProxy.GetAllBookFilePaths(folder.CalibreSettings); + if (files.Any()) + { + var file = files.First(); + + // This directory structure is forced by calibre + var bookFolder = Path.GetDirectoryName(file); + var authorFolder = Path.GetDirectoryName(bookFolder); + var libraryFolder = Path.GetDirectoryName(authorFolder); + + var osPath = new OsPath(libraryFolder); + + if (!osPath.IsValid) + { + if (!calibreIsLocal) + { + return new HealthCheck(GetType(), HealthCheckResult.Error, $"Remote calibre for root folder {folder.Name} reports files in {libraryFolder} but this is not a valid {_osInfo.Name} path. Review your remote path mappings and root folder settings.", "#bad-remote-path-mapping"); + } + else if (_osInfo.IsDocker) + { + return new HealthCheck(GetType(), HealthCheckResult.Error, $"You are using docker; calibre for root folder {folder.Name} reports files in {libraryFolder} but this is not a valid {_osInfo.Name} path. Review your remote path mappings and download client settings.", "#docker-bad-remote-path-mapping"); + } + else + { + return new HealthCheck(GetType(), HealthCheckResult.Error, $"Local calibre server for root folder {folder.Name} reports files in {libraryFolder} but this is not a valid {_osInfo.Name} path. Review your download client settings.", "#bad-download-client-settings"); + } + } + + if (!_diskProvider.FolderExists(libraryFolder)) + { + if (_osInfo.IsDocker) + { + return new HealthCheck(GetType(), HealthCheckResult.Error, $"You are using docker; calibre server for root folder {folder.Name} places downloads in {libraryFolder} but this directory does not appear to exist inside the container. Review your remote path mappings and container volume settings.", "#docker-bad-remote-path-mapping"); + } + else if (!calibreIsLocal) + { + return new HealthCheck(GetType(), HealthCheckResult.Error, $"Remote calibre server for root folder {folder.Name} places downloads in {libraryFolder} but this directory does not appear to exist. Likely missing or incorrect remote path mapping.", "#bad-remote-path-mapping"); + } + else + { + return new HealthCheck(GetType(), HealthCheckResult.Error, $"Calibre server for root folder {folder.Name} places downloads in {libraryFolder} but Readarr cannot see this directory. You may need to adjust the folder's permissions or add a remote path mapping if calibre is running in docker", "#permissions-error"); + } + } + + if (!_diskProvider.FileExists(file)) + { + if (_osInfo.IsDocker) + { + return new HealthCheck(GetType(), HealthCheckResult.Error, $"You are using docker; calibre server for root folder {folder.Name} listed file {file} but this file does not appear to exist inside the container. Review permissions for {libraryFolder} and PUID/PGID container settings", "#docker-bad-remote-path-mapping"); + } + else if (!calibreIsLocal) + { + return new HealthCheck(GetType(), HealthCheckResult.Error, $"Remote calibre server for root folder {folder.Name} listed file {file} but this file does not appear to exist. Review permissions for {libraryFolder}", "#permissions-error"); + } + else + { + return new HealthCheck(GetType(), HealthCheckResult.Error, $"Calibre server for root folder {folder.Name} listed file {file} but Readarr cannot see this file. Review permissions for {libraryFolder}", "#permissions-error"); + } + } + + if (!libraryFolder.PathEquals(folder.Path)) + { + return new HealthCheck(GetType(), HealthCheckResult.Error, $"Calibre for root folder {folder.Name} reports files in {libraryFolder} but this is not the same as the root folder path {folder.Path} you chose. You may need to edit any remote path mapping or delete the root folder and re-create with the correct path", "#calibre-root-does-not-match"); + } + } + } + catch (DownloadClientException ex) + { + _logger.Debug(ex, "Unable to communicate with calibre server for root folder {0}", folder.Name); + } + catch (Exception ex) + { + _logger.Error(ex, "Unknown error occured in CalibreRootFolderCheck HealthCheck"); + } + } + + return new HealthCheck(GetType()); + } + } +}