├── .dockerignore
├── .editorconfig
├── .filenesting.json
├── .gitattributes
├── .github
└── workflows
│ ├── docker.yml
│ └── main.yml
├── .gitignore
├── Dockerfile
├── LICENSE
├── Opserver.sln
├── Opserver.sln.DotSettings
├── appveyor.yml
├── docs
├── Configuration.md
├── Docs.csproj
├── Gemfile
├── HowTo
│ ├── BuildAndRun.md
│ └── UpgradeFromV1.md
├── Releases.md
├── _config.yml
├── _includes
│ └── navigation.html
├── _layouts
│ └── default.html
├── css
│ └── main.css
└── index.md
├── nuget.config
├── readme.md
├── src
├── Directory.Build.props
├── Opserver.Core
│ ├── Data
│ │ ├── Cache.cs
│ │ ├── CloudFlare
│ │ │ ├── CloudFlareAPI.Actions.cs
│ │ │ ├── CloudFlareAPI.Zones.cs
│ │ │ ├── CloudFlareAPI.cs
│ │ │ ├── CloudFlareModule.cs
│ │ │ └── CloudFlareResponse.cs
│ │ ├── Cloudflare
│ │ │ └── CloudflareRoles.cs
│ │ ├── Dashboard
│ │ │ ├── Application.cs
│ │ │ ├── DashboardCategory.cs
│ │ │ ├── DashboardModule.cs
│ │ │ ├── DashboardRoles.cs
│ │ │ ├── GraphPoint.cs
│ │ │ ├── HardwareSummary.cs
│ │ │ ├── HardwareType.cs
│ │ │ ├── IServiceControlProvider.cs
│ │ │ ├── Interface.Data.cs
│ │ │ ├── Interface.cs
│ │ │ ├── Node.Data.cs
│ │ │ ├── Node.Polling.cs
│ │ │ ├── Node.cs
│ │ │ ├── NodeService.cs
│ │ │ ├── NodeStatus.cs
│ │ │ ├── Providers
│ │ │ │ ├── BosunDataProvider.Metrics.cs
│ │ │ │ ├── BosunDataProvider.Nodes.cs
│ │ │ │ ├── BosunDataProvider.cs
│ │ │ │ ├── DashboardDataProvider.cs
│ │ │ │ ├── EmptyDataProvider.cs
│ │ │ │ ├── OrionDataProvider.cs
│ │ │ │ ├── SignalFxDataProvider.HostProperties.cs
│ │ │ │ ├── SignalFxDataProvider.SignalFlow.cs
│ │ │ │ ├── SignalFxDataProvider.cs
│ │ │ │ ├── WmiDataProvider.Data.cs
│ │ │ │ ├── WmiDataProvider.Polling.cs
│ │ │ │ └── WmiDataProvider.cs
│ │ │ ├── Volume.Data.cs
│ │ │ └── Volume.cs
│ │ ├── DataApi.cs
│ │ ├── Elastic
│ │ │ ├── ElasticCluster.Actions.cs
│ │ │ ├── ElasticCluster.Aliases.cs
│ │ │ ├── ElasticCluster.HealthStatus.cs
│ │ │ ├── ElasticCluster.IndexStats.cs
│ │ │ ├── ElasticCluster.Issues.cs
│ │ │ ├── ElasticCluster.KnownNodes.cs
│ │ │ ├── ElasticCluster.Nodes.cs
│ │ │ ├── ElasticCluster.State.cs
│ │ │ ├── ElasticCluster.cs
│ │ │ ├── ElasticModule.cs
│ │ │ ├── ElasticRoles.cs
│ │ │ └── ShardStates.cs
│ │ ├── Exceptions
│ │ │ ├── Application.cs
│ │ │ ├── ExceptionSorts.cs
│ │ │ ├── ExceptionStore.cs
│ │ │ ├── ExceptionsModule.cs
│ │ │ └── ExceptionsRoles.cs
│ │ ├── GlobalPollingStatus.cs
│ │ ├── HAProxy
│ │ │ ├── Backend.cs
│ │ │ ├── Frontend.cs
│ │ │ ├── HAProxyAdmin.cs
│ │ │ ├── HAProxyGroup.Issues.cs
│ │ │ ├── HAProxyGroup.cs
│ │ │ ├── HAProxyInstance.Roles.cs
│ │ │ ├── HAProxyInstance.cs
│ │ │ ├── HAProxyModule.cs
│ │ │ ├── HAProxyRoles.cs
│ │ │ ├── Item.cs
│ │ │ ├── Proxy.cs
│ │ │ ├── Server.cs
│ │ │ ├── ServerStatus.cs
│ │ │ ├── Socket.cs
│ │ │ ├── StatAttribute.cs
│ │ │ ├── StatProperty.cs
│ │ │ └── StatusType.cs
│ │ ├── IPNet.cs
│ │ ├── Issue.cs
│ │ ├── Jira
│ │ │ └── JiraClient.cs
│ │ ├── MonitorStatus.cs
│ │ ├── NodeRole.cs
│ │ ├── PagerDuty
│ │ │ ├── IncidentStatus.cs
│ │ │ ├── PagerDutyAPI.Actions.cs
│ │ │ ├── PagerDutyAPI.Incidents.cs
│ │ │ ├── PagerDutyAPI.Logs.cs
│ │ │ ├── PagerDutyAPI.OnCall.cs
│ │ │ ├── PagerDutyAPI.Schedules.cs
│ │ │ ├── PagerDutyAPI.cs
│ │ │ ├── PagerDutyModule.cs
│ │ │ └── PagerDutyRoles.cs
│ │ ├── PollNode.Equality.cs
│ │ ├── PollNode.Events.cs
│ │ ├── PollNode.cs
│ │ ├── PollingService.Issues.cs
│ │ ├── PollingService.NodeRoles.cs
│ │ ├── PollingService.Polling.cs
│ │ ├── PollingService.Stats.cs
│ │ ├── PollingService.cs
│ │ ├── Redis
│ │ │ ├── RedisAnalyzer.Memory.cs
│ │ │ ├── RedisConnectionInfo.cs
│ │ │ ├── RedisHost.cs
│ │ │ ├── RedisInfo.Parsing.cs
│ │ │ ├── RedisInfo.PropertyAttribute.cs
│ │ │ ├── RedisInfo.cs
│ │ │ ├── RedisInstance.Actions.cs
│ │ │ ├── RedisInstance.Clients.cs
│ │ │ ├── RedisInstance.Config.cs
│ │ │ ├── RedisInstance.Info.cs
│ │ │ ├── RedisInstance.Issues.cs
│ │ │ ├── RedisInstance.SlowLog.cs
│ │ │ ├── RedisInstance.cs
│ │ │ ├── RedisInstanceOperation.cs
│ │ │ ├── RedisModule.cs
│ │ │ ├── RedisReplicationGroup.cs
│ │ │ └── RedisRoles.cs
│ │ ├── SQL
│ │ │ ├── Enums.cs
│ │ │ ├── ISQLVersioned.cs
│ │ │ ├── QueryPlans
│ │ │ │ ├── ShowPlanXML.cs
│ │ │ │ └── ShowPlanXML.generated.cs
│ │ │ ├── SQLAzureServer.cs
│ │ │ ├── SQLCluster.Issues.cs
│ │ │ ├── SQLCluster.cs
│ │ │ ├── SQLInstance.ActiveOperations.cs
│ │ │ ├── SQLInstance.CPUHistory.cs
│ │ │ ├── SQLInstance.Configuration.cs
│ │ │ ├── SQLInstance.Connections.cs
│ │ │ ├── SQLInstance.Databases.cs
│ │ │ ├── SQLInstance.Errors.cs
│ │ │ ├── SQLInstance.Features.cs
│ │ │ ├── SQLInstance.Issues.cs
│ │ │ ├── SQLInstance.Jobs.cs
│ │ │ ├── SQLInstance.Memory.cs
│ │ │ ├── SQLInstance.PerfCounters.cs
│ │ │ ├── SQLInstance.Properties.cs
│ │ │ ├── SQLInstance.Services.cs
│ │ │ ├── SQLInstance.TopOperations.cs
│ │ │ ├── SQLInstance.TraceFlags.cs
│ │ │ ├── SQLInstance.Utils.cs
│ │ │ ├── SQLInstance.Volumes.cs
│ │ │ ├── SQLInstance.WaitStats.cs
│ │ │ ├── SQLInstance.cs
│ │ │ ├── SQLModule.cs
│ │ │ ├── SQLNode.AvailabilityGroups.AGDatabaseReplica.cs
│ │ │ ├── SQLNode.AvailabilityGroups.AGInfo.cs
│ │ │ ├── SQLNode.AvailabilityGroups.AGListener.cs
│ │ │ ├── SQLNode.AvailabilityGroups.AGListenerIPAddress.cs
│ │ │ ├── SQLNode.AvailabilityGroups.AGReplica.cs
│ │ │ ├── SQLNode.AvailabilityGroups.cs
│ │ │ ├── SQLNode.ClusterInfo.cs
│ │ │ ├── SQLNode.TCPListeners.cs
│ │ │ ├── SQLNode.cs
│ │ │ ├── SQLRoles.cs
│ │ │ └── SQLServerEngine.cs
│ │ └── SearchResult.cs
│ ├── ExtensionMethods.Exceptions.cs
│ ├── ExtensionMethods.GetSet.cs
│ ├── ExtensionMethods.Sequences.cs
│ ├── ExtensionMethods.Sql.cs
│ ├── ExtensionMethods.Time.cs
│ ├── ExtensionMethods.cs
│ ├── GlobalSuppressions.cs
│ ├── Helpers
│ │ ├── AddressCache.cs
│ │ ├── Connection.cs
│ │ ├── CryptoRandom.cs
│ │ ├── FileSizeFormatProvider.cs
│ │ ├── OpserverConfigException.cs
│ │ ├── PerfCounters.cs
│ │ ├── Singleton.cs
│ │ ├── SortDir.cs
│ │ ├── ThreadStats.cs
│ │ ├── WindowsKernelVersions.cs
│ │ └── Wmi.cs
│ ├── IMinVersioned.cs
│ ├── IOverallStatusCount.cs
│ ├── ISeachableNode.cs
│ ├── Models
│ │ └── Roles.cs
│ ├── Opserver.Core.csproj
│ ├── ServiceExtensions.cs
│ ├── Settings
│ │ ├── CloudFlareSettings.cs
│ │ ├── DashboardSettings.Bosun.cs
│ │ ├── DashboardSettings.Orion.cs
│ │ ├── DashboardSettings.Providers.cs
│ │ ├── DashboardSettings.SignalFx.cs
│ │ ├── DashboardSettings.WMI.cs
│ │ ├── DashboardSettings.cs
│ │ ├── ElasticSettings.cs
│ │ ├── ExceptionSettings.cs
│ │ ├── GlobalSettings.cs
│ │ ├── HAProxySettings.cs
│ │ ├── Interfaces.cs
│ │ ├── JiraSettings.cs
│ │ ├── ModuleSettings.cs
│ │ ├── OpserverSettings.cs
│ │ ├── PagerDutySettings.cs
│ │ ├── RedisSettings.cs
│ │ └── SQLSettings.cs
│ ├── StatusModule.cs
│ ├── StringBuilderCache.cs
│ ├── StringSplits.cs
│ └── app.config
├── Opserver.Web
│ ├── ContentSource
│ │ ├── bootstrap
│ │ │ ├── js
│ │ │ │ └── bootstrap.min.js
│ │ │ └── less
│ │ │ │ ├── .csscomb.json
│ │ │ │ ├── .csslintrc
│ │ │ │ ├── alerts.less
│ │ │ │ ├── badges.less
│ │ │ │ ├── bootstrap.less
│ │ │ │ ├── breadcrumbs.less
│ │ │ │ ├── button-groups.less
│ │ │ │ ├── buttons.less
│ │ │ │ ├── carousel.less
│ │ │ │ ├── close.less
│ │ │ │ ├── code.less
│ │ │ │ ├── component-animations.less
│ │ │ │ ├── dropdowns.less
│ │ │ │ ├── forms.less
│ │ │ │ ├── grid.less
│ │ │ │ ├── input-groups.less
│ │ │ │ ├── jumbotron.less
│ │ │ │ ├── labels.less
│ │ │ │ ├── list-group.less
│ │ │ │ ├── media.less
│ │ │ │ ├── mixins.less
│ │ │ │ ├── mixins
│ │ │ │ ├── alerts.less
│ │ │ │ ├── background-variant.less
│ │ │ │ ├── border-radius.less
│ │ │ │ ├── buttons.less
│ │ │ │ ├── center-block.less
│ │ │ │ ├── clearfix.less
│ │ │ │ ├── forms.less
│ │ │ │ ├── gradients.less
│ │ │ │ ├── grid-framework.less
│ │ │ │ ├── grid.less
│ │ │ │ ├── hide-text.less
│ │ │ │ ├── image.less
│ │ │ │ ├── labels.less
│ │ │ │ ├── list-group.less
│ │ │ │ ├── nav-divider.less
│ │ │ │ ├── nav-vertical-align.less
│ │ │ │ ├── opacity.less
│ │ │ │ ├── pagination.less
│ │ │ │ ├── panels.less
│ │ │ │ ├── progress-bar.less
│ │ │ │ ├── reset-filter.less
│ │ │ │ ├── reset-text.less
│ │ │ │ ├── resize.less
│ │ │ │ ├── responsive-visibility.less
│ │ │ │ ├── size.less
│ │ │ │ ├── tab-focus.less
│ │ │ │ ├── table-row.less
│ │ │ │ ├── text-emphasis.less
│ │ │ │ ├── text-overflow.less
│ │ │ │ └── vendor-prefixes.less
│ │ │ │ ├── modals.less
│ │ │ │ ├── navbar.less
│ │ │ │ ├── navs.less
│ │ │ │ ├── normalize.less
│ │ │ │ ├── pager.less
│ │ │ │ ├── pagination.less
│ │ │ │ ├── panels.less
│ │ │ │ ├── popovers.less
│ │ │ │ ├── print.less
│ │ │ │ ├── progress-bars.less
│ │ │ │ ├── responsive-embed.less
│ │ │ │ ├── responsive-utilities.less
│ │ │ │ ├── scaffolding.less
│ │ │ │ ├── tables.less
│ │ │ │ ├── theme.less
│ │ │ │ ├── thumbnails.less
│ │ │ │ ├── tooltip.less
│ │ │ │ ├── type.less
│ │ │ │ ├── utilities.less
│ │ │ │ ├── variables.less
│ │ │ │ └── wells.less
│ │ ├── font-awesome
│ │ │ └── less
│ │ │ │ ├── animated.less
│ │ │ │ ├── bordered-pulled.less
│ │ │ │ ├── core.less
│ │ │ │ ├── fixed-width.less
│ │ │ │ ├── font-awesome.less
│ │ │ │ ├── icons.less
│ │ │ │ ├── larger.less
│ │ │ │ ├── list.less
│ │ │ │ ├── mixins.less
│ │ │ │ ├── path.less
│ │ │ │ ├── rotated-flipped.less
│ │ │ │ ├── screen-reader.less
│ │ │ │ ├── stacked.less
│ │ │ │ └── variables.less
│ │ ├── js
│ │ │ ├── Scripts.js
│ │ │ └── plugins
│ │ │ │ ├── bootbox.js
│ │ │ │ ├── colorbrewer.js
│ │ │ │ ├── d3.v3.js
│ │ │ │ ├── highlight.pack.js
│ │ │ │ ├── jquery-ui.min.js
│ │ │ │ ├── qp.js
│ │ │ │ ├── tablesorter.js
│ │ │ │ ├── toastr.js
│ │ │ │ └── visibility.js
│ │ └── themes
│ │ │ ├── _shared.less
│ │ │ ├── _shared
│ │ │ ├── _bootswatch.less
│ │ │ ├── _variables.less
│ │ │ ├── animations.less
│ │ │ ├── charts.less
│ │ │ ├── exceptions.less
│ │ │ ├── highlightjs.less
│ │ │ ├── spinkit.less
│ │ │ ├── sql.less
│ │ │ └── toastr.less
│ │ │ ├── dark.less
│ │ │ └── light.less
│ ├── Controllers
│ │ ├── AdminController.cs
│ │ ├── ApiController.cs
│ │ ├── AuthController.OIDC.cs
│ │ ├── AuthController.cs
│ │ ├── CloudflareController.cs
│ │ ├── DashboardController.Admin.cs
│ │ ├── DashboardController.Polling.cs
│ │ ├── DashboardController.cs
│ │ ├── DataController.cs
│ │ ├── DefaultRoute.cs
│ │ ├── ElasticController.cs
│ │ ├── ExceptionsController.cs
│ │ ├── GraphController.Json.cs
│ │ ├── GraphController.Spark.cs
│ │ ├── GraphController.cs
│ │ ├── HAProxyController.Admin.cs
│ │ ├── HAProxyController.cs
│ │ ├── HomeController.cs
│ │ ├── HubController.cs
│ │ ├── MiscController.cs
│ │ ├── PagerDutyController.Admin.cs
│ │ ├── PagerDutyController.cs
│ │ ├── PollController.cs
│ │ ├── RedisController.Admin.cs
│ │ ├── RedisController.cs
│ │ ├── SQLController.Admin.cs
│ │ ├── SQLController.cs
│ │ └── StatusController.cs
│ ├── Current.cs
│ ├── ExtensionMethods.cs
│ ├── Help
│ │ └── SQL
│ │ │ └── WaitStats.md
│ ├── Helpers
│ │ ├── DataKeys.cs
│ │ ├── ExtensionMethods.cs
│ │ ├── Health.cs
│ │ ├── Icon.cs
│ │ ├── MiniProfilerCacheStorage.cs
│ │ ├── NavTab.cs
│ │ ├── OnlyAllowAttribute.cs
│ │ ├── Poll.cs
│ │ ├── PrefixedJsonConfiguration.cs
│ │ ├── Status.cs
│ │ ├── Tag
│ │ │ └── PollTagHelper.cs
│ │ ├── TestItem.cs
│ │ └── Theme.cs
│ ├── Models
│ │ └── User.cs
│ ├── Opserver.Web.csproj
│ ├── Program.cs
│ ├── Properties
│ │ └── launchSettings.json
│ ├── Security
│ │ ├── ActiveDirectoryProvider.cs
│ │ ├── ActiveDirectorySecuritySettings.cs
│ │ ├── EveryonesAnAdminProvider.cs
│ │ ├── EveryonesReadOnlyProvider.cs
│ │ ├── ISecurityProviderToken.cs
│ │ ├── OIDCProvider.cs
│ │ ├── OIDCSecuritySettings.cs
│ │ ├── OIDCToken.cs
│ │ ├── SecurityManager.cs
│ │ ├── SecurityProvider.cs
│ │ ├── SecurityProviderFlowType.cs
│ │ ├── SecuritySettings.cs
│ │ ├── UnconfiguredProvider.cs
│ │ └── UserNamePasswordToken.cs
│ ├── Startup.cs
│ ├── Views
│ │ ├── Auth
│ │ │ ├── Login.Model.cs
│ │ │ └── Login.cshtml
│ │ ├── CloudFlare
│ │ │ ├── DNS.Model.cs
│ │ │ ├── DNS.cshtml
│ │ │ ├── Dashboard.Model.cs
│ │ │ ├── Dashboard.cshtml
│ │ │ ├── _ViewImports.cshtml
│ │ │ └── _ViewStart.cshtml
│ │ ├── Dashboard
│ │ │ ├── CurrentStatusTypes.cs
│ │ │ ├── Dashboard.Model.cs
│ │ │ ├── Dashboard.Table.cshtml
│ │ │ ├── Dashboard.cshtml
│ │ │ ├── Node.Graph.Model.cs
│ │ │ ├── Node.Graph.cshtml
│ │ │ ├── Node.Hardware.cshtml
│ │ │ ├── Node.Interfaces.cshtml
│ │ │ ├── Node.Model.cs
│ │ │ ├── Node.Network.cshtml
│ │ │ ├── Node.Services.cshtml
│ │ │ ├── Node.Stats.cshtml
│ │ │ ├── Node.VMHost.cshtml
│ │ │ ├── Node.Volumes.cshtml
│ │ │ ├── Node.cshtml
│ │ │ └── _ViewImports.cshtml
│ │ ├── Elastic
│ │ │ ├── AllClusters.cshtml
│ │ │ ├── Cluster.Indexes.cshtml
│ │ │ ├── Cluster.Panel.Indexes.cshtml
│ │ │ ├── Cluster.Panel.Stats.cshtml
│ │ │ ├── Cluster.Selector.cshtml
│ │ │ ├── Cluster.Shards.cshtml
│ │ │ ├── Cluster.cshtml
│ │ │ ├── Dashboard.Model.cs
│ │ │ ├── Dashboard.cshtml
│ │ │ ├── Indexes.Table.cshtml
│ │ │ ├── Indexes.cshtml
│ │ │ ├── Node.Selector.cshtml
│ │ │ ├── Node.Settings.cshtml
│ │ │ ├── Node.cshtml
│ │ │ ├── Shards.Table.cshtml
│ │ │ ├── _ViewImports.cshtml
│ │ │ └── _ViewStart.cshtml
│ │ ├── Exceptions
│ │ │ ├── Exceptions.Detail.cshtml
│ │ │ ├── Exceptions.Filters.cshtml
│ │ │ ├── Exceptions.Jira.cshtml
│ │ │ ├── Exceptions.Model.cs
│ │ │ ├── Exceptions.Navigation.cshtml
│ │ │ ├── Exceptions.Preview.cshtml
│ │ │ ├── Exceptions.Table.Rows.cshtml
│ │ │ ├── Exceptions.Table.cshtml
│ │ │ ├── Exceptions.cshtml
│ │ │ └── _ViewImports.cshtml
│ │ ├── HAProxy
│ │ │ ├── HAProxy.Dashboard.cshtml
│ │ │ ├── HAProxy.Model.cs
│ │ │ ├── HAProxy.cshtml
│ │ │ └── _ViewImports.cshtml
│ │ ├── Home
│ │ │ ├── About.Caches.cshtml
│ │ │ ├── About.Model.cs
│ │ │ ├── About.cshtml
│ │ │ └── Test.cshtml
│ │ ├── Hub
│ │ │ ├── Dashboard.Node.cshtml
│ │ │ ├── HubModel.cs
│ │ │ ├── Index.Dashboard.cshtml
│ │ │ ├── Index.Elastic.cshtml
│ │ │ ├── Index.HAProxy.cshtml
│ │ │ ├── Index.Issues.cshtml
│ │ │ ├── Index.SQL.cshtml
│ │ │ ├── Index.cshtml
│ │ │ └── SQL.AvailabilityGroup.cshtml
│ │ ├── PagerDuty
│ │ │ ├── PagerDuty.EscFull.cshtml
│ │ │ ├── PagerDuty.Incident.Model.cs
│ │ │ ├── PagerDuty.Incident.cshtml
│ │ │ ├── PagerDuty.Model.cs
│ │ │ ├── PagerDuty.OnCallRow.cshtml
│ │ │ ├── PagerDuty.cshtml
│ │ │ └── _ViewImports.cshtml
│ │ ├── Redis
│ │ │ ├── Dashboard.Instances.cshtml
│ │ │ ├── Dashboard.Model.cs
│ │ │ ├── Dashboard.cshtml
│ │ │ ├── Instance.Actions.cshtml
│ │ │ ├── Instance.Analysis.Memory.cshtml
│ │ │ ├── Instance.Clients.cshtml
│ │ │ ├── Instance.Config.cshtml
│ │ │ ├── Instance.Info.cshtml
│ │ │ ├── Instance.Selector.cshtml
│ │ │ ├── Instance.SlowLog.cshtml
│ │ │ ├── Instance.cshtml
│ │ │ ├── Server.Actions.Preview.cshtml
│ │ │ ├── Server.Actions.cshtml
│ │ │ ├── _ViewImports.cshtml
│ │ │ └── _ViewStart.cshtml
│ │ ├── SQL
│ │ │ ├── AllJobs.cshtml
│ │ │ ├── Connections.cshtml
│ │ │ ├── Dashboard.Model.cs
│ │ │ ├── Dashboard.cshtml
│ │ │ ├── Databases.Modal.Backups.cshtml
│ │ │ ├── Databases.Modal.Columns.cshtml
│ │ │ ├── Databases.Modal.MissingIndexes.cshtml
│ │ │ ├── Databases.Modal.Partitions.cshtml
│ │ │ ├── Databases.Modal.Restores.cshtml
│ │ │ ├── Databases.Modal.Storage.cshtml
│ │ │ ├── Databases.Modal.StoredProcedures.cshtml
│ │ │ ├── Databases.Modal.Tables.Indexes.cshtml
│ │ │ ├── Databases.Modal.Tables.cshtml
│ │ │ ├── Databases.Modal.UnusedIndexes.cshtml
│ │ │ ├── Databases.Modal.Views.cshtml
│ │ │ ├── Databases.Modal.cshtml
│ │ │ ├── Databases.Model.cs
│ │ │ ├── Databases.cshtml
│ │ │ ├── Instance.Configuration.cshtml
│ │ │ ├── Instance.Connections.cshtml
│ │ │ ├── Instance.DBFiles.cshtml
│ │ │ ├── Instance.Errors.cshtml
│ │ │ ├── Instance.Jobs.cshtml
│ │ │ ├── Instance.Memory.cshtml
│ │ │ ├── Instance.Model.cs
│ │ │ ├── Instance.Selector.cshtml
│ │ │ ├── Instance.cshtml
│ │ │ ├── Operations.Active.Filters.cshtml
│ │ │ ├── Operations.Active.Model.cs
│ │ │ ├── Operations.Active.cshtml
│ │ │ ├── Operations.Top.Detail.cshtml
│ │ │ ├── Operations.Top.Filters.cshtml
│ │ │ ├── Operations.Top.Model.cs
│ │ │ ├── Operations.Top.cshtml
│ │ │ ├── Ops.Top.Detail.Model.cs
│ │ │ ├── Servers.AvailabilityGroup.cshtml
│ │ │ ├── Servers.ClusterDetail.cshtml
│ │ │ ├── Servers.Model.cs
│ │ │ ├── Servers.cshtml
│ │ │ ├── _ViewImports.cshtml
│ │ │ └── _ViewStart.cshtml
│ │ ├── Shared
│ │ │ ├── AccessDenied.cshtml
│ │ │ ├── Error.cshtml
│ │ │ ├── Gauges
│ │ │ │ ├── Circle.Model.cs
│ │ │ │ └── Circle.cshtml
│ │ │ ├── Issues.cshtml
│ │ │ ├── IssuesButton.cshtml
│ │ │ ├── Master.cshtml
│ │ │ ├── NoConfiguration.cshtml
│ │ │ ├── PageNotFound.Model.cs
│ │ │ ├── PageNotFound.cshtml
│ │ │ ├── Partials.MemoryCell.Model.cs
│ │ │ ├── Partials.MemoryCell.cshtml
│ │ │ ├── PollInfo.Model.cs
│ │ │ ├── PollInfo.cshtml
│ │ │ ├── TopBoxOptions.cs
│ │ │ ├── TopRefresh.Model.cs
│ │ │ ├── TopRefresh.cshtml
│ │ │ └── TopTabs.cshtml
│ │ ├── _ViewImports.cshtml
│ │ └── _ViewStart.cshtml
│ ├── bundleconfig.json
│ ├── compilerconfig.json
│ ├── compilerconfig.json.defaults
│ ├── favicon.ico
│ ├── opserverSettings.json
│ └── wwwroot
│ │ ├── Content
│ │ ├── font-awesome
│ │ │ └── fonts
│ │ │ │ ├── FontAwesome.otf
│ │ │ │ ├── fontawesome-webfont.eot
│ │ │ │ ├── fontawesome-webfont.svg
│ │ │ │ ├── fontawesome-webfont.ttf
│ │ │ │ ├── fontawesome-webfont.woff
│ │ │ │ └── fontawesome-webfont.woff2
│ │ ├── img
│ │ │ ├── apple-touch-icon.png
│ │ │ ├── logo
│ │ │ │ ├── full-dark.svg
│ │ │ │ ├── full.svg
│ │ │ │ ├── icon-128.png
│ │ │ │ ├── icon-16.png
│ │ │ │ ├── icon-256.png
│ │ │ │ ├── icon-32.png
│ │ │ │ ├── icon.svg
│ │ │ │ └── logo.eps
│ │ │ └── query-plan
│ │ │ │ ├── arithmetic_expression.gif
│ │ │ │ ├── assert.gif
│ │ │ │ ├── assign.gif
│ │ │ │ ├── bitmap.gif
│ │ │ │ ├── bookmark_lookup.gif
│ │ │ │ ├── clustered_index_delete.gif
│ │ │ │ ├── clustered_index_insert.gif
│ │ │ │ ├── clustered_index_scan.gif
│ │ │ │ ├── clustered_index_seek.gif
│ │ │ │ ├── clustered_index_update.gif
│ │ │ │ ├── collapse.gif
│ │ │ │ ├── compute_scalar.gif
│ │ │ │ ├── concatenation.gif
│ │ │ │ ├── constant_scan.gif
│ │ │ │ ├── convert.gif
│ │ │ │ ├── declare.gif
│ │ │ │ ├── deleted_scan.gif
│ │ │ │ ├── distribute_streams.gif
│ │ │ │ ├── dynamic.gif
│ │ │ │ ├── fetch_query.gif
│ │ │ │ ├── filter.gif
│ │ │ │ ├── gather_streams.gif
│ │ │ │ ├── hash_match.gif
│ │ │ │ ├── icons.png
│ │ │ │ ├── if.gif
│ │ │ │ ├── inserted_scan.gif
│ │ │ │ ├── intrinsic.gif
│ │ │ │ ├── keyset.gif
│ │ │ │ ├── log_row_scan.gif
│ │ │ │ ├── merge_interval.gif
│ │ │ │ ├── merge_join.gif
│ │ │ │ ├── nested_loops.gif
│ │ │ │ ├── nonclustered_index_delete.gif
│ │ │ │ ├── nonclustered_index_insert.gif
│ │ │ │ ├── nonclustered_index_scan.gif
│ │ │ │ ├── nonclustered_index_seek.gif
│ │ │ │ ├── nonclustered_index_spool.gif
│ │ │ │ ├── nonclustered_index_update.gif
│ │ │ │ ├── online_index_insert.gif
│ │ │ │ ├── parameter_table_scan.gif
│ │ │ │ ├── population_query.gif
│ │ │ │ ├── rdi_lookup.gif
│ │ │ │ ├── refresh_query.gif
│ │ │ │ ├── remote_delete.gif
│ │ │ │ ├── remote_insert.gif
│ │ │ │ ├── remote_query.gif
│ │ │ │ ├── remote_scan.gif
│ │ │ │ ├── remote_update.gif
│ │ │ │ ├── repartition_streams.gif
│ │ │ │ ├── result.gif
│ │ │ │ ├── row_count_spool.gif
│ │ │ │ ├── segment.gif
│ │ │ │ ├── sequence.gif
│ │ │ │ ├── sequenceproject.gif
│ │ │ │ ├── snapshot.gif
│ │ │ │ ├── sort.gif
│ │ │ │ ├── split.gif
│ │ │ │ ├── spool.gif
│ │ │ │ ├── stream_aggregate.gif
│ │ │ │ ├── switch.gif
│ │ │ │ ├── table_delete.gif
│ │ │ │ ├── table_insert.gif
│ │ │ │ ├── table_scan.gif
│ │ │ │ ├── table_spool.gif
│ │ │ │ ├── table_update.gif
│ │ │ │ ├── table_valued_function.gif
│ │ │ │ ├── top.gif
│ │ │ │ ├── udx.gif
│ │ │ │ └── while.gif
│ │ ├── js
│ │ │ └── jquery-2.2.4.min.js
│ │ └── themes
│ │ │ ├── dark.css
│ │ │ ├── dark.min.css
│ │ │ ├── light.css
│ │ │ └── light.min.css
│ │ └── favicon.ico
└── Opserver.ruleset
└── tests
├── Directory.Build.props
├── Opserver.Tests
├── HAProxyTests.cs
├── IPNetTests.cs
├── Opserver.Tests.csproj
└── SecurityProviderTests.cs
└── docker-compose.yml
/.dockerignore:
--------------------------------------------------------------------------------
1 | [Oo]bj/
2 | [Bb]in/
3 | .vs/
4 | TestResults/
5 | artifacts/
6 | _ReSharper.*/
7 | _ReSharper.*
8 | *.user
9 | *.suo
10 | *.cache
11 | *.psess
12 | *.vsp
13 | *.pidb
14 | *.userprefs
15 | *DS_Store
--------------------------------------------------------------------------------
/.filenesting.json:
--------------------------------------------------------------------------------
1 | {
2 | "help": "https://go.microsoft.com/fwlink/?linkid=866610",
3 | "root": true,
4 |
5 | "dependentFileProviders": {
6 | "add": {
7 | "pathSegment": {
8 | "add": {
9 | "ExtensionMethods.*": [ ".cs" ],
10 | "Models\\*.*": [ "*.cs" ]
11 | }
12 | },
13 | "fileSuffixToExtension": {
14 | "add": {
15 | ".Model.cs": [ ".cshtml" ],
16 | ".json.defaults": [ ".json" ],
17 | ".min.css": [ ".css" ],
18 | ".min.js": [ ".js" ],
19 | ".min.js.map": [ ".min.js" ]
20 | }
21 | }
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 | *.sln merge=union
7 | *.csproj merge=union
8 | *.vbproj merge=union
9 | *.fsproj merge=union
10 | *.dbproj merge=union
11 |
12 | # Standard to msysgit
13 | *.doc diff=astextplain
14 | *.DOC diff=astextplain
15 | *.docx diff=astextplain
16 | *.DOCX diff=astextplain
17 | *.dot diff=astextplain
18 | *.DOT diff=astextplain
19 | *.pdf diff=astextplain
20 | *.PDF diff=astextplain
21 | *.rtf diff=astextplain
22 | *.RTF diff=astextplain
23 |
--------------------------------------------------------------------------------
/.github/workflows/docker.yml:
--------------------------------------------------------------------------------
1 | name: Docker Image CI
2 |
3 | on:
4 | push:
5 | paths:
6 | - 'Dockerfile'
7 |
8 | jobs:
9 | build:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v3
13 | - name: Build Opserver Docker Image
14 | run: |
15 | docker build --tag opserver/opserver-ci:latest --target web .
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: CI Build
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 | runs-on: windows-2019
8 | steps:
9 | - name: Setup .NET
10 | uses: actions/setup-dotnet@v1
11 | with:
12 | dotnet-version: 8.0.x
13 | - uses: actions/checkout@v3
14 | - name: Build with dotnet
15 | run: dotnet build
16 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
2 | WORKDIR /app
3 |
4 | # Global
5 | COPY ./*.sln ./nuget.config ./
6 | # Apps
7 | COPY src/*/*.csproj ./
8 | RUN for file in $(ls *.csproj); do mkdir -p src/${file%.*}/ && mv $file src/${file%.*}/; done
9 | # Tests
10 | COPY tests/*/*.csproj ./
11 | RUN for file in $(ls *.csproj); do mkdir -p tests/${file%.*}/ && mv $file tests/${file%.*}/; done
12 |
13 | # Copy all
14 | COPY . .
15 |
16 | # FROM build AS test-runner
17 | # WORKDIR /app/tests/Opserver.Tests
18 | # ENTRYPOINT dotnet test --results-directory /app/artifacts --logger:trx
19 |
20 | FROM build AS web-publish
21 | WORKDIR /app/src/Opserver.Web
22 | RUN dotnet publish -c Release -o publish
23 |
24 | # Build runtime image
25 | FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS web
26 | WORKDIR /app
27 | COPY --from=web-publish /app/src/Opserver.Web/publish ./
28 | ENTRYPOINT ["dotnet", "Opserver.Web.dll"]
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013 Stack Exchange Inc.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
--------------------------------------------------------------------------------
/Opserver.sln.DotSettings:
--------------------------------------------------------------------------------
1 |
2 | CPU
3 | OS
4 | VM
5 | True
6 | True
7 | True
8 | True
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | image: Visual Studio 2019
2 |
3 | install:
4 | - choco install dotnet-sdk --version 8.0.100
5 |
6 | skip_branch_with_pr: true
7 | skip_tags: true
8 | skip_commits:
9 | files:
10 | - '**/*.md'
11 | - docs/*
12 |
13 | environment:
14 | Appveyor: true
15 | DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
16 |
17 | build_script:
18 | - cmd: dotnet build
19 |
20 | test: off
--------------------------------------------------------------------------------
/docs/Docs.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net6.0
4 |
5 |
--------------------------------------------------------------------------------
/docs/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 | gem 'github-pages', group: :jekyll_plugins
--------------------------------------------------------------------------------
/docs/HowTo/UpgradeFromV1.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: "default"
3 | ---
4 | ### How-To Upgrade From Opserver V1 to V2
5 |
6 | Opserver v2 is a migration from .NET Full Framework (requiring IIS) to .NET Core. This means it can run in a variety of ways:
7 |
8 | - Under IIS (you'll need the ASP.NET Core hosting module - [here's how that works'](https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/iis/?view=aspnetcore-3.1))
9 | - Directly as an executable (using the Kestrel web server)
10 | - Using a container (these aren't published yet, but we'll get there)
11 |
12 | It can run on Windows, macOS, or Linux.
13 |
14 | Legacy configuration under `src\Opserver.Web\Config` is still supported with the exception of `securitySettings.config`.
15 | Everything is now JSON (though it may support more options in the future), and there's no XML/JSON mix.
16 |
17 | See [the confgiuration documentation](../Configuration) for examples of all configs.
--------------------------------------------------------------------------------
/docs/Releases.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Release Notes"
3 | layout: "default"
4 | ---
5 | ### Release Notes
6 | This page tracks major changes included in any update starting with version 2.0
7 |
8 | #### Version 2.0.0 (In preview)
9 | - **New**:
10 | - Runs on .NET Core (previous .NET Framework)
11 | - Now supports Windows, Linux, and MacOS
12 | - Windows: Doesn't require IIS - can spin up via `dotnet run`, or via a publish and copy
13 | - Supports running in a container (via Docker)
14 | - Adds SignalFX as a dashboard provider
15 | - Adds OpenId Connect as a login provider
16 | - Overhauled configuration system (though most old formats are supported for a smooth transition)
17 |
--------------------------------------------------------------------------------
/docs/_config.yml:
--------------------------------------------------------------------------------
1 | title: 'Opserver'
2 | subtitle: 'Documentation'
3 | codeurl: 'https://github.com/Opserver/Opserver'
4 | baseurl: '/Opserver'
5 | highlighter: rouge
6 | exclude: [README.md']
7 | markdown: kramdown
8 | redcarpet:
9 | extensions: [
10 | 'no_intra_emphasis',
11 | 'fenced_code_blocks',
12 | 'autolink',
13 | 'strikethrough',
14 | 'superscript',
15 | 'with_toc_data',
16 | 'tables',
17 | 'hardwrap'
18 | ]
--------------------------------------------------------------------------------
/docs/_includes/navigation.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/nuget.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | ## Opserver
2 | [](https://ci.appveyor.com/project/StackExchange/opserver/branch/main)
3 | [](https://www.codetriage.com/opserver/opserver)
4 |
5 | Opserver is a monitoring system originally from the team at [Stack Exchange](https://stackexchange.com), home of [Stack Overflow](https://stackoverflow.com).
6 |
7 | [Please see the documentation site here](https://opserver.github.io/Opserver/).
8 |
9 | ### License
10 | Opserver is licensed under the [MIT License](https://opensource.org/licenses/MIT).
11 |
--------------------------------------------------------------------------------
/src/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | 8.0
4 | true
5 | $(MSBuildThisFileDirectory)Opserver.ruleset
6 |
7 | $(AssemblyName)
8 | https://github.com/Opserver/Opserver/
9 | https://raw.github.com/Opserver/Opserver/master/LICENSE
10 |
11 | git
12 | https://github.com/Opserver/Opserver/
13 |
14 | true
15 | embedded
16 | en-US
17 | false
18 | true
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/CloudFlare/CloudFlareAPI.Actions.cs:
--------------------------------------------------------------------------------
1 | using System.Text.RegularExpressions;
2 | using System.Threading.Tasks;
3 |
4 | namespace Opserver.Data.Cloudflare
5 | {
6 | public partial class CloudflareAPI
7 | {
8 | public Task PurgeFileAsync(string url)
9 | {
10 | var zone = GetZoneFromUrl(url);
11 | if (zone == null) return Task.FromResult(false);
12 |
13 | var otherUrl = url.StartsWith("http:")
14 | ? Regex.Replace(url, "^http:", "https:")
15 | : Regex.Replace(url, "^https:", "http:");
16 |
17 | return Module.PurgeFilesAsync(zone, new[] {url, otherUrl});
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/Cloudflare/CloudflareRoles.cs:
--------------------------------------------------------------------------------
1 | namespace Opserver.Data.Cloudflare
2 | {
3 | public static class CloudflareRoles
4 | {
5 | public const string Admin = nameof(CloudflareModule) + ":" + nameof(Admin);
6 | public const string Viewer = nameof(CloudflareModule) + ":" + nameof(Viewer);
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/Dashboard/Application.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Opserver.Data.Dashboard
4 | {
5 | public class Application
6 | {
7 | public Node Node { get; set; }
8 |
9 | public string Id { get; internal set; }
10 | public string NodeId { get; internal set; }
11 | public string NiceName { get; internal set; }
12 | public string AppName { get; internal set; }
13 | public string ComponentName { get; internal set; }
14 | public DateTime LastUpdated { get; internal set; }
15 | public bool IsUnwatched { get; internal set; }
16 |
17 | public int? ProcessID { get; internal set; }
18 | public string ProcessName { get; internal set; }
19 | public DateTime? LastTimeUp { get; internal set; }
20 |
21 | public double? CurrentPercentCPU { get; internal set; }
22 | public double? CurrentPercentMemory { get; internal set; }
23 | public long? CurrentMemoryUsed { get; internal set; }
24 | public long? CurrentVirtualMemoryUsed { get; internal set; }
25 |
26 | public decimal? PercentCPU { get; internal set; }
27 | public decimal? PercentMemory { get; internal set; }
28 | public long? MemoryUsed { get; internal set; }
29 | public long? VirtualMemoryUsed { get; internal set; }
30 | public string ErrorMessage { get; internal set; }
31 |
32 | public bool IsRunning { get; internal set; }
33 | }
34 | }
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/Dashboard/DashboardRoles.cs:
--------------------------------------------------------------------------------
1 | namespace Opserver.Data.Dashboard
2 | {
3 | public static class DashboardRoles
4 | {
5 | public const string Admin = nameof(DashboardModule) + ":" + nameof(Admin);
6 | public const string Viewer = nameof(DashboardModule) + ":" + nameof(Viewer);
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/Dashboard/GraphPoint.cs:
--------------------------------------------------------------------------------
1 | namespace Opserver.Data.Dashboard
2 | {
3 | public interface IGraphPoint
4 | {
5 | long DateEpoch { get; }
6 | }
7 |
8 | public class GraphPoint : IGraphPoint
9 | {
10 | ///
11 | /// Date represented as a unix epoch
12 | ///
13 | public virtual long DateEpoch { get; set; }
14 | ///
15 | /// Value of the top (or only) point
16 | ///
17 | public virtual double? Value { get; set; }
18 | }
19 |
20 | public class DoubleGraphPoint : GraphPoint
21 | {
22 | ///
23 | /// Value of the bottom (or second) point
24 | ///
25 | public virtual double? BottomValue { get; set; }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/Dashboard/HardwareType.cs:
--------------------------------------------------------------------------------
1 | namespace Opserver.Data.Dashboard
2 | {
3 | public enum HardwareType
4 | {
5 | Unknown = 0,
6 | Physical = 1,
7 | VirtualMachine = 2,
8 | Network = 3
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/Dashboard/IServiceControlProvider.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 |
3 | namespace Opserver.Data.Dashboard
4 | {
5 | public interface IServiceControlProvider
6 | {
7 | Task UpdateServiceAsync(Node node, string serviceName, NodeService.Action action);
8 | }
9 |
10 | public static class ServiceControlProviderExtensions
11 | {
12 | public static bool HasServiceControl(this Node node) => node?.DataProvider is IServiceControlProvider;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/Dashboard/Node.Polling.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using Opserver.Helpers;
4 |
5 | namespace Opserver.Data.Dashboard
6 | {
7 | public partial class Node
8 | {
9 | public async Task> GetCPUUtilization()
10 | {
11 | if (OperatingSystem.IsWindows())
12 | {
13 | return await PerfCounters.Windows.GetCPUUtilization(Ip);
14 | }
15 | return new PerfCounters.QueryResult
16 | {
17 | Duration = TimeSpan.Zero
18 | };
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/Elastic/ElasticCluster.Actions.cs:
--------------------------------------------------------------------------------
1 | namespace Opserver.Data.Elastic
2 | {
3 | public partial class ElasticCluster
4 | {
5 | public bool AllocateShard() //string index, int shard, string node)
6 | {
7 | // TODO: Implementation
8 | // if node is null - take a stab at a random one!
9 | // for real though, algorithms (space probably?)
10 | //return PostAction("/_cluster/reroute", GetRerouteCommand(new { allocate = new { index, shard, node } }));
11 | return false;
12 | }
13 |
14 | public bool MoveShard() //string index, int shard, string fromNode, string toNode)
15 | {
16 | // TODO: Implementation
17 | return false;
18 | //return PostAction("/_clusterreroute", GetRerouteCommand(new { move = new {index, shard, from_node = fromNode, to_node = toNode} }));
19 | }
20 |
21 | public bool CancelInitialization() //string index, int shard, string node)
22 | {
23 | // TODO: Implementation
24 | return false;
25 | //return PostAction("/_cluster/reroute", GetRerouteCommand(new { cancel = new {index, shard, node} }));
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/Elastic/ElasticCluster.Issues.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Opserver.Data.Elastic
4 | {
5 | public partial class ElasticCluster : IIssuesProvider
6 | {
7 | string IIssuesProvider.Name => "Elastic";
8 |
9 | public IEnumerable GetIssues()
10 | {
11 | if (MonitorStatus != MonitorStatus.Good && LastPoll.HasValue)
12 | {
13 | yield return new Issue(this, "Elastic", Name) { IsCluster = true };
14 | }
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/Elastic/ElasticModule.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Microsoft.Extensions.Configuration;
5 |
6 | namespace Opserver.Data.Elastic
7 | {
8 | public class ElasticModule : StatusModule
9 | {
10 | public override string Name => "Elastic";
11 | public override bool Enabled => Clusters.Count > 0;
12 |
13 | public List Clusters { get; }
14 |
15 | public ElasticModule(IConfiguration config, PollingService poller) : base(config, poller)
16 | {
17 | Clusters = Settings.Clusters
18 | .Select(c => new ElasticCluster(this, c))
19 | .Where(i => i.TryAddToGlobalPollers())
20 | .ToList();
21 | }
22 |
23 | public override MonitorStatus MonitorStatus => Clusters.GetWorstStatus();
24 | public override bool IsMember(string node)
25 | {
26 | return Clusters.Any(c => c.KnownNodes.Any(sn => string.Equals(sn.Host, node, StringComparison.InvariantCultureIgnoreCase)));
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/Elastic/ElasticRoles.cs:
--------------------------------------------------------------------------------
1 | namespace Opserver.Data.Elastic
2 | {
3 | public static class ElasticRoles
4 | {
5 | public const string Admin = nameof(ElasticModule) + ":" + nameof(Admin);
6 | public const string Viewer = nameof(ElasticModule) + ":" + nameof(Viewer);
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/Elastic/ShardStates.cs:
--------------------------------------------------------------------------------
1 | namespace Opserver.Data.Elastic
2 | {
3 | public static class ShardStates
4 | {
5 | public const string Unassigned = "UNASSIGNED";
6 | public const string Initializing = "INITIALIZING";
7 | public const string Started = "STARTED";
8 | public const string Relocating = "RELOCATING";
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/Exceptions/ExceptionSorts.cs:
--------------------------------------------------------------------------------
1 | namespace Opserver.Data.Exceptions
2 | {
3 | public enum ExceptionSorts
4 | {
5 | TimeDesc = 0,
6 | TimeAsc = 1,
7 | AppAsc = 2,
8 | AppDesc = 3,
9 | TypeAsc = 4,
10 | TypeDesc = 5,
11 | MessageAsc = 6,
12 | MessageDesc = 7,
13 | UrlAsc = 8,
14 | UrlDesc = 9,
15 | IPAddressAsc = 10,
16 | IPAddressDesc = 11,
17 | HostAsc = 12,
18 | HostDesc = 13,
19 | MachineNameAsc = 14,
20 | MachineNameDesc = 15,
21 | CountAsc = 16,
22 | CountDesc = 17
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/Exceptions/ExceptionsRoles.cs:
--------------------------------------------------------------------------------
1 | namespace Opserver.Data.Exceptions
2 | {
3 | public static class ExceptionsRoles
4 | {
5 | public const string Admin = nameof(ExceptionsModule) + ":" + nameof(Admin);
6 | public const string Viewer = nameof(ExceptionsModule) + ":" + nameof(Viewer);
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/GlobalPollingStatus.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Opserver.Data
5 | {
6 | public class GlobalPollingStatus : IMonitorStatus
7 | {
8 | public MonitorStatus MonitorStatus { get; internal set; }
9 | public string MonitorStatusReason { get; internal set; }
10 | public DateTime StartTime { get; internal set; }
11 | public DateTime? LastPollAll { get; internal set; }
12 | public bool IsAlive { get; internal set; }
13 | public long TotalPollIntervals { get; internal set; }
14 | public long ActivePolls { get; internal set; }
15 | public int NodeCount { get; internal set; }
16 | public int TotalPollers { get; internal set; }
17 | public List> NodeBreakdown { get; internal set; }
18 | public List Nodes { get; internal set; }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/HAProxy/Backend.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Opserver.Data.HAProxy
4 | {
5 | ///
6 | /// Represents an HAProxy backend for a proxy
7 | ///
8 | public class Backend : Item
9 | {
10 | public List Servers { get; internal set; }
11 | }
12 | }
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/HAProxy/Frontend.cs:
--------------------------------------------------------------------------------
1 | namespace Opserver.Data.HAProxy
2 | {
3 | ///
4 | /// Represents a HAProxy Frontend for a proxy
5 | ///
6 | public class Frontend : Item
7 | {
8 | }
9 | }
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/HAProxy/HAProxyGroup.Issues.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 |
4 | namespace Opserver.Data.HAProxy
5 | {
6 | public partial class HAProxyGroup : IIssuesProvider
7 | {
8 | string IIssuesProvider.Name => "HAProxy";
9 |
10 | public IEnumerable GetIssues()
11 | {
12 | if (MonitorStatus != MonitorStatus.Good && Instances.Any(i => i.LastPoll.HasValue))
13 | {
14 | yield return new Issue(this, "HAProxy", Name);
15 | }
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/HAProxy/HAProxyRoles.cs:
--------------------------------------------------------------------------------
1 | namespace Opserver.Data.HAProxy
2 | {
3 | public static class HAProxyRoles
4 | {
5 | public const string Admin = nameof(HAProxyModule) + ":" + nameof(Admin);
6 | public const string Viewer = nameof(HAProxyModule) + ":" + nameof(Viewer);
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/HAProxy/Server.cs:
--------------------------------------------------------------------------------
1 | namespace Opserver.Data.HAProxy
2 | {
3 | public class Server : Item
4 | {
5 | public string Name => ServerName;
6 | }
7 | }
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/HAProxy/Socket.cs:
--------------------------------------------------------------------------------
1 | namespace Opserver.Data.HAProxy
2 | {
3 | ///
4 | /// Represents a Socket interface in HAProxy (a Frontend/backend are a tied combination here)
5 | ///
6 | public class Socket : Item
7 | {
8 | }
9 | }
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/HAProxy/StatAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Opserver.Data.HAProxy
4 | {
5 | ///
6 | /// Represents a statistic from the proxy stat dump, since these are always added at the end in newer versions, they're parsed based on position.
7 | ///
8 | [AttributeUsage(AttributeTargets.Property)]
9 | public sealed class StatAttribute : Attribute
10 | {
11 | public int Position { get; set; }
12 | public string Name { get; set; }
13 |
14 | public StatAttribute(string name, int position)
15 | {
16 | Position = position;
17 | Name = name;
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/HAProxy/StatusType.cs:
--------------------------------------------------------------------------------
1 | namespace Opserver.Data.HAProxy
2 | {
3 | public enum StatusType
4 | {
5 | Frontend = 0,
6 | Backend = 1,
7 | Server = 2,
8 | Socket = 3
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/Issue.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Opserver.Data
5 | {
6 | public class Issue : Issue where T : IMonitorStatus
7 | {
8 | public T Item { get; set; }
9 |
10 | public Issue(T item, string type, string title, DateTime? date = null)
11 | {
12 | Type = type;
13 | Title = title;
14 | Date = date ?? DateTime.UtcNow;
15 | Item = item;
16 | MonitorStatus = item.MonitorStatus;
17 | Description = item.MonitorStatusReason;
18 | }
19 | }
20 |
21 | public class Issue : IMonitorStatus
22 | {
23 | public string Title { get; set; }
24 | public string Description { get; set; }
25 | public string Type { get; set; }
26 | ///
27 | /// Whether this issue is a service rather than a node - presumably an entire service being offline is worse
28 | ///
29 | public bool IsCluster { get; set; }
30 | public DateTime Date { get; set; }
31 | public MonitorStatus MonitorStatus { get; set; }
32 | public string MonitorStatusReason => Description;
33 | }
34 |
35 | public interface IIssuesProvider
36 | {
37 | string Name { get; }
38 | IEnumerable GetIssues();
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/MonitorStatus.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel;
2 |
3 | namespace Opserver.Data
4 | {
5 | public enum MonitorStatus
6 | {
7 | [Description("good")]
8 | Good = 0,
9 | [Description("unknown")]
10 | Unknown = 1,
11 | [Description("maintenance")]
12 | Maintenance = 2,
13 | [Description("warning")]
14 | Warning = 3,
15 | [Description("critical")]
16 | Critical = 4
17 | }
18 |
19 | public interface IMonitoredService : IMonitorStatus { }
20 |
21 | public interface IMonitorStatus
22 | {
23 | MonitorStatus MonitorStatus { get; }
24 | string MonitorStatusReason { get; }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/NodeRole.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Threading.Tasks;
3 |
4 | namespace Opserver.Data
5 | {
6 | public class NodeRole
7 | {
8 | public string Node { get; set; }
9 | public string Service { get; set; }
10 | public string Description { get; set; }
11 | public bool Active { get; set; }
12 | public int? SiblingsActive { get; set; }
13 | public int? SiblingsInactive { get; set; }
14 | }
15 |
16 | public interface INodeRoleProvider
17 | {
18 | IEnumerable GetRoles(string node);
19 | Task EnableAsync(string node);
20 | Task DisableAsync(string node);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/PagerDuty/IncidentStatus.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel;
2 |
3 | namespace Opserver.Data.PagerDuty
4 | {
5 | public enum IncidentStatus
6 | {
7 | // ReSharper disable InconsistentNaming
8 | [Description("Triggered")]
9 | triggered = 0,
10 | [Description("Acknowledged")]
11 | acknowledged = 1,
12 | [Description("Resolved")]
13 | resolved = 2
14 | // ReSharper restore InconsistentNaming
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/PagerDuty/PagerDutyModule.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Configuration;
2 |
3 | namespace Opserver.Data.PagerDuty
4 | {
5 | public class PagerDutyModule : StatusModule
6 | {
7 | public override string Name => "PagerDuty";
8 | public override bool Enabled => Settings.Enabled;
9 |
10 | public PagerDutyAPI API { get; }
11 |
12 | public PagerDutyModule(IConfiguration config, PollingService poller) : base(config, poller)
13 | {
14 | if (Settings.Enabled)
15 | {
16 | API = new PagerDutyAPI(this);
17 | API.TryAddToGlobalPollers();
18 | }
19 | }
20 |
21 | public override MonitorStatus MonitorStatus => API.MonitorStatus;
22 | public override bool IsMember(string node) => false;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/PagerDuty/PagerDutyRoles.cs:
--------------------------------------------------------------------------------
1 | namespace Opserver.Data.PagerDuty
2 | {
3 | public static class PagerDutyRoles
4 | {
5 | public const string Admin = nameof(PagerDutyModule) + ":" + nameof(Admin);
6 | public const string Viewer = nameof(PagerDutyModule) + ":" + nameof(Viewer);
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/PollNode.Equality.cs:
--------------------------------------------------------------------------------
1 | namespace Opserver.Data
2 | {
3 | public partial class PollNode
4 | {
5 | public bool Equals(PollNode other)
6 | {
7 | if (other is null) return false;
8 | if (ReferenceEquals(this, other)) return true;
9 | return other.GetType() == GetType() && string.Equals(UniqueKey, other.UniqueKey);
10 | }
11 |
12 | public override int GetHashCode() => UniqueKey?.GetHashCode() ?? 0;
13 |
14 | public static bool operator ==(PollNode left, PollNode right) => Equals(left, right);
15 |
16 | public static bool operator !=(PollNode left, PollNode right) => !Equals(left, right);
17 |
18 | public override bool Equals(object obj)
19 | {
20 | if (obj is null) return false;
21 | if (ReferenceEquals(this, obj)) return true;
22 | if (obj.GetType() != GetType()) return false;
23 | return Equals((PollNode)obj);
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/PollNode.Events.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Opserver.Data
4 | {
5 | public partial class PollNode
6 | {
7 | public event EventHandler MonitorStatusChanged;
8 | public event EventHandler Polling;
9 | public event EventHandler Polled;
10 |
11 | public class PollStartArgs : EventArgs
12 | {
13 | ///
14 | /// Whether to abort the poll
15 | ///
16 | public bool AbortPoll { get; set; }
17 | }
18 |
19 | public class PollResultArgs : EventArgs { }
20 |
21 | public class MonitorStatusArgs : EventArgs
22 | {
23 | public MonitorStatus OldMonitorStatus { get; internal set; }
24 | public MonitorStatus NewMonitorStatus { get; internal set; }
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/PollingService.NodeRoles.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Concurrent;
2 | using System.Collections.Generic;
3 | using System.Threading.Tasks;
4 |
5 | namespace Opserver.Data
6 | {
7 | public partial class PollingService
8 | {
9 | internal ConcurrentBag NodeRoleProviders { get; } = new ConcurrentBag();
10 |
11 | public IEnumerable GetNodeRoles(string node)
12 | {
13 | foreach (var p in NodeRoleProviders)
14 | {
15 | foreach (var r in p.GetRoles(node))
16 | {
17 | yield return r;
18 | }
19 | }
20 | }
21 |
22 | public async Task EnableAllNodeRolesAsync(string node)
23 | {
24 | var tasks = new List>();
25 | foreach (var p in NodeRoleProviders)
26 | {
27 | tasks.Add(p.EnableAsync(node));
28 | }
29 | await Task.WhenAll(tasks);
30 | return tasks.TrueForAll(b => b.Result);
31 | }
32 |
33 | public async Task DisableAllNodeRolesAsync(string node)
34 | {
35 | var tasks = new List>();
36 | foreach (var p in NodeRoleProviders)
37 | {
38 | tasks.Add(p.DisableAsync(node));
39 | }
40 | await Task.WhenAll(tasks);
41 | return tasks.TrueForAll(b => b.Result);
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/PollingService.Stats.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using Opserver.Helpers;
4 |
5 | namespace Opserver.Data
6 | {
7 | public partial class PollingService
8 | {
9 | public GlobalPollingStatus GetPollingStatus() => new GlobalPollingStatus
10 | {
11 | MonitorStatus = _globalPollingThread.IsAlive ? (AllPollNodes.Count > 0 ? MonitorStatus.Good : MonitorStatus.Unknown) : MonitorStatus.Critical,
12 | MonitorStatusReason = _globalPollingThread.IsAlive ? (AllPollNodes.Count > 0 ? null : "No Poll Nodes") : "Global Polling Thread Dead",
13 | StartTime = _startTime,
14 | LastPollAll = _lastPollAll,
15 | IsAlive = _globalPollingThread.IsAlive,
16 | TotalPollIntervals = _totalPollIntervals,
17 | ActivePolls = _globalActivePolls,
18 | NodeCount = AllPollNodes.Count,
19 | TotalPollers = AllPollNodes.Sum(n => n.DataPollers.Count()),
20 | NodeBreakdown = AllPollNodes.GroupBy(n => n.GetType()).Select(g => Tuple.Create(g.Key, g.Count())).ToList(),
21 | Nodes = AllPollNodes.ToList()
22 | };
23 |
24 | public ThreadStats GetThreadStats() => new ThreadStats();
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/Redis/RedisConnectionInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Net;
3 | using Opserver.Helpers;
4 | using StackExchange.Redis;
5 |
6 | namespace Opserver.Data.Redis
7 | {
8 | public class RedisConnectionInfo
9 | {
10 | private AddressCache AddressCache { get; }
11 | public string Name => Settings.Name;
12 | public string Host => Server.HostName;
13 | public int Port => Settings.Port;
14 | public string Password => Settings.Password;
15 | public RedisFeatures Features { get; internal set; }
16 | public RedisHost Server { get; }
17 | internal RedisSettings.Instance Settings { get; set; }
18 |
19 | internal RedisConnectionInfo(RedisHost server, RedisSettings.Instance settings, AddressCache addressCache)
20 | {
21 | Server = server;
22 | Settings = settings;
23 | AddressCache = addressCache;
24 | }
25 |
26 | public List IPAddresses => AddressCache.GetHostAddresses(Host);
27 |
28 | public override string ToString() => $"{Name} ({Host}:{Port})";
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/Redis/RedisInfo.PropertyAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Opserver.Data.Redis
4 | {
5 | [AttributeUsage(AttributeTargets.Property)]
6 | public sealed class RedisInfoPropertyAttribute : Attribute
7 | {
8 | public string PropertyName { get; }
9 | public RedisInfoPropertyAttribute(string propertyName)
10 | {
11 | PropertyName = propertyName;
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/Redis/RedisInstance.Clients.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using StackExchange.Profiling;
4 | using StackExchange.Redis;
5 |
6 | namespace Opserver.Data.Redis
7 | {
8 | public partial class RedisInstance
9 | {
10 | private Cache> _clients;
11 | public Cache> Clients =>
12 | _clients ??= GetRedisCache(60.Seconds(), async () =>
13 | {
14 | using (MiniProfiler.Current.CustomTiming("redis", "CLIENT LIST"))
15 | {
16 | var result = await Connection.GetSingleServer().ClientListAsync();
17 | return result.ToList();
18 | }
19 | });
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/Redis/RedisInstance.Issues.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Opserver.Data.Redis
4 | {
5 | public partial class RedisInstance : IIssuesProvider
6 | {
7 | string IIssuesProvider.Name => "Redis";
8 |
9 | public IEnumerable GetIssues()
10 | {
11 | if (MonitorStatus != MonitorStatus.Good && LastPoll.HasValue)
12 | {
13 | yield return new Issue(this, "Redis", Name);
14 | }
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/Redis/RedisReplicationGroup.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Opserver.Data.Redis
4 | {
5 | public class RedisReplicationGroup
6 | {
7 | public string Name { get; }
8 | public List Hosts { get; internal set; }
9 |
10 | public RedisReplicationGroup(string name, List hosts)
11 | {
12 | Name = name;
13 | Hosts = hosts;
14 |
15 | foreach (var h in hosts)
16 | {
17 | h.ReplicationGroup = this;
18 | }
19 | }
20 |
21 | public override string ToString() => Name;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/Redis/RedisRoles.cs:
--------------------------------------------------------------------------------
1 | namespace Opserver.Data.Redis
2 | {
3 | public static class RedisRoles
4 | {
5 | public const string Admin = nameof(RedisModule) + ":" + nameof(Admin);
6 | public const string Viewer = nameof(RedisModule) + ":" + nameof(Viewer);
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/SQL/ISQLVersioned.cs:
--------------------------------------------------------------------------------
1 | namespace Opserver.Data.SQL
2 | {
3 | public interface ISQLVersioned : IMinVersioned
4 | {
5 | SQLServerEditions SupportedEditions { get; }
6 |
7 | string GetFetchSQL(in SQLServerEngine e);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/SQL/SQLCluster.Issues.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Opserver.Data.SQL
4 | {
5 | public partial class SQLCluster : IIssuesProvider
6 | {
7 | string IIssuesProvider.Name => "SQL";
8 |
9 | public IEnumerable GetIssues()
10 | {
11 | foreach (var ag in AvailabilityGroups.WithIssues())
12 | {
13 | yield return new Issue(ag, "SQL Availability Group", ag.Name) { IsCluster = true };
14 | }
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/SQL/SQLInstance.Errors.cs:
--------------------------------------------------------------------------------
1 | using Dapper;
2 | using System;
3 | using System.Collections.Generic;
4 |
5 | namespace Opserver.Data.SQL
6 | {
7 | public partial class SQLInstance
8 | {
9 | public LightweightCache> GetErrorLog(int minutesAgo)
10 | {
11 | return TimedCache("ErrorInfo-" + minutesAgo.ToString(),
12 | conn =>
13 | {
14 | var sql = GetFetchSQL();
15 | return conn.Query(sql, new { minutesAgo }).AsList();
16 | }, RefreshInterval, 5.Minutes());
17 | }
18 |
19 | public class SQLErrorLogInfo : ISQLVersioned
20 | {
21 | Version IMinVersioned.MinVersion => SQLServerVersions.SQL2005.RTM;
22 | SQLServerEditions ISQLVersioned.SupportedEditions => SQLServerEditions.All;
23 |
24 | public DateTime LogDate { get; internal set; }
25 | public string ProcessInfo { get; internal set; }
26 | public string Text { get; internal set; }
27 |
28 | public string GetFetchSQL(in SQLServerEngine e) => @"
29 | Declare @Time_Start varchar(30);
30 | Set @Time_Start = DATEADD(mi, -@minutesAgo, GETDATE());
31 | Declare @ErrorLog Table (LogDate datetime, ProcessInfo varchar(255), Text varchar(max));
32 | Insert Into @ErrorLog Exec master.dbo.xp_readerrorlog 0, 1, NULL, NULL, @Time_Start, NULL;
33 | Select * From @ErrorLog Order By LogDate Desc;";
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/SQL/SQLInstance.Issues.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Opserver.Data.SQL
4 | {
5 | public partial class SQLInstance : IIssuesProvider
6 | {
7 | string IIssuesProvider.Name => "SQL";
8 |
9 | public IEnumerable GetIssues()
10 | {
11 | if (MonitorStatus != MonitorStatus.Good)
12 | {
13 | yield return new Issue(this, "SQL Server", Name);
14 | }
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/SQL/SQLInstance.TraceFlags.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Opserver.Data.SQL
5 | {
6 | public partial class SQLInstance
7 | {
8 | private Cache> _traceFlags;
9 | public Cache> TraceFlags => _traceFlags ??= SqlCacheList(5.Minutes());
10 |
11 | public class TraceFlagInfo : ISQLVersioned
12 | {
13 | // This likely works fine on 6+, need to test
14 | Version IMinVersioned.MinVersion => SQLServerVersions.SQL2000.RTM;
15 | SQLServerEditions ISQLVersioned.SupportedEditions => SQLServerEditions.All;
16 |
17 | public int TraceFlag { get; internal set; }
18 | public bool Enabled { get; internal set; }
19 | public bool Global { get; internal set; }
20 | public int Session { get; internal set; }
21 |
22 | public string GetFetchSQL(in SQLServerEngine e) => @"
23 | Declare @Flags Table(TraceFlag INT, Enabled BIT, Global BIT, Session INT);
24 | Insert Into @Flags Exec('DBCC TRACESTATUS (-1) WITH NO_INFOMSGS');
25 | Select * From @Flags;";
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/SQL/SQLInstance.Utils.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 |
4 | namespace Opserver.Data.SQL
5 | {
6 | public partial class SQLInstance
7 | {
8 | ///
9 | /// Removes a query plan from the cache
10 | ///
11 | /// The handle of the plan to fetch
12 | public async Task RemovePlanAsync(byte[] planHandle)
13 | {
14 | try
15 | {
16 | using var conn = await GetConnectionAsync();
17 | return await conn.ExecuteAsync("DBCC FREEPROCCACHE (@planHandle);", new { planHandle });
18 | }
19 | catch (Exception ex)
20 | {
21 | ex.Log();
22 | return 0;
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/SQL/SQLNode.AvailabilityGroups.AGListener.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Opserver.Data.SQL
5 | {
6 | public partial class SQLNode
7 | {
8 | ///
9 | /// http://msdn.microsoft.com/en-us/library/hh231328.aspx
10 | ///
11 | public class AGListener : ISQLVersioned
12 | {
13 | Version IMinVersioned.MinVersion => SQLServerVersions.SQL2012.RTM;
14 | SQLServerEditions ISQLVersioned.SupportedEditions => SQLServerEditions.All;
15 |
16 | public Guid GroupId { get; internal set; }
17 | public string ListenerId { get; internal set; }
18 | public string DnsName { get; internal set; }
19 | public int Port { get; internal set; }
20 | public bool IsConformant { get; internal set; }
21 | public string IPConfigurationString { get; internal set; }
22 |
23 | public List Addresses { get; internal set; }
24 | public List LocalAddresses { get; internal set; }
25 |
26 | public string GetFetchSQL(in SQLServerEngine e) => @"
27 | Select group_id GroupId,
28 | listener_id ListenerId,
29 | dns_name DNSName,
30 | port Port,
31 | is_conformant IsConformant,
32 | ip_configuration_string_from_cluster IPConfigurationString
33 | From sys.availability_group_listeners;";
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/SQL/SQLRoles.cs:
--------------------------------------------------------------------------------
1 | namespace Opserver.Data.SQL
2 | {
3 | public static class SQLRoles
4 | {
5 | public const string Admin = nameof(SQLModule) + ":" + nameof(Admin);
6 | public const string Viewer = nameof(SQLModule) + ":" + nameof(Viewer);
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Data/SearchResult.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.Serialization;
2 |
3 | namespace Opserver.Data
4 | {
5 | public class SearchResult : SearchResult
6 | {
7 | [IgnoreDataMember]
8 | public T Item { get; set; }
9 | }
10 |
11 | public class SearchResult : IMonitorStatus
12 | {
13 | public MonitorStatus MonitorStatus { get; set; }
14 | public string MonitorStatusReason { get; set; }
15 | public string Title { get; set; }
16 | public string Description { get; set; }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/Opserver.Core/ExtensionMethods.Exceptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using StackExchange.Exceptional;
3 |
4 | namespace Opserver
5 | {
6 | public static partial class ExtensionMethods
7 | {
8 | ///
9 | /// Manually write an exception to our standard exception log.
10 | ///
11 | /// The to log.
12 | public static void Log(this Exception exception) => exception.LogNoContext();
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/Opserver.Core/ExtensionMethods.Sequences.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Buffers;
3 | using System.Text;
4 |
5 | namespace Opserver
6 | {
7 | public static partial class ExtensionMethods
8 | {
9 | internal static string GetString(this ref SequenceReader reader, int length)
10 | {
11 | var value = Encoding.UTF8.GetString(reader.UnreadSpan.Slice(0, length).Trim((byte)0));
12 | reader.Advance(length);
13 | return value;
14 | }
15 |
16 | internal static string GetBase64EncodedString(this ref SequenceReader reader, int length)
17 | {
18 | var value = Convert.ToBase64String(reader.UnreadSpan.Slice(0, length));
19 | reader.Advance(length);
20 | return value;
21 | }
22 |
23 | internal static bool TryReadBigEndian(this ref SequenceReader reader, out double value)
24 | {
25 | if (!reader.TryReadBigEndian(out long longValue))
26 | {
27 | value = default;
28 | return false;
29 | }
30 |
31 | value = BitConverter.Int64BitsToDouble(longValue);
32 | return true;
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Opserver.Core/ExtensionMethods.Time.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Opserver
4 | {
5 | public static partial class ExtensionMethods
6 | {
7 | // TODO: Convert these to TicksPerSecond constructor, faster.
8 | public static TimeSpan Seconds(this int seconds) => TimeSpan.FromSeconds(seconds);
9 | public static TimeSpan Minutes(this int minutes) => TimeSpan.FromMinutes(minutes);
10 | public static TimeSpan Hours(this int hours) => TimeSpan.FromHours(hours);
11 | public static TimeSpan Days(this int days) => TimeSpan.FromDays(days);
12 | // https://stackoverflow.com/a/20046261/871146
13 | public static DateTime RoundDown(this DateTime dt, TimeSpan d)
14 | {
15 | var delta = dt.Ticks % d.Ticks;
16 | return new DateTime(dt.Ticks - delta, dt.Kind);
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Helpers/OpserverConfigException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.Serialization;
3 |
4 | namespace Opserver.Helpers
5 | {
6 | public class OpserverConfigException : Exception
7 | {
8 | public OpserverConfigException() { }
9 | public OpserverConfigException(string message) : base(message) { }
10 | public OpserverConfigException(string message, Exception innerException) : base(message, innerException) { }
11 | protected OpserverConfigException(SerializationInfo info, StreamingContext context) { }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Helpers/Singleton.cs:
--------------------------------------------------------------------------------
1 | namespace Opserver.Helpers
2 | {
3 | public static class Singleton where T : new()
4 | {
5 | public static readonly T Instance = new T();
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Helpers/SortDir.cs:
--------------------------------------------------------------------------------
1 | namespace Opserver
2 | {
3 | public enum SortDir
4 | {
5 | Asc,
6 | Desc
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Helpers/ThreadStats.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 |
3 | namespace Opserver.Helpers
4 | {
5 | public class ThreadStats
6 | {
7 | private readonly int _minWorkerThreads;
8 | public int MinWorkerThreads => _minWorkerThreads;
9 |
10 | private readonly int _minIOThreads;
11 | public int MinIOThreads => _minIOThreads;
12 |
13 | private readonly int _availableWorkerThreads;
14 | public int AvailableWorkerThreads => _availableWorkerThreads;
15 |
16 | private readonly int _availableIOThreads;
17 | public int AvailableIOThreads => _availableIOThreads;
18 |
19 | private readonly int _maxIOThreads;
20 | public int MaxIOThreads => _maxIOThreads;
21 |
22 | private readonly int _maxWorkerThreads;
23 | public int MaxWorkerThreads => _maxWorkerThreads;
24 |
25 | public int BusyIOThreads => _maxIOThreads - _availableIOThreads;
26 | public int BusyWorkerThreads => _maxWorkerThreads - _availableWorkerThreads;
27 |
28 | public ThreadStats()
29 | {
30 | ThreadPool.GetMinThreads(out _minWorkerThreads, out _minIOThreads);
31 | ThreadPool.GetAvailableThreads(out _availableWorkerThreads, out _availableIOThreads);
32 | ThreadPool.GetMaxThreads(out _maxWorkerThreads, out _maxIOThreads);
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Helpers/WindowsKernelVersions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Opserver.Helpers
4 | {
5 | public static class WindowsKernelVersions
6 | {
7 | public static readonly Version WindowsNT31 = new Version(3, 1);
8 | public static readonly Version WindowsNT35 = new Version(3, 5);
9 | public static readonly Version WindowsNT351 = new Version(3, 51);
10 | public static readonly Version WindowsNT4 = new Version(4, 0);
11 | public static readonly Version Windows2000 = new Version(5, 0);
12 | public static readonly Version WindowsXP = new Version(5, 1);
13 | public static readonly Version Windows2003AndXP64 = new Version(5, 2);
14 | public static readonly Version Windows2008AndVista = new Version(6, 0);
15 | public static readonly Version Windows2008R2And7 = new Version(6, 1);
16 | public static readonly Version Windows2012And8 = new Version(6, 2);
17 | public static readonly Version Windows2012R2And81 = new Version(6, 3);
18 | public static readonly Version Windows2016And10 = new Version(10, 0);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Opserver.Core/IMinVersioned.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.Serialization;
3 |
4 | namespace Opserver
5 | {
6 | public interface IMinVersioned
7 | {
8 | [IgnoreDataMember]
9 | Version MinVersion { get; }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/Opserver.Core/IOverallStatusCount.cs:
--------------------------------------------------------------------------------
1 | namespace Opserver
2 | {
3 | public interface IOverallStatusCount
4 | {
5 | int Count { get; }
6 | string Tooltip { get; }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/Opserver.Core/ISeachableNode.cs:
--------------------------------------------------------------------------------
1 | using Opserver.Data;
2 |
3 | namespace Opserver
4 | {
5 | public interface ISearchableNode
6 | {
7 | string DisplayName { get; }
8 | string Name { get; }
9 | string CategoryName { get; }
10 | MonitorStatus MonitorStatus { get; }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Models/Roles.cs:
--------------------------------------------------------------------------------
1 | namespace Opserver.Models
2 | {
3 | public static class Roles
4 | {
5 | public const string Anonymous = nameof(Anonymous);
6 | public const string Authenticated = nameof(Authenticated);
7 |
8 | public const string LocalRequest = nameof(LocalRequest);
9 | public const string InternalRequest = nameof(InternalRequest);
10 | public const string ApiRequest = nameof(ApiRequest);
11 |
12 | public const string GlobalAdmin = nameof(GlobalAdmin);
13 | public const string GlobalViewer = nameof(GlobalViewer);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Settings/CloudFlareSettings.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Opserver.Data.Cloudflare;
3 |
4 | namespace Opserver
5 | {
6 | public class CloudflareSettings : ModuleSettings
7 | {
8 | public override bool Enabled => Email.HasValue() && APIKey.HasValue();
9 | public override string AdminRole => CloudflareRoles.Admin;
10 | public override string ViewRole => CloudflareRoles.Viewer;
11 | public List DataCenters { get; set; } = new List();
12 |
13 | ///
14 | /// Email for the Cloudflare account
15 | ///
16 | public string Email { get; set; }
17 |
18 | ///
19 | /// APIKey for the Cloudflare account
20 | ///
21 | public string APIKey { get; set; }
22 |
23 | public class DataCenter : ISettingsCollectionItem
24 | {
25 | ///
26 | /// The name for this data center
27 | ///
28 | public string Name { get; set; }
29 |
30 | ///
31 | /// The IP ranges for this data center, in CIDR format
32 | ///
33 | public List Ranges { get; set; } = new List();
34 |
35 | ///
36 | /// The masked IP ranges for this data center, in CIDR format
37 | ///
38 | public List MaskedRanges { get; set; } = new List();
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Settings/DashboardSettings.Bosun.cs:
--------------------------------------------------------------------------------
1 | namespace Opserver
2 | {
3 | public class BosunSettings : IProviderSettings
4 | {
5 | public bool Enabled => Host.HasValue();
6 | public string Name => "Bosun";
7 |
8 | ///
9 | /// Endpoint for Bosun APIs, e.g. "bosun.mydomain.com", also accepts http or https:// forms
10 | ///
11 | public string Host { get; set; }
12 |
13 | ///
14 | /// API Key for accessing Bosun APIs
15 | ///
16 | public string APIKey { get; set; }
17 |
18 | ///
19 | /// Whether to ignore the Bosun ping status. If true , unreachable nodes will not be marked as such.
20 | ///
21 | public bool IgnorePing { get; set; }
22 |
23 | public void Normalize()
24 | {
25 | Host = Host.NormalizeHostOrFQDN();
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Settings/DashboardSettings.Orion.cs:
--------------------------------------------------------------------------------
1 | namespace Opserver
2 | {
3 | public class OrionSettings : IProviderSettings
4 | {
5 | public bool Enabled => Host.HasValue();
6 | public string Name => "Orion";
7 |
8 | ///
9 | /// The host for Orion, used for generating links
10 | ///
11 | public string Host { get; set; }
12 |
13 | ///
14 | /// The connection string for this provider
15 | ///
16 | public string ConnectionString { get; set; }
17 |
18 | ///
19 | /// Default maximum timeout in milliseconds before giving up on fetching data from this provider
20 | ///
21 | public int QueryTimeoutMs { get; set; } = 10 * 1000;
22 |
23 | ///
24 | /// Whether to show child statuses if the node is overall healthy. Example: warning if any drives are in warning state.
25 | ///
26 | public bool ChildStatusForHealthy { get; set; } = false;
27 |
28 | public void Normalize()
29 | {
30 | Host = Host.NormalizeHostOrFQDN();
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Settings/DashboardSettings.Providers.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 |
4 | namespace Opserver
5 | {
6 | public class ProvidersSettings
7 | {
8 | public BosunSettings Bosun { get; set; }
9 | public OrionSettings Orion { get; set; }
10 | public WMISettings WMI { get; set; }
11 | public SignalFxSettings SignalFx { get; set; }
12 |
13 | public bool Any() => All.Any(p => p != null);
14 |
15 | public IEnumerable All
16 | {
17 | get
18 | {
19 | yield return Bosun;
20 | yield return Orion;
21 | yield return WMI;
22 | yield return SignalFx;
23 | }
24 | }
25 | }
26 |
27 | public interface IProviderSettings
28 | {
29 | bool Enabled { get; }
30 | string Name { get; }
31 |
32 | void Normalize();
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Settings/DashboardSettings.SignalFx.cs:
--------------------------------------------------------------------------------
1 | namespace Opserver
2 | {
3 | public class SignalFxSettings : IProviderSettings
4 | {
5 | public bool Enabled => AccessToken.HasValue();
6 | public string Name => "SignalFx";
7 |
8 | ///
9 | /// Realm for SignalFx, e.g. "us1", "eu0"
10 | ///
11 | public string Realm { get; set; }
12 |
13 | ///
14 | /// API Key for accessing SignalFx APIs
15 | ///
16 | public string AccessToken { get; set; }
17 |
18 | public void Normalize()
19 | {
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Settings/DashboardSettings.WMI.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Opserver
4 | {
5 | public class WMISettings : IProviderSettings
6 | {
7 | public bool Enabled => Nodes.Count > 0;
8 | public string Name => "WMI";
9 |
10 | ///
11 | /// List of hostnames to poll via WMI
12 | ///
13 | public List Nodes { get; set; } = new List();
14 |
15 | ///
16 | /// Timeout in seconds for cache with more or less static data, like node name or volume size.
17 | ///
18 | public int StaticDataTimeoutSeconds { get; set; } = 5 * 60;
19 |
20 | ///
21 | /// Timeout in seconds for dynamic data like CPU load.
22 | ///
23 | public int DynamicDataTimeoutSeconds { get; set; } = 30;
24 |
25 | ///
26 | /// Amount of history to keep
27 | ///
28 | public int HistoryHours { get; set; } = 24;
29 |
30 | ///
31 | /// Username to use when polling (for non-domain testing)
32 | ///
33 | public string Username { get; set; }
34 | ///
35 | /// Password to use when polling (for non-domain testing)
36 | ///
37 | public string Password { get; set; }
38 |
39 | public void Normalize() {}
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Settings/Interfaces.cs:
--------------------------------------------------------------------------------
1 | namespace Opserver
2 | {
3 | public interface ISecurableModule
4 | {
5 | bool Enabled { get; }
6 | // TODO: List
7 | string ViewGroups { get; }
8 | string AdminGroups { get; }
9 |
10 | string ViewRole { get; }
11 | string AdminRole { get; }
12 | }
13 |
14 | public interface ISettingsCollectionItem
15 | {
16 | string Name { get; }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Settings/ModuleSettings.cs:
--------------------------------------------------------------------------------
1 | namespace Opserver
2 | {
3 | public abstract class ModuleSettings : ISecurableModule
4 | {
5 | ///
6 | /// Whether this section is enabled (has servers, has connection, etc.)
7 | ///
8 | public abstract bool Enabled { get; }
9 |
10 | ///
11 | /// Semilcolon delimited list of security groups that can see this section, but not perform actions.
12 | ///
13 | public string ViewGroups { get; set; }
14 |
15 | ///
16 | /// Semilcolon delimited list of security groups that can do anything in this section, including management actions.
17 | ///
18 | public string AdminGroups { get; set; }
19 |
20 | ///
21 | /// The role name to use for viewers of this module.
22 | ///
23 | public abstract string ViewRole { get; }
24 |
25 | ///
26 | /// The role name to use for admins of this module.
27 | ///
28 | public abstract string AdminRole { get; }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Settings/OpserverSettings.cs:
--------------------------------------------------------------------------------
1 | namespace Opserver
2 | {
3 | public class OpserverSettings
4 | {
5 | public GlobalSettings Global { get; set; } = new GlobalSettings();
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/Opserver.Core/Settings/PagerDutySettings.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Opserver.Data.PagerDuty;
3 |
4 | namespace Opserver
5 | {
6 | public class PagerDutySettings : ModuleSettings
7 | {
8 | public override bool Enabled => APIKey.HasValue();
9 | public override string AdminRole => PagerDutyRoles.Admin;
10 | public override string ViewRole => PagerDutyRoles.Viewer;
11 |
12 | public string APIKey { get; set; }
13 | public List UserNameMap { get; set; } = new List();
14 |
15 | public int OnCallToShow { get; set; } = 2;
16 | public int DaysToCache { get; set; } = 60;
17 | public string HeaderTitle { get; set; }
18 | public string HeaderHtml { get; set; }
19 |
20 | public string PrimaryScheduleName { get; set; }
21 | }
22 |
23 | public class EmailMapping
24 | {
25 | public string OpServerName { get; set; }
26 | public string EmailUser { get; set; }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Opserver.Core/StringSplits.cs:
--------------------------------------------------------------------------------
1 | namespace Opserver
2 | {
3 | ///
4 | /// From Marc Gravell - every time a split is used you can either allocate an array
5 | /// ...or reference the single one created here and avoid that
6 | ///
7 | public static class StringSplits
8 | {
9 | public static readonly char[]
10 | Space = {' '},
11 | Comma = {','},
12 | Period = {'.'},
13 | Minus = {'-'},
14 | Plus = {'+'},
15 | Asterisk = {'*'},
16 | Percent = {'%'},
17 | Ampersand = {'&'},
18 | AtSign = {'@'},
19 | Equal = {'='},
20 | Underscore = {'_'},
21 | NewLine = {'\n'},
22 | SemiColon = {';'},
23 | Colon = {':'},
24 | VerticalBar = {'|'},
25 | ForwardSlash = {'/'},
26 | BackSlash = {'\\'},
27 | DoubleQuote = {'"'},
28 | Tilde = {'`'},
29 | Period_Plus = {'.', '+'},
30 | NewLine_CarriageReturn = {'\n', '\r'},
31 | Comma_SemiColon = {',', ';'},
32 | Comma_SemiColon_Space = {',', ';', ' '},
33 | BackSlash_Slash_Period = {'\\', '/', '.'},
34 | Numbers = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Opserver.Core/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/bootstrap/less/.csslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "adjoining-classes": false,
3 | "box-sizing": false,
4 | "box-model": false,
5 | "compatible-vendor-prefixes": false,
6 | "floats": false,
7 | "font-sizes": false,
8 | "gradients": false,
9 | "important": false,
10 | "known-properties": false,
11 | "outline-none": false,
12 | "qualified-headings": false,
13 | "regex-selectors": false,
14 | "shorthand": false,
15 | "text-indent": false,
16 | "unique-headings": false,
17 | "universal-selector": false,
18 | "unqualified-attributes": false
19 | }
20 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/bootstrap/less/badges.less:
--------------------------------------------------------------------------------
1 | //
2 | // Badges
3 | // --------------------------------------------------
4 |
5 |
6 | // Base class
7 | .badge {
8 | display: inline-block;
9 | min-width: 10px;
10 | padding: 3px 7px;
11 | font-size: @font-size-small;
12 | font-weight: @badge-font-weight;
13 | color: @badge-color;
14 | line-height: @badge-line-height;
15 | vertical-align: middle;
16 | white-space: nowrap;
17 | text-align: center;
18 | background-color: @badge-bg;
19 | border-radius: @badge-border-radius;
20 |
21 | // Empty badges collapse automatically (not available in IE8)
22 | &:empty {
23 | display: none;
24 | }
25 |
26 | // Quick fix for badges in buttons
27 | .btn & {
28 | position: relative;
29 | top: -1px;
30 | }
31 |
32 | .btn-xs &,
33 | .btn-group-xs > .btn & {
34 | top: 0;
35 | padding: 1px 5px;
36 | }
37 |
38 | // Hover state, but only for links
39 | a& {
40 | &:hover,
41 | &:focus {
42 | color: @badge-link-hover-color;
43 | text-decoration: none;
44 | cursor: pointer;
45 | }
46 | }
47 |
48 | // Account for badges in navs
49 | .list-group-item.active > &,
50 | .nav-pills > .active > a > & {
51 | color: @badge-active-color;
52 | background-color: @badge-active-bg;
53 | }
54 |
55 | .list-group-item > & {
56 | float: right;
57 | }
58 |
59 | .list-group-item > & + & {
60 | margin-right: 5px;
61 | }
62 |
63 | .nav-pills > li > a > & {
64 | margin-left: 3px;
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/bootstrap/less/bootstrap.less:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap v3.3.7 (http://getbootstrap.com)
3 | * Copyright 2011-2016 Twitter, Inc.
4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
5 | */
6 |
7 | // Core variables and mixins
8 | @import "variables.less";
9 | @import "mixins.less";
10 |
11 | // Reset and dependencies
12 | @import "normalize.less";
13 | @import "print.less";
14 |
15 | // Core CSS
16 | @import "scaffolding.less";
17 | @import "type.less";
18 | @import "code.less";
19 | @import "grid.less";
20 | @import "tables.less";
21 | @import "forms.less";
22 | @import "buttons.less";
23 |
24 | // Components
25 | @import "component-animations.less";
26 | @import "dropdowns.less";
27 | @import "button-groups.less";
28 | @import "input-groups.less";
29 | @import "navs.less";
30 | @import "navbar.less";
31 | @import "breadcrumbs.less";
32 | @import "pagination.less";
33 | @import "pager.less";
34 | @import "labels.less";
35 | @import "badges.less";
36 | @import "jumbotron.less";
37 | @import "thumbnails.less";
38 | @import "alerts.less";
39 | @import "progress-bars.less";
40 | @import "media.less";
41 | @import "list-group.less";
42 | @import "panels.less";
43 | @import "responsive-embed.less";
44 | @import "wells.less";
45 | @import "close.less";
46 |
47 | // Components w/ JavaScript
48 | @import "modals.less";
49 | @import "tooltip.less";
50 | @import "popovers.less";
51 | @import "carousel.less";
52 |
53 | // Utility classes
54 | @import "utilities.less";
55 | @import "responsive-utilities.less";
56 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/bootstrap/less/breadcrumbs.less:
--------------------------------------------------------------------------------
1 | //
2 | // Breadcrumbs
3 | // --------------------------------------------------
4 |
5 |
6 | .breadcrumb {
7 | padding: @breadcrumb-padding-vertical @breadcrumb-padding-horizontal;
8 | margin-bottom: @line-height-computed;
9 | list-style: none;
10 | background-color: @breadcrumb-bg;
11 | border-radius: @border-radius-base;
12 |
13 | > li {
14 | display: inline-block;
15 |
16 | + li:before {
17 | content: "@{breadcrumb-separator}\00a0"; // Unicode space added since inline-block means non-collapsing white-space
18 | padding: 0 5px;
19 | color: @breadcrumb-color;
20 | }
21 | }
22 |
23 | > .active {
24 | color: @breadcrumb-active-color;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/bootstrap/less/close.less:
--------------------------------------------------------------------------------
1 | //
2 | // Close icons
3 | // --------------------------------------------------
4 |
5 |
6 | .close {
7 | float: right;
8 | font-size: (@font-size-base * 1.5);
9 | font-weight: @close-font-weight;
10 | line-height: 1;
11 | color: @close-color;
12 | text-shadow: @close-text-shadow;
13 | .opacity(.2);
14 |
15 | &:hover,
16 | &:focus {
17 | color: @close-color;
18 | text-decoration: none;
19 | cursor: pointer;
20 | .opacity(.5);
21 | }
22 |
23 | // Additional properties for button version
24 | // iOS requires the button element instead of an anchor tag.
25 | // If you want the anchor version, it requires `href="#"`.
26 | // See https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile
27 | button& {
28 | padding: 0;
29 | cursor: pointer;
30 | background: transparent;
31 | border: 0;
32 | -webkit-appearance: none;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/bootstrap/less/component-animations.less:
--------------------------------------------------------------------------------
1 | //
2 | // Component animations
3 | // --------------------------------------------------
4 |
5 | // Heads up!
6 | //
7 | // We don't use the `.opacity()` mixin here since it causes a bug with text
8 | // fields in IE7-8. Source: https://github.com/twbs/bootstrap/pull/3552.
9 |
10 | .fade {
11 | opacity: 0;
12 | .transition(opacity .15s linear);
13 | &.in {
14 | opacity: 1;
15 | }
16 | }
17 |
18 | .collapse {
19 | display: none;
20 |
21 | &.in { display: block; }
22 | tr&.in { display: table-row; }
23 | tbody&.in { display: table-row-group; }
24 | }
25 |
26 | .collapsing {
27 | position: relative;
28 | height: 0;
29 | overflow: hidden;
30 | .transition-property(~"height, visibility");
31 | .transition-duration(.35s);
32 | .transition-timing-function(ease);
33 | }
34 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/bootstrap/less/jumbotron.less:
--------------------------------------------------------------------------------
1 | //
2 | // Jumbotron
3 | // --------------------------------------------------
4 |
5 |
6 | .jumbotron {
7 | padding-top: @jumbotron-padding;
8 | padding-bottom: @jumbotron-padding;
9 | margin-bottom: @jumbotron-padding;
10 | color: @jumbotron-color;
11 | background-color: @jumbotron-bg;
12 |
13 | h1,
14 | .h1 {
15 | color: @jumbotron-heading-color;
16 | }
17 |
18 | p {
19 | margin-bottom: (@jumbotron-padding / 2);
20 | font-size: @jumbotron-font-size;
21 | font-weight: 200;
22 | }
23 |
24 | > hr {
25 | border-top-color: darken(@jumbotron-bg, 10%);
26 | }
27 |
28 | .container &,
29 | .container-fluid & {
30 | border-radius: @border-radius-large; // Only round corners at higher resolutions if contained in a container
31 | padding-left: (@grid-gutter-width / 2);
32 | padding-right: (@grid-gutter-width / 2);
33 | }
34 |
35 | .container {
36 | max-width: 100%;
37 | }
38 |
39 | @media screen and (min-width: @screen-sm-min) {
40 | padding-top: (@jumbotron-padding * 1.6);
41 | padding-bottom: (@jumbotron-padding * 1.6);
42 |
43 | .container &,
44 | .container-fluid & {
45 | padding-left: (@jumbotron-padding * 2);
46 | padding-right: (@jumbotron-padding * 2);
47 | }
48 |
49 | h1,
50 | .h1 {
51 | font-size: @jumbotron-heading-font-size;
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/bootstrap/less/labels.less:
--------------------------------------------------------------------------------
1 | //
2 | // Labels
3 | // --------------------------------------------------
4 |
5 | .label {
6 | display: inline;
7 | padding: .2em .6em .3em;
8 | font-size: 75%;
9 | font-weight: bold;
10 | line-height: 1;
11 | color: @label-color;
12 | text-align: center;
13 | white-space: nowrap;
14 | vertical-align: baseline;
15 | border-radius: .25em;
16 |
17 | // Add hover effects, but only for links
18 | a& {
19 | &:hover,
20 | &:focus {
21 | color: @label-link-hover-color;
22 | text-decoration: none;
23 | cursor: pointer;
24 | }
25 | }
26 |
27 | // Empty labels collapse automatically (not available in IE8)
28 | &:empty {
29 | display: none;
30 | }
31 |
32 | // Quick fix for labels in buttons
33 | .btn & {
34 | position: relative;
35 | top: -1px;
36 | }
37 | }
38 |
39 | // Colors
40 | // Contextual variations (linked labels get darker on :hover)
41 |
42 | .label-default {
43 | .label-variant(@label-default-bg);
44 | }
45 |
46 | .label-primary {
47 | .label-variant(@label-primary-bg);
48 | }
49 |
50 | .label-success {
51 | .label-variant(@label-success-bg);
52 | }
53 |
54 | .label-info {
55 | .label-variant(@label-info-bg);
56 | }
57 |
58 | .label-warning {
59 | .label-variant(@label-warning-bg);
60 | }
61 |
62 | .label-danger {
63 | .label-variant(@label-danger-bg);
64 | }
65 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/bootstrap/less/media.less:
--------------------------------------------------------------------------------
1 | .media {
2 | // Proper spacing between instances of .media
3 | margin-top: 15px;
4 |
5 | &:first-child {
6 | margin-top: 0;
7 | }
8 | }
9 |
10 | .media,
11 | .media-body {
12 | zoom: 1;
13 | overflow: hidden;
14 | }
15 |
16 | .media-body {
17 | width: 10000px;
18 | }
19 |
20 | .media-object {
21 | display: block;
22 |
23 | // Fix collapse in webkit from max-width: 100% and display: table-cell.
24 | &.img-thumbnail {
25 | max-width: none;
26 | }
27 | }
28 |
29 | .media-right,
30 | .media > .pull-right {
31 | padding-left: 10px;
32 | }
33 |
34 | .media-left,
35 | .media > .pull-left {
36 | padding-right: 10px;
37 | }
38 |
39 | .media-left,
40 | .media-right,
41 | .media-body {
42 | display: table-cell;
43 | vertical-align: top;
44 | }
45 |
46 | .media-middle {
47 | vertical-align: middle;
48 | }
49 |
50 | .media-bottom {
51 | vertical-align: bottom;
52 | }
53 |
54 | // Reset margins on headings for tighter default spacing
55 | .media-heading {
56 | margin-top: 0;
57 | margin-bottom: 5px;
58 | }
59 |
60 | // Media list variation
61 | //
62 | // Undo default ul/ol styles
63 | .media-list {
64 | padding-left: 0;
65 | list-style: none;
66 | }
67 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/bootstrap/less/mixins.less:
--------------------------------------------------------------------------------
1 | // Mixins
2 | // --------------------------------------------------
3 |
4 | // Utilities
5 | @import "mixins/hide-text.less";
6 | @import "mixins/opacity.less";
7 | @import "mixins/image.less";
8 | @import "mixins/labels.less";
9 | @import "mixins/reset-filter.less";
10 | @import "mixins/resize.less";
11 | @import "mixins/responsive-visibility.less";
12 | @import "mixins/size.less";
13 | @import "mixins/tab-focus.less";
14 | @import "mixins/reset-text.less";
15 | @import "mixins/text-emphasis.less";
16 | @import "mixins/text-overflow.less";
17 | @import "mixins/vendor-prefixes.less";
18 |
19 | // Components
20 | @import "mixins/alerts.less";
21 | @import "mixins/buttons.less";
22 | @import "mixins/panels.less";
23 | @import "mixins/pagination.less";
24 | @import "mixins/list-group.less";
25 | @import "mixins/nav-divider.less";
26 | @import "mixins/forms.less";
27 | @import "mixins/progress-bar.less";
28 | @import "mixins/table-row.less";
29 |
30 | // Skins
31 | @import "mixins/background-variant.less";
32 | @import "mixins/border-radius.less";
33 | @import "mixins/gradients.less";
34 |
35 | // Layout
36 | @import "mixins/clearfix.less";
37 | @import "mixins/center-block.less";
38 | @import "mixins/nav-vertical-align.less";
39 | @import "mixins/grid-framework.less";
40 | @import "mixins/grid.less";
41 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/bootstrap/less/mixins/alerts.less:
--------------------------------------------------------------------------------
1 | // Alerts
2 |
3 | .alert-variant(@background; @border; @text-color) {
4 | background-color: @background;
5 | border-color: @border;
6 | color: @text-color;
7 |
8 | hr {
9 | border-top-color: darken(@border, 5%);
10 | }
11 | .alert-link {
12 | color: darken(@text-color, 10%);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/bootstrap/less/mixins/background-variant.less:
--------------------------------------------------------------------------------
1 | // Contextual backgrounds
2 |
3 | .bg-variant(@color) {
4 | background-color: @color;
5 | a&:hover,
6 | a&:focus {
7 | background-color: darken(@color, 10%);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/bootstrap/less/mixins/border-radius.less:
--------------------------------------------------------------------------------
1 | // Single side border-radius
2 |
3 | .border-top-radius(@radius) {
4 | border-top-right-radius: @radius;
5 | border-top-left-radius: @radius;
6 | }
7 | .border-right-radius(@radius) {
8 | border-bottom-right-radius: @radius;
9 | border-top-right-radius: @radius;
10 | }
11 | .border-bottom-radius(@radius) {
12 | border-bottom-right-radius: @radius;
13 | border-bottom-left-radius: @radius;
14 | }
15 | .border-left-radius(@radius) {
16 | border-bottom-left-radius: @radius;
17 | border-top-left-radius: @radius;
18 | }
19 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/bootstrap/less/mixins/center-block.less:
--------------------------------------------------------------------------------
1 | // Center-align a block level element
2 |
3 | .center-block() {
4 | display: block;
5 | margin-left: auto;
6 | margin-right: auto;
7 | }
8 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/bootstrap/less/mixins/clearfix.less:
--------------------------------------------------------------------------------
1 | // Clearfix
2 | //
3 | // For modern browsers
4 | // 1. The space content is one way to avoid an Opera bug when the
5 | // contenteditable attribute is included anywhere else in the document.
6 | // Otherwise it causes space to appear at the top and bottom of elements
7 | // that are clearfixed.
8 | // 2. The use of `table` rather than `block` is only necessary if using
9 | // `:before` to contain the top-margins of child elements.
10 | //
11 | // Source: http://nicolasgallagher.com/micro-clearfix-hack/
12 |
13 | .clearfix() {
14 | &:before,
15 | &:after {
16 | content: " "; // 1
17 | display: table; // 2
18 | }
19 | &:after {
20 | clear: both;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/bootstrap/less/mixins/hide-text.less:
--------------------------------------------------------------------------------
1 | // CSS image replacement
2 | //
3 | // Heads up! v3 launched with only `.hide-text()`, but per our pattern for
4 | // mixins being reused as classes with the same name, this doesn't hold up. As
5 | // of v3.0.1 we have added `.text-hide()` and deprecated `.hide-text()`.
6 | //
7 | // Source: https://github.com/h5bp/html5-boilerplate/commit/aa0396eae757
8 |
9 | // Deprecated as of v3.0.1 (will be removed in v4)
10 | .hide-text() {
11 | font: ~"0/0" a;
12 | color: transparent;
13 | text-shadow: none;
14 | background-color: transparent;
15 | border: 0;
16 | }
17 |
18 | // New mixin to use as of v3.0.1
19 | .text-hide() {
20 | .hide-text();
21 | }
22 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/bootstrap/less/mixins/image.less:
--------------------------------------------------------------------------------
1 | // Image Mixins
2 | // - Responsive image
3 | // - Retina image
4 |
5 |
6 | // Responsive image
7 | //
8 | // Keep images from scaling beyond the width of their parents.
9 | .img-responsive(@display: block) {
10 | display: @display;
11 | max-width: 100%; // Part 1: Set a maximum relative to the parent
12 | height: auto; // Part 2: Scale the height according to the width, otherwise you get stretching
13 | }
14 |
15 |
16 | // Retina image
17 | //
18 | // Short retina mixin for setting background-image and -size. Note that the
19 | // spelling of `min--moz-device-pixel-ratio` is intentional.
20 | .img-retina(@file-1x; @file-2x; @width-1x; @height-1x) {
21 | background-image: url("@{file-1x}");
22 |
23 | @media
24 | only screen and (-webkit-min-device-pixel-ratio: 2),
25 | only screen and ( min--moz-device-pixel-ratio: 2),
26 | only screen and ( -o-min-device-pixel-ratio: 2/1),
27 | only screen and ( min-device-pixel-ratio: 2),
28 | only screen and ( min-resolution: 192dpi),
29 | only screen and ( min-resolution: 2dppx) {
30 | background-image: url("@{file-2x}");
31 | background-size: @width-1x @height-1x;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/bootstrap/less/mixins/labels.less:
--------------------------------------------------------------------------------
1 | // Labels
2 |
3 | .label-variant(@color) {
4 | background-color: @color;
5 |
6 | &[href] {
7 | &:hover,
8 | &:focus {
9 | background-color: darken(@color, 10%);
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/bootstrap/less/mixins/list-group.less:
--------------------------------------------------------------------------------
1 | // List Groups
2 |
3 | .list-group-item-variant(@state; @background; @color) {
4 | .list-group-item-@{state} {
5 | color: @color;
6 | background-color: @background;
7 |
8 | a&,
9 | button& {
10 | color: @color;
11 |
12 | .list-group-item-heading {
13 | color: inherit;
14 | }
15 |
16 | &:hover,
17 | &:focus {
18 | color: @color;
19 | background-color: darken(@background, 5%);
20 | }
21 | &.active,
22 | &.active:hover,
23 | &.active:focus {
24 | color: #fff;
25 | background-color: @color;
26 | border-color: @color;
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/bootstrap/less/mixins/nav-divider.less:
--------------------------------------------------------------------------------
1 | // Horizontal dividers
2 | //
3 | // Dividers (basically an hr) within dropdowns and nav lists
4 |
5 | .nav-divider(@color: #e5e5e5) {
6 | height: 1px;
7 | margin: ((@line-height-computed / 2) - 1) 0;
8 | overflow: hidden;
9 | background-color: @color;
10 | }
11 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/bootstrap/less/mixins/nav-vertical-align.less:
--------------------------------------------------------------------------------
1 | // Navbar vertical align
2 | //
3 | // Vertically center elements in the navbar.
4 | // Example: an element has a height of 30px, so write out `.navbar-vertical-align(30px);` to calculate the appropriate top margin.
5 |
6 | .navbar-vertical-align(@element-height) {
7 | margin-top: ((@navbar-height - @element-height) / 2);
8 | margin-bottom: ((@navbar-height - @element-height) / 2);
9 | }
10 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/bootstrap/less/mixins/opacity.less:
--------------------------------------------------------------------------------
1 | // Opacity
2 |
3 | .opacity(@opacity) {
4 | opacity: @opacity;
5 | // IE8 filter
6 | @opacity-ie: (@opacity * 100);
7 | filter: ~"alpha(opacity=@{opacity-ie})";
8 | }
9 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/bootstrap/less/mixins/pagination.less:
--------------------------------------------------------------------------------
1 | // Pagination
2 |
3 | .pagination-size(@padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {
4 | > li {
5 | > a,
6 | > span {
7 | padding: @padding-vertical @padding-horizontal;
8 | font-size: @font-size;
9 | line-height: @line-height;
10 | }
11 | &:first-child {
12 | > a,
13 | > span {
14 | .border-left-radius(@border-radius);
15 | }
16 | }
17 | &:last-child {
18 | > a,
19 | > span {
20 | .border-right-radius(@border-radius);
21 | }
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/bootstrap/less/mixins/panels.less:
--------------------------------------------------------------------------------
1 | // Panels
2 |
3 | .panel-variant(@border; @heading-text-color; @heading-bg-color; @heading-border) {
4 | border-color: @border;
5 |
6 | & > .panel-heading {
7 | color: @heading-text-color;
8 | background-color: @heading-bg-color;
9 | border-color: @heading-border;
10 |
11 | + .panel-collapse > .panel-body {
12 | border-top-color: @border;
13 | }
14 | .badge {
15 | color: @heading-bg-color;
16 | background-color: @heading-text-color;
17 | }
18 | }
19 | & > .panel-footer {
20 | + .panel-collapse > .panel-body {
21 | border-bottom-color: @border;
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/bootstrap/less/mixins/progress-bar.less:
--------------------------------------------------------------------------------
1 | // Progress bars
2 |
3 | .progress-bar-variant(@color) {
4 | background-color: @color;
5 |
6 | // Deprecated parent class requirement as of v3.2.0
7 | .progress-striped & {
8 | #gradient > .striped();
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/bootstrap/less/mixins/reset-filter.less:
--------------------------------------------------------------------------------
1 | // Reset filters for IE
2 | //
3 | // When you need to remove a gradient background, do not forget to use this to reset
4 | // the IE filter for IE9 and below.
5 |
6 | .reset-filter() {
7 | filter: e(%("progid:DXImageTransform.Microsoft.gradient(enabled = false)"));
8 | }
9 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/bootstrap/less/mixins/reset-text.less:
--------------------------------------------------------------------------------
1 | .reset-text() {
2 | font-family: @font-family-base;
3 | // We deliberately do NOT reset font-size.
4 | font-style: normal;
5 | font-weight: normal;
6 | letter-spacing: normal;
7 | line-break: auto;
8 | line-height: @line-height-base;
9 | text-align: left; // Fallback for where `start` is not supported
10 | text-align: start;
11 | text-decoration: none;
12 | text-shadow: none;
13 | text-transform: none;
14 | white-space: normal;
15 | word-break: normal;
16 | word-spacing: normal;
17 | word-wrap: normal;
18 | }
19 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/bootstrap/less/mixins/resize.less:
--------------------------------------------------------------------------------
1 | // Resize anything
2 |
3 | .resizable(@direction) {
4 | resize: @direction; // Options: horizontal, vertical, both
5 | overflow: auto; // Per CSS3 UI, `resize` only applies when `overflow` isn't `visible`
6 | }
7 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/bootstrap/less/mixins/responsive-visibility.less:
--------------------------------------------------------------------------------
1 | // Responsive utilities
2 |
3 | //
4 | // More easily include all the states for responsive-utilities.less.
5 | .responsive-visibility() {
6 | display: block !important;
7 | table& { display: table !important; }
8 | tr& { display: table-row !important; }
9 | th&,
10 | td& { display: table-cell !important; }
11 | }
12 |
13 | .responsive-invisibility() {
14 | display: none !important;
15 | }
16 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/bootstrap/less/mixins/size.less:
--------------------------------------------------------------------------------
1 | // Sizing shortcuts
2 |
3 | .size(@width; @height) {
4 | width: @width;
5 | height: @height;
6 | }
7 |
8 | .square(@size) {
9 | .size(@size; @size);
10 | }
11 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/bootstrap/less/mixins/tab-focus.less:
--------------------------------------------------------------------------------
1 | // WebKit-style focus
2 |
3 | .tab-focus() {
4 | // Default
5 | outline: thin dotted;
6 | // WebKit
7 | outline: 5px auto -webkit-focus-ring-color;
8 | outline-offset: -2px;
9 | }
10 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/bootstrap/less/mixins/table-row.less:
--------------------------------------------------------------------------------
1 | // Tables
2 |
3 | .table-row-variant(@state; @background) {
4 | // Exact selectors below required to override `.table-striped` and prevent
5 | // inheritance to nested tables.
6 | .table > thead > tr,
7 | .table > tbody > tr,
8 | .table > tfoot > tr {
9 | > td.@{state},
10 | > th.@{state},
11 | &.@{state} > td,
12 | &.@{state} > th {
13 | background-color: @background;
14 | }
15 | }
16 |
17 | // Hover states for `.table-hover`
18 | // Note: this is not available for cells or rows within `thead` or `tfoot`.
19 | .table-hover > tbody > tr {
20 | > td.@{state}:hover,
21 | > th.@{state}:hover,
22 | &.@{state}:hover > td,
23 | &:hover > .@{state},
24 | &.@{state}:hover > th {
25 | background-color: darken(@background, 5%);
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/bootstrap/less/mixins/text-emphasis.less:
--------------------------------------------------------------------------------
1 | // Typography
2 |
3 | .text-emphasis-variant(@color) {
4 | color: @color;
5 | a&:hover,
6 | a&:focus {
7 | color: darken(@color, 10%);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/bootstrap/less/mixins/text-overflow.less:
--------------------------------------------------------------------------------
1 | // Text overflow
2 | // Requires inline-block or block for proper styling
3 |
4 | .text-overflow() {
5 | overflow: hidden;
6 | text-overflow: ellipsis;
7 | white-space: nowrap;
8 | }
9 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/bootstrap/less/pager.less:
--------------------------------------------------------------------------------
1 | //
2 | // Pager pagination
3 | // --------------------------------------------------
4 |
5 |
6 | .pager {
7 | padding-left: 0;
8 | margin: @line-height-computed 0;
9 | list-style: none;
10 | text-align: center;
11 | &:extend(.clearfix all);
12 | li {
13 | display: inline;
14 | > a,
15 | > span {
16 | display: inline-block;
17 | padding: 5px 14px;
18 | background-color: @pager-bg;
19 | border: 1px solid @pager-border;
20 | border-radius: @pager-border-radius;
21 | }
22 |
23 | > a:hover,
24 | > a:focus {
25 | text-decoration: none;
26 | background-color: @pager-hover-bg;
27 | }
28 | }
29 |
30 | .next {
31 | > a,
32 | > span {
33 | float: right;
34 | }
35 | }
36 |
37 | .previous {
38 | > a,
39 | > span {
40 | float: left;
41 | }
42 | }
43 |
44 | .disabled {
45 | > a,
46 | > a:hover,
47 | > a:focus,
48 | > span {
49 | color: @pager-disabled-color;
50 | background-color: @pager-bg;
51 | cursor: @cursor-disabled;
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/bootstrap/less/responsive-embed.less:
--------------------------------------------------------------------------------
1 | // Embeds responsive
2 | //
3 | // Credit: Nicolas Gallagher and SUIT CSS.
4 |
5 | .embed-responsive {
6 | position: relative;
7 | display: block;
8 | height: 0;
9 | padding: 0;
10 | overflow: hidden;
11 |
12 | .embed-responsive-item,
13 | iframe,
14 | embed,
15 | object,
16 | video {
17 | position: absolute;
18 | top: 0;
19 | left: 0;
20 | bottom: 0;
21 | height: 100%;
22 | width: 100%;
23 | border: 0;
24 | }
25 | }
26 |
27 | // Modifier class for 16:9 aspect ratio
28 | .embed-responsive-16by9 {
29 | padding-bottom: 56.25%;
30 | }
31 |
32 | // Modifier class for 4:3 aspect ratio
33 | .embed-responsive-4by3 {
34 | padding-bottom: 75%;
35 | }
36 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/bootstrap/less/thumbnails.less:
--------------------------------------------------------------------------------
1 | //
2 | // Thumbnails
3 | // --------------------------------------------------
4 |
5 |
6 | // Mixin and adjust the regular image class
7 | .thumbnail {
8 | display: block;
9 | padding: @thumbnail-padding;
10 | margin-bottom: @line-height-computed;
11 | line-height: @line-height-base;
12 | background-color: @thumbnail-bg;
13 | border: 1px solid @thumbnail-border;
14 | border-radius: @thumbnail-border-radius;
15 | .transition(border .2s ease-in-out);
16 |
17 | > img,
18 | a > img {
19 | &:extend(.img-responsive);
20 | margin-left: auto;
21 | margin-right: auto;
22 | }
23 |
24 | // Add a hover state for linked versions only
25 | a&:hover,
26 | a&:focus,
27 | a&.active {
28 | border-color: @link-color;
29 | }
30 |
31 | // Image captions
32 | .caption {
33 | padding: @thumbnail-caption-padding;
34 | color: @thumbnail-caption-color;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/bootstrap/less/utilities.less:
--------------------------------------------------------------------------------
1 | //
2 | // Utility classes
3 | // --------------------------------------------------
4 |
5 |
6 | // Floats
7 | // -------------------------
8 |
9 | .clearfix {
10 | .clearfix();
11 | }
12 | .center-block {
13 | .center-block();
14 | }
15 | .pull-right {
16 | float: right !important;
17 | }
18 | .pull-left {
19 | float: left !important;
20 | }
21 |
22 |
23 | // Toggling content
24 | // -------------------------
25 |
26 | // Note: Deprecated .hide in favor of .hidden or .sr-only (as appropriate) in v3.0.1
27 | .hide {
28 | display: none !important;
29 | }
30 | .show {
31 | display: block !important;
32 | }
33 | .invisible {
34 | visibility: hidden;
35 | }
36 | .text-hide {
37 | .text-hide();
38 | }
39 |
40 |
41 | // Hide from screenreaders and browsers
42 | //
43 | // Credit: HTML5 Boilerplate
44 |
45 | .hidden {
46 | display: none;
47 | }
48 |
49 |
50 | // For Affix plugin
51 | // -------------------------
52 |
53 | .affix {
54 | position: fixed;
55 | }
56 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/bootstrap/less/wells.less:
--------------------------------------------------------------------------------
1 | //
2 | // Wells
3 | // --------------------------------------------------
4 |
5 |
6 | // Base class
7 | .well {
8 | min-height: 20px;
9 | padding: 19px;
10 | margin-bottom: 20px;
11 | background-color: @well-bg;
12 | border: 1px solid @well-border;
13 | border-radius: @border-radius-base;
14 | .box-shadow(inset 0 1px 1px rgba(0,0,0,.05));
15 | blockquote {
16 | border-color: #ddd;
17 | border-color: rgba(0,0,0,.15);
18 | }
19 | }
20 |
21 | // Sizes
22 | .well-lg {
23 | padding: 24px;
24 | border-radius: @border-radius-large;
25 | }
26 | .well-sm {
27 | padding: 9px;
28 | border-radius: @border-radius-small;
29 | }
30 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/font-awesome/less/animated.less:
--------------------------------------------------------------------------------
1 | // Animated Icons
2 | // --------------------------
3 |
4 | .@{fa-css-prefix}-spin {
5 | -webkit-animation: fa-spin 2s infinite linear;
6 | animation: fa-spin 2s infinite linear;
7 | }
8 |
9 | .@{fa-css-prefix}-pulse {
10 | -webkit-animation: fa-spin 1s infinite steps(8);
11 | animation: fa-spin 1s infinite steps(8);
12 | }
13 |
14 | @-webkit-keyframes fa-spin {
15 | 0% {
16 | -webkit-transform: rotate(0deg);
17 | transform: rotate(0deg);
18 | }
19 | 100% {
20 | -webkit-transform: rotate(359deg);
21 | transform: rotate(359deg);
22 | }
23 | }
24 |
25 | @keyframes fa-spin {
26 | 0% {
27 | -webkit-transform: rotate(0deg);
28 | transform: rotate(0deg);
29 | }
30 | 100% {
31 | -webkit-transform: rotate(359deg);
32 | transform: rotate(359deg);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/font-awesome/less/bordered-pulled.less:
--------------------------------------------------------------------------------
1 | // Bordered & Pulled
2 | // -------------------------
3 |
4 | .@{fa-css-prefix}-border {
5 | padding: .2em .25em .15em;
6 | border: solid .08em @fa-border-color;
7 | border-radius: .1em;
8 | }
9 |
10 | .@{fa-css-prefix}-pull-left { float: left; }
11 | .@{fa-css-prefix}-pull-right { float: right; }
12 |
13 | .@{fa-css-prefix} {
14 | &.@{fa-css-prefix}-pull-left { margin-right: .3em; }
15 | &.@{fa-css-prefix}-pull-right { margin-left: .3em; }
16 | }
17 |
18 | /* Deprecated as of 4.4.0 */
19 | .pull-right { float: right; }
20 | .pull-left { float: left; }
21 |
22 | .@{fa-css-prefix} {
23 | &.pull-left { margin-right: .3em; }
24 | &.pull-right { margin-left: .3em; }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/font-awesome/less/core.less:
--------------------------------------------------------------------------------
1 | // Base Class Definition
2 | // -------------------------
3 |
4 | .@{fa-css-prefix} {
5 | display: inline-block;
6 | font: normal normal normal @fa-font-size-base/@fa-line-height-base FontAwesome; // shortening font declaration
7 | font-size: inherit; // can't have font-size inherit on line above, so need to override
8 | text-rendering: auto; // optimizelegibility throws things off #1094
9 | -webkit-font-smoothing: antialiased;
10 | -moz-osx-font-smoothing: grayscale;
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/font-awesome/less/fixed-width.less:
--------------------------------------------------------------------------------
1 | // Fixed Width Icons
2 | // -------------------------
3 | .@{fa-css-prefix}-fw {
4 | width: (18em / 14);
5 | text-align: center;
6 | }
7 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/font-awesome/less/font-awesome.less:
--------------------------------------------------------------------------------
1 | /*!
2 | * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome
3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
4 | */
5 |
6 | @import "variables.less";
7 | @import "mixins.less";
8 | @import "path.less";
9 | @import "core.less";
10 | @import "larger.less";
11 | @import "fixed-width.less";
12 | @import "list.less";
13 | @import "bordered-pulled.less";
14 | @import "animated.less";
15 | @import "rotated-flipped.less";
16 | @import "stacked.less";
17 | @import "icons.less";
18 | @import "screen-reader.less";
19 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/font-awesome/less/larger.less:
--------------------------------------------------------------------------------
1 | // Icon Sizes
2 | // -------------------------
3 |
4 | /* makes the font 33% larger relative to the icon container */
5 | .@{fa-css-prefix}-lg {
6 | font-size: (4em / 3);
7 | line-height: (3em / 4);
8 | vertical-align: -15%;
9 | }
10 | .@{fa-css-prefix}-2x { font-size: 2em; }
11 | .@{fa-css-prefix}-3x { font-size: 3em; }
12 | .@{fa-css-prefix}-4x { font-size: 4em; }
13 | .@{fa-css-prefix}-5x { font-size: 5em; }
14 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/font-awesome/less/list.less:
--------------------------------------------------------------------------------
1 | // List Icons
2 | // -------------------------
3 |
4 | .@{fa-css-prefix}-ul {
5 | padding-left: 0;
6 | margin-left: @fa-li-width;
7 | list-style-type: none;
8 | > li { position: relative; }
9 | }
10 | .@{fa-css-prefix}-li {
11 | position: absolute;
12 | left: -@fa-li-width;
13 | width: @fa-li-width;
14 | top: (2em / 14);
15 | text-align: center;
16 | &.@{fa-css-prefix}-lg {
17 | left: (-@fa-li-width + (4em / 14));
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/font-awesome/less/path.less:
--------------------------------------------------------------------------------
1 | /* FONT PATH
2 | * -------------------------- */
3 |
4 | @font-face {
5 | font-family: 'FontAwesome';
6 | src: url('@{fa-font-path}/fontawesome-webfont.eot?v=@{fa-version}');
7 | src: url('@{fa-font-path}/fontawesome-webfont.eot?#iefix&v=@{fa-version}') format('embedded-opentype'),
8 | url('@{fa-font-path}/fontawesome-webfont.woff2?v=@{fa-version}') format('woff2'),
9 | url('@{fa-font-path}/fontawesome-webfont.woff?v=@{fa-version}') format('woff'),
10 | url('@{fa-font-path}/fontawesome-webfont.ttf?v=@{fa-version}') format('truetype'),
11 | url('@{fa-font-path}/fontawesome-webfont.svg?v=@{fa-version}#fontawesomeregular') format('svg');
12 | // src: url('@{fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts
13 | font-weight: normal;
14 | font-style: normal;
15 | font-display: swap;
16 | }
17 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/font-awesome/less/rotated-flipped.less:
--------------------------------------------------------------------------------
1 | // Rotated & Flipped Icons
2 | // -------------------------
3 |
4 | .@{fa-css-prefix}-rotate-90 { .fa-icon-rotate(90deg, 1); }
5 | .@{fa-css-prefix}-rotate-180 { .fa-icon-rotate(180deg, 2); }
6 | .@{fa-css-prefix}-rotate-270 { .fa-icon-rotate(270deg, 3); }
7 |
8 | .@{fa-css-prefix}-flip-horizontal { .fa-icon-flip(-1, 1, 0); }
9 | .@{fa-css-prefix}-flip-vertical { .fa-icon-flip(1, -1, 2); }
10 |
11 | // Hook for IE8-9
12 | // -------------------------
13 |
14 | :root .@{fa-css-prefix}-rotate-90,
15 | :root .@{fa-css-prefix}-rotate-180,
16 | :root .@{fa-css-prefix}-rotate-270,
17 | :root .@{fa-css-prefix}-flip-horizontal,
18 | :root .@{fa-css-prefix}-flip-vertical {
19 | filter: none;
20 | }
21 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/font-awesome/less/screen-reader.less:
--------------------------------------------------------------------------------
1 | // Screen Readers
2 | // -------------------------
3 |
4 | .sr-only { .sr-only(); }
5 | .sr-only-focusable { .sr-only-focusable(); }
6 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/font-awesome/less/stacked.less:
--------------------------------------------------------------------------------
1 | // Stacked Icons
2 | // -------------------------
3 |
4 | .@{fa-css-prefix}-stack {
5 | position: relative;
6 | display: inline-block;
7 | width: 2em;
8 | height: 2em;
9 | line-height: 2em;
10 | vertical-align: middle;
11 | }
12 | .@{fa-css-prefix}-stack-1x, .@{fa-css-prefix}-stack-2x {
13 | position: absolute;
14 | left: 0;
15 | width: 100%;
16 | text-align: center;
17 | }
18 | .@{fa-css-prefix}-stack-1x { line-height: inherit; }
19 | .@{fa-css-prefix}-stack-2x { font-size: 2em; }
20 | .@{fa-css-prefix}-inverse { color: @fa-inverse; }
21 |
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/js/plugins/visibility.js:
--------------------------------------------------------------------------------
1 | /*! jquery-visibility v1.0.12 | MIT license | http://mths.be/visibility */
2 | !function (e, i) { "function" == typeof define && define.amd ? define(["jquery"], function (t) { return i(e, t) }) : "object" == typeof exports ? module.exports = i(e, require("jquery")) : i(e, jQuery) }(this, function (e, i, t) { "use strict"; function o() { "hidden" !== n && (r.hidden = f.pageVisibility ? r[n] : t) } for (var n, u, r = e.document, s = ["webkit", "o", "ms", "moz", ""], f = i.support || {}, c = ("onfocusin" in r && "hasFocus" in r ? "focusin focusout" : "focus blur") ; (u = s.pop()) !== t;) if (n = (u ? u + "H" : "h") + "idden", f.pageVisibility = r[n] !== t, f.pageVisibility) { c = u + "visibilitychange"; break } o(), i(/blur$/.test(c) ? e : r).on(c, function (e) { var u = e.type, s = e.originalEvent; if (s) { var f = s.toElement; (!/^focus./.test(u) || f === t && s.fromElement === t && s.relatedTarget === t) && i(r).triggerHandler(n && r[n] || /^(?:blur|focusout)$/.test(u) ? "hide" : "show"), o() } }) });
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/themes/_shared/animations.less:
--------------------------------------------------------------------------------
1 | .spin, .hover-spin:hover .fa {
2 | -webkit-animation: spin 2s infinite linear;
3 | -moz-animation: spin 2s infinite linear;
4 | -o-animation: spin 2s infinite linear;
5 | animation: spin 2s infinite linear;
6 | }
7 | @-moz-keyframes spin {
8 | 0% { -moz-transform: rotate(0deg); }
9 | 100% { -moz-transform: rotate(359deg); }
10 | }
11 | @-webkit-keyframes spin {
12 | 0% { -webkit-transform: rotate(0deg); }
13 | 100% { -webkit-transform: rotate(359deg); }
14 | }
15 | @-o-keyframes spin {
16 | 0% { -o-transform: rotate(0deg); }
17 | 100% { -o-transform: rotate(359deg); }
18 | }
19 | @keyframes spin {
20 | 0% {
21 | -webkit-transform: rotate(0deg);
22 | transform: rotate(0deg);
23 | }
24 | 100% {
25 | -webkit-transform: rotate(359deg);
26 | transform: rotate(359deg);
27 | }
28 | }
29 |
30 | .hover-pulsate:not(.fa-spin):hover .fa:not(.fa-spin), .hover-pulsate.fa:not(.fa-spin):not(.disabled):hover {
31 | -webkit-animation: pulsate 2s infinite linear;
32 | -moz-animation: pulsate 2s infinite linear;
33 | -o-animation: pulsate 2s infinite linear;
34 | animation: pulsate 2s infinite linear;
35 | }
36 |
37 | @-webkit-keyframes pulsate {
38 | 0% {
39 | -webkit-transform: scale(1.0, 1.0);
40 | }
41 |
42 | 50% {
43 | -webkit-transform: scale(0.8, 0.8);
44 | }
45 |
46 | 100% {
47 | -webkit-transform: scale(1.0, 1.0);
48 | }
49 | }
--------------------------------------------------------------------------------
/src/Opserver.Web/ContentSource/themes/light.less:
--------------------------------------------------------------------------------
1 | // Based on Bootswatch: Yeti - https://bootswatch.com/yeti/
2 | @import "_shared.less";
3 |
4 | @gray-light: rgba(78,93,108,0.2);
5 | @label-muted-color: #999999;
6 | @value-block-text-color: black;
7 | //@value-block-background-color: rgba(0,0,0,0.018);
8 | @value-block-border-color: #f9f9f9;
9 | @value-block-progress-background-color: #666;
10 |
11 | @panel-inverse-heading-bg: white;
12 |
13 | @chart-area-blue: #008cba;
14 |
15 | @axis-color: #444;
16 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Controllers/AdminController.cs:
--------------------------------------------------------------------------------
1 | using StackExchange.Exceptional;
2 | using Opserver.Helpers;
3 | using Opserver.Models;
4 | using System.Threading.Tasks;
5 | using Microsoft.AspNetCore.Mvc;
6 | using Microsoft.Extensions.Options;
7 |
8 | namespace Opserver.Controllers
9 | {
10 | [OnlyAllow(Roles.GlobalAdmin)]
11 | public class AdminController : StatusController
12 | {
13 | public AdminController(IOptions _settings) : base(_settings) {}
14 |
15 | [Route("admin/security/purge-cache")]
16 | public ActionResult Dashboard()
17 | {
18 | Current.Security.PurgeCache();
19 | return TextPlain("Cache Purged");
20 | }
21 |
22 | ///
23 | /// Access our error log.
24 | ///
25 | [Route("admin/errors/{resource?}/{subResource?}"), AlsoAllow(Roles.LocalRequest)]
26 | public Task InvokeErrorHandler() => ExceptionalMiddleware.HandleRequestAsync(HttpContext);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Controllers/CloudflareController.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Microsoft.AspNetCore.Mvc;
3 | using Microsoft.Extensions.Options;
4 | using Opserver.Data.Cloudflare;
5 | using Opserver.Helpers;
6 | using Opserver.Views.Cloudflare;
7 |
8 | namespace Opserver.Controllers
9 | {
10 | [OnlyAllow(CloudflareRoles.Viewer)]
11 | public class CloudflareController : StatusController
12 | {
13 | public CloudflareController(CloudflareModule module, IOptions settings) : base(module, settings) { }
14 |
15 | [DefaultRoute("cloudflare")]
16 | public ActionResult Dashboard() => RedirectToAction(nameof(DNS));
17 |
18 | [Route("cloudflare/dns")]
19 | public async Task DNS()
20 | {
21 | await Module.API.PollAsync();
22 | var vd = new DNSModel
23 | {
24 | View = DashboardModel.Views.DNS,
25 | Zones = Module.API.Zones.SafeData(true),
26 | DNSRecords = Module.API.DNSRecords.Data,
27 | DataCenters = Module.AllDatacenters
28 | };
29 | return View(vd);
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Controllers/DashboardController.Admin.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Opserver.Helpers;
3 | using Opserver.Data.Dashboard;
4 | using Microsoft.AspNetCore.Mvc;
5 |
6 | namespace Opserver.Controllers
7 | {
8 | public partial class DashboardController
9 | {
10 | [Route("dashboard/node/service/action"), HttpPost, OnlyAllow(DashboardRoles.Admin)]
11 | public async Task ControlService(string node, string name, NodeService.Action serviceAction)
12 | {
13 | var n = Module.GetNodeByName(node);
14 | var s = n.GetService(name);
15 | var result = Json(await s.Update(serviceAction));
16 | await n.DataProvider.PollAsync(true);
17 | return result;
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Controllers/DashboardController.Polling.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System.Threading.Tasks;
3 | using Microsoft.AspNetCore.Mvc;
4 |
5 | namespace Opserver.Controllers
6 | {
7 | public partial class DashboardController
8 | {
9 | [ResponseCache(Duration = 1, VaryByQueryKeys = new string[] { "id", "node" }, Location = ResponseCacheLocation.Client)]
10 | [Route("dashboard/node/poll/cpu")]
11 | public async Task PollCPU(string id = null, string node = null)
12 | {
13 | var n = id.HasValue() ? Module.GetNodeById(id) : Module.GetNodeByName(node);
14 | if (n == null)
15 | return JsonNotFound();
16 |
17 | var data = await n.GetCPUUtilization();
18 | if (data?.Data == null)
19 | return JsonNotFound();
20 |
21 | var total = data.Data.Find(c => c.Name == "Total");
22 |
23 | return Json(new
24 | {
25 | duration = data.Duration.TotalMilliseconds,
26 | cores = data.Data.Where(c => c != total).Select(c => new
27 | {
28 | name = c.Name,
29 | utilization = c.Utilization
30 | }),
31 | total = total?.Utilization ?? 0
32 | });
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Controllers/DefaultRoute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Microsoft.AspNetCore.Mvc;
4 |
5 | namespace Opserver.Controllers
6 | {
7 | [AttributeUsage(AttributeTargets.Method)]
8 | public sealed class DefaultRoute : RouteAttribute
9 | {
10 | private static Dictionary AllRoutes => new Dictionary();
11 |
12 | public DefaultRoute(string template) : base(template) { }
13 |
14 | public static DefaultRoute GetFor(Type t) => AllRoutes.TryGetValue(t, out var route) ? route : null;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Controllers/GraphController.cs:
--------------------------------------------------------------------------------
1 | namespace Opserver.Controllers
2 | {
3 | public partial class GraphController : StatusController
4 | {
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Controllers/HAProxyController.Admin.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System.Threading.Tasks;
3 | using Microsoft.AspNetCore.Mvc;
4 | using Opserver.Data.HAProxy;
5 | using Opserver.Helpers;
6 |
7 | namespace Opserver.Controllers
8 | {
9 | public partial class HAProxyController
10 | {
11 | [Route("haproxy/admin/action"), HttpPost, OnlyAllow(HAProxyRoles.Admin)]
12 | public async Task HAProxyAdminProxy(string group, string proxy, string server, Action act)
13 | {
14 | // Entire server
15 | if (proxy.IsNullOrEmpty() && group.IsNullOrEmpty() && server.HasValue())
16 | return Json(await Module.Admin.PerformServerActionAsync(server, act));
17 | // Entire group
18 | if (proxy.IsNullOrEmpty() && server.IsNullOrEmpty() && group.HasValue())
19 | return Json(await Module.Admin.PerformGroupActionAsync(group, act));
20 |
21 | var haGroup = Module.GetGroup(group);
22 | var proxies = (haGroup != null ? haGroup.GetProxies() : Module.GetAllProxies()).Where(pr => pr.Name == proxy);
23 |
24 | return Json(await Module.Admin.PerformProxyActionAsync(proxies, server, act));
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Controllers/HAProxyController.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Microsoft.AspNetCore.Mvc;
3 | using Microsoft.Extensions.Options;
4 | using Opserver.Data.HAProxy;
5 | using Opserver.Helpers;
6 | using Opserver.Views.HAProxy;
7 |
8 | namespace Opserver.Controllers
9 | {
10 | [OnlyAllow(HAProxyRoles.Viewer)]
11 | public partial class HAProxyController : StatusController
12 | {
13 | public HAProxyController(HAProxyModule module, IOptions settings) : base(module, settings) { }
14 |
15 | [DefaultRoute("haproxy")]
16 | public ActionResult Dashboard(string group, string node, string watch = null, bool norefresh = false)
17 | {
18 | var haGroup = Module.GetGroup(group ?? node);
19 | var proxies = haGroup != null ? haGroup.GetProxies() : Module.GetAllProxies();
20 | proxies.RemoveAll(p => !p.HasServers);
21 |
22 | var vd = new HAProxyModel
23 | {
24 | SelectedGroup = haGroup,
25 | Groups = haGroup != null ? new List { haGroup } : Module.Groups,
26 | Proxies = proxies,
27 | View = HAProxyModel.Views.Dashboard,
28 | Refresh = !norefresh,
29 | WatchProxy = watch
30 | };
31 | return View("HAProxy.Dashboard", vd);
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Controllers/HubController.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Mvc;
2 | using Microsoft.Extensions.Options;
3 | using Opserver.Data.Dashboard;
4 | using Opserver.Helpers;
5 | using Opserver.Models;
6 | using Opserver.Views.Hub;
7 |
8 | namespace Opserver.Controllers
9 | {
10 | [OnlyAllow(Roles.Authenticated)]
11 | public class HubController : StatusController
12 | {
13 | public HubController(DashboardModule module, IOptions settings) : base(module, settings) { }
14 |
15 | [Route("hub"), Route("headsup"), AlsoAllow(Roles.InternalRequest)]
16 | public ActionResult Index()
17 | {
18 | var vd = new HubModel();
19 | return View(vd);
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Controllers/MiscController.cs:
--------------------------------------------------------------------------------
1 | using Opserver.Views.Shared;
2 | using Microsoft.AspNetCore.Mvc;
3 | using System.Net;
4 | using Microsoft.Extensions.Options;
5 | using Microsoft.AspNetCore.Authorization;
6 | using Opserver.Helpers;
7 | using Opserver.Models;
8 |
9 | namespace Opserver.Controllers
10 | {
11 | [AlsoAllow(Roles.Anonymous)]
12 | public class MiscController : StatusController
13 | {
14 | public MiscController(IOptions _settings) : base(_settings) { }
15 |
16 | [Route("no-config")]
17 | public ViewResult NoConfig() => View("NoConfiguration");
18 |
19 | [Route("404")]
20 | public ViewResult PageNotFound(string title = null, string message = null)
21 | {
22 | Response.StatusCode = (int)HttpStatusCode.NotFound;
23 |
24 | var vd = new PageNotFoundModel
25 | {
26 | Title = title,
27 | Message = message
28 | };
29 | return View("PageNotFound", vd);
30 | }
31 |
32 | [AllowAnonymous]
33 | [Route("denied")]
34 | public ActionResult AccessDenied()
35 | {
36 | Response.StatusCode = (int)HttpStatusCode.Forbidden;
37 | return View("~/Views/Shared/AccessDenied.cshtml");
38 | }
39 |
40 | [Route("error")]
41 | public ActionResult ErrorPage()
42 | {
43 | Response.StatusCode = (int)HttpStatusCode.InternalServerError;
44 | return View("Error");
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Controllers/PollController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Threading.Tasks;
4 | using Microsoft.AspNetCore.Mvc;
5 | using Microsoft.Extensions.Options;
6 | using Opserver.Data;
7 | using Opserver.Helpers;
8 | using Opserver.Models;
9 |
10 | namespace Opserver.Controllers
11 | {
12 | [OnlyAllow(Roles.Authenticated)]
13 | public class PollController : StatusController
14 | {
15 | private PollingService Poller { get; }
16 |
17 | public PollController(IOptions _settings, PollingService poller) : base(_settings) => Poller = poller;
18 |
19 | [Route("poll")]
20 | public async Task PollNodes(string type, string[] key, Guid? guid = null)
21 | {
22 | if (type.IsNullOrEmpty())
23 | return JsonError("type is missing");
24 | if (!(key?.Any() ?? false))
25 | return JsonError("key is missing");
26 | try
27 | {
28 | var polls = key.Select(k => Poller.PollAsync(type, k, guid));
29 | var results = await Task.WhenAll(polls);
30 | return Json(results.Aggregate(true, (current, r) => current && r));
31 | }
32 | catch (Exception e)
33 | {
34 | return JsonError("Error polling node: " + e.Message);
35 | }
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Helpers/DataKeys.cs:
--------------------------------------------------------------------------------
1 | namespace Opserver.Helpers
2 | {
3 | ///
4 | /// Contains keys for common ViewData collection values
5 | ///
6 | public static class ViewDataKeys
7 | {
8 | public const string PageTitle = nameof(PageTitle);
9 | public const string TopBoxOptions = nameof(TopBoxOptions);
10 | }
11 | }
--------------------------------------------------------------------------------
/src/Opserver.Web/Helpers/MiniProfilerCacheStorage.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Caching.Memory;
2 | using Microsoft.Extensions.Options;
3 | using Opserver.Data;
4 | using StackExchange.Profiling;
5 | using StackExchange.Profiling.Storage;
6 | using System;
7 | using System.Threading.Tasks;
8 |
9 | namespace Opserver.Helpers
10 | {
11 | public class MiniProfilerCacheStorage : MemoryCacheStorage, IAsyncStorage
12 | {
13 | private readonly PollingService _poller;
14 | public MiniProfilerCacheStorage(IMemoryCache cache, PollingService poller, TimeSpan cacheDuration) : base(cache, cacheDuration)
15 | {
16 | _poller = poller;
17 | }
18 |
19 | MiniProfiler IAsyncStorage.Load(Guid id) => Load(id) ?? _poller.GetCache(id)?.Profiler;
20 | Task IAsyncStorage.LoadAsync(Guid id) => LoadAsync(id) ?? Task.FromResult(_poller.GetCache(id)?.Profiler);
21 | }
22 |
23 | internal class MiniProfilerCacheStorageDefaults : IConfigureOptions
24 | {
25 | private readonly IMemoryCache _cache;
26 | private readonly PollingService _poller;
27 | public MiniProfilerCacheStorageDefaults(IMemoryCache cache, PollingService poller)
28 | {
29 | _cache = cache;
30 | _poller = poller;
31 | }
32 |
33 | public void Configure(MiniProfilerOptions options)
34 | {
35 | if (options.Storage == null)
36 | {
37 | options.Storage = new MiniProfilerCacheStorage(_cache, _poller, TimeSpan.FromMinutes(10));
38 | }
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Helpers/TestItem.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Opserver.Data;
3 |
4 | namespace Opserver.Helpers
5 | {
6 | public sealed class TestItem : IMonitorStatus
7 | {
8 | public static readonly TestItem Good = new TestItem(MonitorStatus.Good);
9 | public static readonly TestItem Warning = new TestItem(MonitorStatus.Warning);
10 | public static readonly TestItem Maintenance = new TestItem(MonitorStatus.Maintenance);
11 | public static readonly TestItem Critical = new TestItem(MonitorStatus.Critical);
12 | public static readonly TestItem Unknown = new TestItem(MonitorStatus.Unknown);
13 |
14 | public MonitorStatus MonitorStatus { get; }
15 | public string MonitorStatusReason { get; }
16 |
17 | private TestItem(MonitorStatus status)
18 | {
19 | MonitorStatus = status;
20 | MonitorStatusReason = status.ToString();
21 | }
22 |
23 | public static IEnumerable Batch(int good = 0, int warning = 0, int maintenance = 0, int critical = 0, int unknown = 0)
24 | {
25 | for (var i = 0; i < good; i++)
26 | yield return Good;
27 | for (var i = 0; i < warning; i++)
28 | yield return Warning;
29 | for (var i = 0; i < maintenance; i++)
30 | yield return Maintenance;
31 | for (var i = 0; i < critical; i++)
32 | yield return Critical;
33 | for (var i = 0; i < unknown; i++)
34 | yield return Unknown;
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Helpers/Theme.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Microsoft.AspNetCore.Http;
4 |
5 | namespace Opserver.Helpers
6 | {
7 | public static class Theme
8 | {
9 | public static List Options { get; } = new List { "light", "dark" };
10 |
11 | public const string Default = "light";
12 | private const string CookieName = "Op-Theme";
13 |
14 | // Cookies need an expiration date! A sliding time period seems downright silly so...
15 | // I chose at random from https://en.wikipedia.org/wiki/List_of_dates_predicted_for_apocalyptic_events
16 | // "Members predict that the world will end in 2026, when an asteroid would collide with Earth..."
17 | private static readonly DateTime CookieExpirationDate = new DateTime(2026, 1, 1);
18 |
19 | public static string Get(HttpRequest request) => request.Cookies[CookieName] ?? Default;
20 |
21 | public static void Set(string theme, HttpResponse response)
22 | {
23 | if (Options.Contains(theme))
24 | {
25 | response.Cookies.Append(CookieName, theme, new CookieOptions() { Expires = CookieExpirationDate });
26 | }
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "http://localhost:56057/",
7 | "sslPort": 0
8 | }
9 | },
10 | "profiles": {
11 | "Opserver.Web": {
12 | "commandName": "Project",
13 | "launchBrowser": true,
14 | "environmentVariables": {
15 | "ASPNETCORE_ENVIRONMENT": "Development"
16 | },
17 | "applicationUrl": "http://localhost:56058/"
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/src/Opserver.Web/Security/ActiveDirectorySecuritySettings.cs:
--------------------------------------------------------------------------------
1 | namespace Opserver.Security
2 | {
3 | ///
4 | /// Security settings for configuring the .
5 | ///
6 | public class ActiveDirectorySecuritySettings : SecuritySettings
7 | {
8 | ///
9 | /// Gets or sets the server to bind to in order to authenticate users.
10 | ///
11 | public string Server { get; set; }
12 | ///
13 | /// Gets or sets the username used to bind to AD in order to authenticate users.
14 | ///
15 | public string AuthUser { get; set; }
16 | ///
17 | /// Gets or sets the password used to bind to AD in order to authenticate users.
18 | ///
19 | public string AuthPassword { get; set; }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Security/EveryonesAnAdminProvider.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Security.Claims;
3 | using Opserver.Models;
4 |
5 | namespace Opserver.Security
6 | {
7 | ///
8 | /// Does this REALLY need an explanation?
9 | ///
10 | public class EveryonesAnAdminProvider : SecurityProvider
11 | {
12 | public override string ProviderName => "Everyone's an Admin!";
13 | public override SecurityProviderFlowType FlowType => SecurityProviderFlowType.Username;
14 | public EveryonesAnAdminProvider(SecuritySettings settings) : base(settings) { }
15 |
16 | internal override bool InAdminGroups(User user, StatusModule module) => true;
17 | protected override bool InGroupsCore(User user, string[] groupNames) => true;
18 |
19 | protected override bool TryValidateToken(UserNamePasswordToken token, out ClaimsPrincipal claimsPrincipal)
20 | {
21 | claimsPrincipal = CreateNamedPrincipal(token.UserName);
22 | return true;
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Security/EveryonesReadOnlyProvider.cs:
--------------------------------------------------------------------------------
1 | using System.Security.Claims;
2 | using Opserver.Models;
3 |
4 | namespace Opserver.Security
5 | {
6 | ///
7 | /// Does this REALLY need an explanation?
8 | ///
9 | public class EveryonesReadOnlyProvider : SecurityProvider
10 | {
11 | public override string ProviderName => "Everyone's Read-only";
12 | public override SecurityProviderFlowType FlowType => SecurityProviderFlowType.Username;
13 | public EveryonesReadOnlyProvider(SecuritySettings settings) : base(settings) { }
14 |
15 | internal override bool InAdminGroups(User user, StatusModule module) => false;
16 | internal override bool InReadGroups(User user, StatusModule module) => true;
17 | protected override bool TryValidateToken(UserNamePasswordToken token, out ClaimsPrincipal claimsPrincipal)
18 | {
19 | claimsPrincipal = CreateNamedPrincipal(token.UserName);
20 | return true;
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Security/ISecurityProviderToken.cs:
--------------------------------------------------------------------------------
1 | namespace Opserver.Security
2 | {
3 | ///
4 | /// Marker interface used to indicate a class is a token consumed by a .
5 | ///
6 | public interface ISecurityProviderToken
7 | {
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Security/OIDCToken.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Security.Claims;
4 |
5 | namespace Opserver.Security
6 | {
7 | ///
8 | /// that wraps the claims retrieved from an OpenId Connect login flow.
9 | ///
10 | public class OIDCToken : ISecurityProviderToken
11 | {
12 | public OIDCToken(IEnumerable claims)
13 | {
14 | Claims = claims ?? throw new ArgumentNullException(nameof(claims));
15 | }
16 |
17 | ///
18 | /// Gets the claims retrieved as a result of an OpenId Connect login flow.
19 | ///
20 | public IEnumerable Claims { get; }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Security/SecurityProviderFlowType.cs:
--------------------------------------------------------------------------------
1 | namespace Opserver.Security
2 | {
3 | ///
4 | /// Types of login flows supported by a .
5 | ///
6 | public enum SecurityProviderFlowType
7 | {
8 | None,
9 | Username,
10 | UsernamePassword,
11 | OIDC,
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Security/UnconfiguredProvider.cs:
--------------------------------------------------------------------------------
1 | using System.Security.Claims;
2 |
3 | namespace Opserver.Security
4 | {
5 | ///
6 | /// Used when no valid configuration is present, as a default to slam the door
7 | /// If people want read-all access...it should be explicit. Default is no access.
8 | ///
9 | public class UnconfiguredProvider : SecurityProvider
10 | {
11 | public override string ProviderName => "Unconfigured";
12 | public override SecurityProviderFlowType FlowType => SecurityProviderFlowType.None;
13 | public override bool IsConfigured => false;
14 |
15 | public UnconfiguredProvider(SecuritySettings settings) : base(settings) { }
16 |
17 | public override bool TryValidateToken(ISecurityProviderToken token, out ClaimsPrincipal claimsPrincipal)
18 | {
19 | claimsPrincipal = CreateAnonymousPrincipal();
20 | return false;
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Security/UserNamePasswordToken.cs:
--------------------------------------------------------------------------------
1 | namespace Opserver.Security
2 | {
3 | public class UserNamePasswordToken : ISecurityProviderToken
4 | {
5 | public UserNamePasswordToken(string userName, string password)
6 | {
7 | UserName = userName;
8 | Password = password;
9 | }
10 |
11 | public string UserName { get; }
12 | public string Password { get; }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Auth/Login.Model.cs:
--------------------------------------------------------------------------------
1 | namespace Opserver.Views.Login
2 | {
3 | public class LoginModel
4 | {
5 | public string Message { get; set; }
6 | public string ErrorMessage { get; set; }
7 | }
8 | }
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/CloudFlare/DNS.Model.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Opserver.Data;
3 | using Opserver.Data.Cloudflare;
4 |
5 | namespace Opserver.Views.Cloudflare
6 | {
7 | public class DNSModel : DashboardModel
8 | {
9 | public override Views View => Views.DNS;
10 |
11 | public Dictionary> DataCenters { get; set; }
12 | public List Zones { get; set; }
13 | public List DNSRecords { get; set; }
14 | }
15 | }
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/CloudFlare/Dashboard.Model.cs:
--------------------------------------------------------------------------------
1 | namespace Opserver.Views.Cloudflare
2 | {
3 | public class DashboardModel
4 | {
5 | public virtual Views View { get; set; }
6 |
7 | public enum Views
8 | {
9 | Overview = 0,
10 | DNS = 1,
11 | Analytics = 2
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/CloudFlare/Dashboard.cshtml:
--------------------------------------------------------------------------------
1 | @model DashboardModel
2 | @{
3 | Layout = "~/Views/Shared/Master.cshtml";
4 | this.SetPageTitle("Cloudflare Dashboard");
5 | }
6 | @section head {
7 |
12 | }
13 | @functions
14 | {
15 | string TabLink(DashboardModel.Views view, string action, string name)
16 | {
17 | @name
18 | return null;
19 | }
20 | }
21 |
22 | @TabLink(DashboardModel.Views.DNS, nameof(CloudflareController.DNS), "DNS")
23 |
24 | @RenderBody()
25 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/CloudFlare/_ViewImports.cshtml:
--------------------------------------------------------------------------------
1 | @using Opserver.Data.Cloudflare
2 | @using Opserver.Views.Cloudflare
3 | @inject CloudflareModule Module
4 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/CloudFlare/_ViewStart.cshtml:
--------------------------------------------------------------------------------
1 | @{
2 | Layout = this.IsAjaxRequest() ? null : "Dashboard.cshtml";
3 | }
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Dashboard/CurrentStatusTypes.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel;
2 |
3 | namespace Opserver.Views.Dashboard
4 | {
5 | public enum CurrentStatusTypes
6 | {
7 | [Description("None")]
8 | None = 0,
9 | [Description("Stats")]
10 | Stats = 1,
11 | [Description("Interfaces")]
12 | Interfaces = 2,
13 | [Description("VM Info")]
14 | VMHost = 3,
15 | [Description("Elastic")]
16 | Elastic = 4,
17 | [Description("HAProxy")]
18 | HAProxy = 5,
19 | [Description("SQL Instance")]
20 | SQLInstance = 6,
21 | [Description("Active SQL")]
22 | SQLActive = 7,
23 | [Description("Top SQL")]
24 | SQLTop = 8,
25 | [Description("Redis Info")]
26 | Redis = 9
27 | }
28 | }
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Dashboard/Dashboard.Model.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Opserver.Data.Dashboard;
3 |
4 | namespace Opserver.Views.Dashboard
5 | {
6 | public class DashboardModel
7 | {
8 | public string Filter { get; set; }
9 | public List ErrorMessages { get; set; }
10 | public List Nodes { get; set; }
11 | public bool IsStartingUp { get; set; }
12 | }
13 | }
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Dashboard/Dashboard.cshtml:
--------------------------------------------------------------------------------
1 | @model DashboardModel
2 | @{
3 | Layout = "~/Views/Shared/Master.cshtml";
4 | this.SetPageTitle("Dashboard");
5 |
6 | this.SetTopSearch("All Nodes",
7 | Model.Filter,
8 | Url.Action(nameof(DashboardController.Dashboard)));
9 | }
10 | @section head {
11 | }
16 | @section preload {
17 | @RenderSection("preload", false)
18 | }
19 |
20 | @RenderBody()
21 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Dashboard/Node.Graph.Model.cs:
--------------------------------------------------------------------------------
1 | using Opserver.Data.Dashboard;
2 |
3 | namespace Opserver.Views.Dashboard
4 | {
5 | public class NodeGraphModel
6 | {
7 | public Node Node { get; set; }
8 | public string Title { get; set; }
9 | public string Type { get; set; }
10 | public Interface Interface { get; set; }
11 | public Volume Volume { get; set; }
12 |
13 | public bool IsLive => Type == KnownTypes.Live;
14 |
15 | public object CpuData { get; set; }
16 | public object MemoryData { get; set; }
17 | public object NetworkData { get; set; }
18 | public object VolumeData { get; set; }
19 | public object VolumePerformanceData { get; set; }
20 |
21 | public static class KnownTypes
22 | {
23 | public const string CPU = "cpu";
24 | public const string Memory = "memory";
25 | public const string Network = "network";
26 | public const string Volume = "volume";
27 | public const string VolumePerformance = "volumePerformance";
28 | public const string Live = "live";
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Dashboard/Node.Model.cs:
--------------------------------------------------------------------------------
1 | using Opserver.Data.Dashboard;
2 |
3 | namespace Opserver.Views.Dashboard
4 | {
5 | public class NodeModel
6 | {
7 | public Node CurrentNode { get; set; }
8 | public CurrentStatusTypes? CurrentStatusType { get; set; }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Dashboard/Node.Stats.cshtml:
--------------------------------------------------------------------------------
1 | @model Node
2 |
3 |
4 |
5 |
6 | CPU Utilization
7 |
8 |
11 |
12 |
13 |
14 |
15 |
Memory Utilization (@(Model.TotalMemory.GetValueOrDefault(0).ToSize()))
16 |
19 |
20 |
21 |
22 |
28 | @if (Model.Interfaces.Any())
29 | {
30 |
31 | }
32 |
33 | @if (Model.Services?.Any() == true)
34 | {
35 |
36 | }
37 |
38 | @if (Module.Settings.ShowVolumePerformance && Model.Volumes.Any())
39 | {
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Dashboard/Node.VMHost.cshtml:
--------------------------------------------------------------------------------
1 | @model Node
2 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Dashboard/_ViewImports.cshtml:
--------------------------------------------------------------------------------
1 | @using Opserver.Data.Dashboard
2 | @using Opserver.Views.Dashboard
3 | @inject DashboardModule Module
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Elastic/Cluster.Indexes.cshtml:
--------------------------------------------------------------------------------
1 | @model DashboardModel
2 |
3 | @Model.CurrentCluster.Name Index Details
4 |
5 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Elastic/Cluster.Selector.cshtml:
--------------------------------------------------------------------------------
1 | @{
2 | var clusters = Module.Clusters;
3 | }
4 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Elastic/Cluster.Shards.cshtml:
--------------------------------------------------------------------------------
1 | @model DashboardModel
2 | @{
3 | var c = Model.CurrentCluster;
4 | var i = Model.CurrentIndexName;
5 | var shards = Model.DisplayShards.Where(s => i == null || s.Index == i).ToList();
6 | }
7 |
8 | @shards.Count.ToComma() Shards @(Model.DisplayMode == DashboardModel.DisplayModes.WarningsOnly ? "in trouble" : "") on @c.HealthStatus.Data.TotalNodeCount.Pluralize("node") (@c.HealthStatus.Data.Name)
9 |
10 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Elastic/Indexes.cshtml:
--------------------------------------------------------------------------------
1 | @model DashboardModel
2 | @{
3 | var c = Model.CurrentCluster;
4 | if (c == null) { return; }
5 | var indexes = c.HealthStatus.Data.Indexes.Values.ToList();
6 | }
7 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Elastic/Node.Selector.cshtml:
--------------------------------------------------------------------------------
1 | @{
2 | var clusters = Module.Clusters;
3 | }
4 |
5 |
6 |
7 | @foreach (var c in clusters)
8 | {
9 | var nodes = c.Nodes.SafeData(true).Nodes;
10 | if (nodes == null || !nodes.Any()) { continue; }
11 |
26 | }
27 |
28 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Elastic/Node.Settings.cshtml:
--------------------------------------------------------------------------------
1 | @using Jil
2 | @model DashboardModel
3 | @{
4 | var settings = Model.CurrentNode?.Settings ?? new Dictionary();
5 | }
6 |
7 | Node Settings
8 |
9 | @if (settings.Any())
10 | {
11 |
12 |
13 |
14 | Setting
15 | Value
16 |
17 |
18 |
19 | @foreach (var s in settings.OrderBy(s => s.Key))
20 | {
21 |
22 | @s.Key
23 | @JSON.Serialize(s.Value, Options.ISO8601PrettyPrint)
24 |
25 | }
26 |
27 |
28 | }
29 | else
30 | {
31 | No settings found
32 | }
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Elastic/_ViewImports.cshtml:
--------------------------------------------------------------------------------
1 | @using Opserver.Data.Elastic
2 | @using Opserver.Views.Elastic
3 | @inject ElasticModule Module
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Elastic/_ViewStart.cshtml:
--------------------------------------------------------------------------------
1 | @{
2 | Layout = this.IsAjaxRequest() ? null : "Dashboard.cshtml";
3 | }
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Exceptions/Exceptions.Jira.cshtml:
--------------------------------------------------------------------------------
1 | @model List
2 | @if (Model != null && Model.Count > 0)
3 | {
4 |
5 | Jira Actions
6 | @foreach (var action in Model)
7 | {
8 |
9 | ( @action.Caption )
10 |
11 | }
12 |
13 |
14 |
15 | Browse in Jira
16 |
17 | }
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Exceptions/_ViewImports.cshtml:
--------------------------------------------------------------------------------
1 | @using Opserver.Data.Exceptions
2 | @using Opserver.Views.Exceptions
3 | @inject ExceptionsModule Module
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/HAProxy/_ViewImports.cshtml:
--------------------------------------------------------------------------------
1 | @using Opserver.Data.HAProxy
2 | @using Opserver.Views.HAProxy
3 | @inject HAProxyModule Module
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Home/About.Model.cs:
--------------------------------------------------------------------------------
1 | namespace Opserver.Views.Home
2 | {
3 | public class AboutModel
4 | {
5 | public bool AutoRefresh { get; set; }
6 | public string Filter { get; set; }
7 | }
8 | }
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Hub/HubModel.cs:
--------------------------------------------------------------------------------
1 | namespace Opserver.Views.Hub
2 | {
3 | public class HubModel
4 | {
5 | }
6 | }
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Hub/Index.Dashboard.cshtml:
--------------------------------------------------------------------------------
1 | @using Opserver.Data.Dashboard
2 | @inject DashboardModule Dashboard
3 | @{
4 | var nodes = Dashboard.AllNodes;
5 | var groups = nodes.GroupBy(n => n.Category).OrderBy(g => g.Key.Index).ToList();
6 | }
7 |
8 |
@nodes.GetWorstStatus().IconSpan() @nodes.Count.Pluralize("Node")
9 |
10 |
11 |
12 |
13 | Group
14 | Nodes
15 | Network
16 |
17 | @foreach (var g in groups)
18 | {
19 |
20 | @g.Key.Name
21 | @Health.Description(g)
22 | @g.Sum(n => n.TotalPrimaryNetworkBitsPerSecond < 0 ? 0 : n.TotalPrimaryNetworkBitsPerSecond).ToSpeed()
23 |
24 | }
25 |
26 |
27 |
28 | Total
29 | @Health.Description(nodes)
30 | @nodes.Sum(n => n.TotalPrimaryNetworkBitsPerSecond < 0 ? 0 : n.TotalPrimaryNetworkBitsPerSecond).ToSpeed()
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Hub/Index.Issues.cshtml:
--------------------------------------------------------------------------------
1 | @inject PollingService Poller
2 | @{
3 | var issues = Poller.GetIssues().OrderByWorst().ToList();
4 | }
5 |
6 |
@((issues.Count > 0 ? MonitorStatus.Warning : MonitorStatus.Good).IconSpan()) @issues.Count.Pluralize("Issue")
7 |
8 | @foreach (var i in issues)
9 | {
10 |
11 |
12 | @i.MonitorStatus.IconSpan() @i.Title (@(i.Type ?? "Unknown Type"))
13 | @i.Date.ToRelativeTimeSpan()
14 |
15 |
@i.Description
16 |
17 |
18 | }
19 |
20 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Hub/SQL.AvailabilityGroup.cshtml:
--------------------------------------------------------------------------------
1 | @model Opserver.Data.SQL.SQLNode.AGInfo
2 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/PagerDuty/PagerDuty.EscFull.cshtml:
--------------------------------------------------------------------------------
1 | @model List
2 | @{
3 | this.SetPageTitle("PagerDuty Full Escalation");
4 | }
5 |
6 | Full Escalation Schedule
7 |
8 |
9 |
10 |
11 | Name
12 | Level
13 | Policy
14 | Email
15 | Phone
16 |
17 |
18 |
19 | @foreach (var a in Model.OrderBy(u => u.EscalationLevel))
20 | {
21 |
22 | }
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/PagerDuty/PagerDuty.Incident.Model.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Opserver.Data.PagerDuty;
3 |
4 | namespace Opserver.Views.PagerDuty
5 | {
6 | public class PagerDutyIncidentModel
7 | {
8 | public Incident Incident { get; set; }
9 | public List Logs { get; set; }
10 | }
11 | }
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/PagerDuty/PagerDuty.Model.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Opserver.Data.PagerDuty;
3 |
4 | namespace Opserver.Views.PagerDuty
5 | {
6 | public class PagerDutyModel
7 | {
8 | public List Schedule { get; set; }
9 | public int CachedDays { get; set; }
10 |
11 | public List AllIncidents { get; set; }
12 | public PagerDutyPerson CurrentPagerDutyPerson { get; set; }
13 | }
14 | }
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/PagerDuty/PagerDuty.OnCallRow.cshtml:
--------------------------------------------------------------------------------
1 | @model OnCall
2 |
3 | @Model.AssignedUser.FullName
4 |
5 | @if (Model.IsPrimary)
6 | {
7 | @Model.EscalationLevelDescription @Model.SharedLevelDescription
8 | }
9 | else
10 | {
11 | @Model.EscalationLevelDescription@Model.SharedLevelDescription
12 | }
13 | @if (Model.IsOverride)
14 | {
15 | (Override )
16 | }
17 |
18 | @Model.PolicyTitle
19 | @Model.AssignedUser.Email
20 | @Module.API.AllUsers.Data.FirstOrDefault(u => u.Id == @Model.AssignedUser.Id).Phone
21 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/PagerDuty/_ViewImports.cshtml:
--------------------------------------------------------------------------------
1 | @using Opserver.Data.PagerDuty
2 | @using Opserver.Views.PagerDuty
3 | @inject PagerDutyModule Module
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Redis/Dashboard.cshtml:
--------------------------------------------------------------------------------
1 | @model DashboardModel
2 | @{
3 | Layout = "~/Views/Shared/Master.cshtml";
4 | this.SetPageTitle("Redis Dashboard");
5 | this.SetTopNodes(Module.Instances, "Redis Servers", Model.CurrentInstance);
6 | }
7 | @functions
8 | {
9 | string TabLink(RedisViews view, string action, string name, bool includeParams = true)
10 | {
11 |
12 | @name
13 |
14 | return null;
15 | }
16 | }
17 | @section head {
18 |
24 | }
25 |
26 | @TabLink(RedisViews.All, nameof(RedisController.Dashboard), "dashboard", false)
27 | @TabLink(RedisViews.Instance, nameof(RedisController.Instance), "instance")
28 |
29 |
30 | @if (Model.View == RedisViews.Instance && Model.CurrentInstance == null)
31 | {
32 |
33 | }
34 | @RenderBody()
35 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Redis/Instance.Config.cshtml:
--------------------------------------------------------------------------------
1 | @model RedisInstance
2 | @{
3 | var config = Model.Config.Data;
4 | }
5 |
6 | Server Config
7 |
8 | @if (config?.Any() ?? false)
9 | {
10 |
11 |
12 |
13 | Config Param
14 | Value
15 |
16 |
17 |
18 | @foreach (var entry in config.OrderBy(e => e.Key))
19 | {
20 |
21 | @entry.Key
22 | @entry.Value
23 |
24 | }
25 |
26 |
27 | }
28 | else
29 | {
30 | No configuration available
31 | }
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Redis/Instance.Selector.cshtml:
--------------------------------------------------------------------------------
1 | @{
2 | var instances = Module.Instances;
3 | var groups = instances.GroupBy(i => new {i.Name, i.Port})
4 | .OrderBy(g => g.Key.Port)
5 | .ThenBy(g => g.Key.Name);
6 | }
7 |
8 |
9 | @foreach (var g in groups)
10 | {
11 |
12 |
13 |
@g.Key.Name - @g.Key.Port.ToString()
14 |
24 |
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Redis/Instance.SlowLog.cshtml:
--------------------------------------------------------------------------------
1 | @model RedisInstance
2 | @{
3 | var log = Model.SlowLog.SafeData(true);
4 | }
5 |
6 | Slow Command Log
7 |
8 | @if (log.Any())
9 | {
10 |
11 |
12 |
13 | Command
14 | Duration
15 | When
16 | Ref
17 |
18 |
19 |
20 | @foreach (var entry in log)
21 | {
22 |
23 | @string.Join(" ", entry.Arguments)
24 | @entry.Duration.ToTimeStringMini(3)
25 | @entry.Time.ToRelativeTimeSpanMini()
26 | docs
27 |
28 | }
29 |
30 |
31 | }
32 | else
33 | {
34 |
35 | @if (!Model.IsSlowLogEnabled)
36 | {
37 | @:Slow log is not enabled
38 | }
39 | else
40 | {
41 | @:No slow commands in the log
42 | }
43 |
44 | }
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Redis/_ViewImports.cshtml:
--------------------------------------------------------------------------------
1 | @using Opserver.Data.Redis
2 | @using Opserver.Views.Redis
3 | @inject RedisModule Module
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Redis/_ViewStart.cshtml:
--------------------------------------------------------------------------------
1 | @{
2 | Layout = this.IsAjaxRequest() ? null : "Dashboard.cshtml";
3 | }
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/SQL/Dashboard.Model.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Opserver.Data;
3 | using Opserver.Data.SQL;
4 |
5 | namespace Opserver.Views.SQL
6 | {
7 | public enum SQLViews
8 | {
9 | Servers = 0,
10 | Jobs = 1,
11 | Instance = 3,
12 | Active = 4,
13 | Top = 5,
14 | Connections = 6,
15 | Databases = 7
16 | }
17 |
18 | public class DashboardModel
19 | {
20 | public SQLInstance CurrentInstance { get; set; }
21 | public string ErrorMessage { get; set; }
22 |
23 | public int Refresh { get; set; }
24 | public SQLViews View { get; set; }
25 |
26 | public enum LastRunInterval
27 | {
28 | FiveMinutes = 5 * 60,
29 | Hour = 60 * 60,
30 | Day = 24 * 60 * 60,
31 | Week = 7 * 24 * 60 * 60
32 | }
33 |
34 | public List Connections { get; set; }
35 | public Cache Cache { get; set; }
36 | }
37 | }
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/SQL/Databases.Model.cs:
--------------------------------------------------------------------------------
1 | using Opserver.Data.SQL;
2 | using Opserver.Helpers;
3 |
4 | namespace Opserver.Views.SQL
5 | {
6 | public class DatabasesModel
7 | {
8 | public SQLInstance Instance { get; set; }
9 | public string Database { get; set; }
10 | public string ObjectName { get; set; }
11 | public Views View { get; set; }
12 |
13 | // TODO: Remove for extensibility, create a dictionary instead and nameof()
14 | public enum Views
15 | {
16 | Tables = 0,
17 | Backups = 1,
18 | Views = 2,
19 | BlitzIndex = 3,
20 | MissingIndexes = 4,
21 | UnusedIndexes = 5,
22 | Storage = 6,
23 | Other = 7,
24 | Restores = 8,
25 | StoredProcedures = 9
26 | }
27 |
28 | public static string GetDatabaseClass(SQLInstance.Database db)
29 | {
30 | if (db.IsSystemDatabase) return "text-primary";
31 | if (db.State == DatabaseStates.Restoring) return StatusIndicator.WarningClass;
32 |
33 | return db.MonitorStatus.TextClass(showGood: true);
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/SQL/Instance.Memory.cshtml:
--------------------------------------------------------------------------------
1 | @model SQLInstance
2 | @{
3 | var types = Model.MemoryClerkSummary;
4 | var data = types.SafeData(true);
5 | }
6 |
7 | Memory Usage Details
8 |
9 | @if (data.Any())
10 | {
11 |
12 |
13 |
14 | Description
15 | Clerk Type
16 | Memory Used
17 | % Used
18 |
19 |
20 |
21 | @foreach (var t in data)
22 | {
23 |
24 | @t.Name
25 | @t.ClerkType
26 | @t.UsedBytes.ToSize()
27 |
28 |
29 |
30 |
@(t.UsedPercent.ToString("#0.###"))%
31 |
32 |
33 |
34 | }
35 |
36 |
37 | }
38 | else
39 | {
40 | No memory clerk information available
41 | }
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/SQL/Instance.Model.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Opserver.Data.SQL;
3 |
4 | namespace Opserver.Views.SQL
5 | {
6 | public class InstanceModel : DashboardModel
7 | {
8 | public List PerfCounters { get; set; }
9 | }
10 | }
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/SQL/Operations.Active.Model.cs:
--------------------------------------------------------------------------------
1 | using Opserver.Data.SQL;
2 |
3 | namespace Opserver.Views.SQL
4 | {
5 | public class OperationsActiveModel : DashboardModel
6 | {
7 | public SQLInstance.ActiveSearchOptions ActiveSearchOptions { get; set; }
8 | }
9 | }
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/SQL/Ops.Top.Detail.Model.cs:
--------------------------------------------------------------------------------
1 | using Opserver.Data.SQL;
2 |
3 | namespace Opserver.Views.SQL
4 | {
5 | public class OperationsTopDetailModel
6 | {
7 | public SQLInstance Instance { get; set; }
8 | public SQLInstance.TopOperation Op { get; set; }
9 | }
10 | }
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/SQL/Servers.Model.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Opserver.Data.SQL;
3 |
4 | namespace Opserver.Views.SQL
5 | {
6 | public class ServersModel : DashboardModel
7 | {
8 | public List AzureServers { get; set; }
9 | public List Clusters { get; set; }
10 | public List StandaloneInstances { get; set; }
11 | public List AvailabilityGroups { get; set; }
12 |
13 | public SQLCluster CurrentCluster { get; set; }
14 |
15 | public JobSort? JobSort { get; set; }
16 | public SortDir? SortDirection { get; set; }
17 | }
18 |
19 | public enum JobSort
20 | {
21 | Server,
22 | Name,
23 | LastRun,
24 | Start,
25 | End,
26 | Duration,
27 | Enabled
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/SQL/_ViewImports.cshtml:
--------------------------------------------------------------------------------
1 | @using Opserver.Data.SQL
2 | @using Opserver.Views.SQL
3 | @inject SQLModule Module
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/SQL/_ViewStart.cshtml:
--------------------------------------------------------------------------------
1 | @{
2 | Layout = this.IsAjaxRequest() ? null : "Dashboard.cshtml";
3 | }
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Shared/AccessDenied.cshtml:
--------------------------------------------------------------------------------
1 | @{
2 | this.SetPageTitle("Access Denied");
3 | }
4 |
5 |
6 |
7 | Access Denied
8 |
9 |
10 | You do not have permissions to see this page.
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Shared/Error.cshtml:
--------------------------------------------------------------------------------
1 | @{
2 | this.SetPageTitle("Error");
3 | }
4 |
5 |
6 |
7 | Oh no, an error!
8 |
9 |
10 | I'm so sorry, an error occured doing the thing in the place.
11 |
12 | @if (Current.User.IsGlobalAdmin)
13 | {
14 |
You can check the error log for details.
15 | }
16 |
17 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Shared/Gauges/Circle.Model.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Opserver.Data;
3 |
4 | namespace Opserver.Views.Shared.Guages
5 | {
6 | public class CircleModel
7 | {
8 | public string Label { get; set; }
9 | public IEnumerable Items { get; set; }
10 |
11 | public CircleModel(string label, IEnumerable items)
12 | {
13 | Label = label;
14 | Items = items;
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Shared/IssuesButton.cshtml:
--------------------------------------------------------------------------------
1 | @inject PollingService Poller
2 | @{
3 | var issues = Poller.GetIssues();
4 | }
5 | @if (issues.Any())
6 | {
7 |
12 | }
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Shared/NoConfiguration.cshtml:
--------------------------------------------------------------------------------
1 | @{
2 | this.SetPageTitle("No configuration");
3 | }
4 |
5 |
6 |
7 | Uh no! Configuration Problem!
8 |
9 | @if (!Current.Security.IsConfigured)
10 | {
11 |
12 | No security provider was found! This means the security section in your config is either missing or has an invalid provider.
13 |
14 |
See the configuration docs here: TODO: Fix after merge
15 | }
16 | else
17 | {
18 |
19 | No configuration data was found. This means no Opserver sections are configured/enabled or you have no roles assigned to you.
20 |
21 |
22 | See the Readme.txt
in /Config and *.example.json
files for examples of a basic JSON configuration...or you can implement your own provider to get settings from anywhere.
23 |
24 |
25 | You can browse to /about to see how you have been authenticated and what monitors have been enabled.
26 |
27 | }
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Shared/PageNotFound.Model.cs:
--------------------------------------------------------------------------------
1 | namespace Opserver.Views.Shared
2 | {
3 | public class PageNotFoundModel
4 | {
5 | public string Title { get; set; }
6 | public string Message { get; set; }
7 | }
8 | }
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Shared/PageNotFound.cshtml:
--------------------------------------------------------------------------------
1 | @model Opserver.Views.Shared.PageNotFoundModel
2 | @{
3 | this.SetPageTitle("404 - Not Found");
4 | }
5 |
6 |
7 |
8 | @Model.Title.IsNullOrEmptyReturn("404 - Not Found")
9 |
10 |
11 | @Model.Message.IsNullOrEmptyReturn("The page you were looking for could not be found.")
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Shared/Partials.MemoryCell.Model.cs:
--------------------------------------------------------------------------------
1 | using Opserver.Data.SQL;
2 |
3 | namespace Opserver.Views.Shared
4 | {
5 | public class PartialsMemoryCellModel
6 | {
7 | public SQLInstance Instance { get; }
8 | public int DecimalPlaces { get; }
9 |
10 | public PartialsMemoryCellModel(SQLInstance instance, int decimalPlaces = 0) =>
11 | (Instance, DecimalPlaces) = (instance, decimalPlaces);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Shared/Partials.MemoryCell.cshtml:
--------------------------------------------------------------------------------
1 | @inject Opserver.Data.Dashboard.DashboardModule Dashboard
2 | @model PartialsMemoryCellModel
3 | @{
4 | var i = Model.Instance;
5 | var serverInfo = Dashboard.GetNodeByName(i.Name);
6 | }
7 | @if (serverInfo != null)
8 | {
9 | @(serverInfo?.MemoryPercentStatusSpan())
10 | }
11 | else
12 | {
13 | var props = i.ServerProperties.SafeData();
14 | if (props != null && props.PhysicalMemoryBytes > 0)
15 | {
16 | @(decimal.Round(i.CurrentMemoryPercent.Value, Model.DecimalPlaces) + "%")
17 | }
18 | else
19 | {
20 | ?
21 | }
22 | }
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Shared/PollInfo.Model.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Opserver.Data;
3 |
4 | namespace Opserver.Views
5 | {
6 | public class PollInfoModel
7 | {
8 | public string Name { get; set; }
9 | public PollNode Node { get; set; }
10 | public IEnumerable Nodes { get; set; }
11 | public Cache Cache { get; set; }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Shared/TopBoxOptions.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Opserver.Views.Shared
4 | {
5 | public class TopBoxOptions
6 | {
7 | public string SearchUrl { get; set; }
8 | public IEnumerable AllNodes { get; set; }
9 |
10 | public ISearchableNode CurrentNode { get; set; }
11 | public string Url { get; set; }
12 | public bool SearchOnly { get; set; }
13 | public string SearchText { get; set; }
14 | public string SearchValue { get; set; }
15 | public string QueryParam { get; set; } = "q";
16 | public Dictionary SearchParams { get; set; }
17 | }
18 | }
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Shared/TopRefresh.Model.cs:
--------------------------------------------------------------------------------
1 | namespace Opserver.Views.Shared
2 | {
3 | public class TopRefreshModel
4 | {
5 | public string Tab { get; set; }
6 | }
7 | }
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Shared/TopRefresh.cshtml:
--------------------------------------------------------------------------------
1 | @model Opserver.Views.Shared.TopRefreshModel
2 |
5 |
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/Shared/TopTabs.cshtml:
--------------------------------------------------------------------------------
1 | @using StackExchange.Profiling
2 | @functions
3 | {
4 | void RenderTab(NavTab tab)
5 | {
6 | if (tab.IsEnabled)
7 | {
8 | // Optimism!
9 | using (MiniProfiler.Current.Step("Render Tab: " + tab.Name))
10 | {
11 |
12 |
13 | @tab.Name
14 | @if (tab.BadgeCount > 0)
15 | {
16 | @tab.BadgeCount.ToComma()
17 | }
18 |
19 |
20 | }
21 | }
22 | }
23 | }
24 | @using (MiniProfiler.Current.Step("TopTabs"))
25 | {
26 |
27 | @foreach (var tab in NavTab.AllTabs)
28 | {
29 | RenderTab(tab);
30 | }
31 |
32 | }
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/_ViewImports.cshtml:
--------------------------------------------------------------------------------
1 | @using EnumsNET
2 | @using Microsoft.AspNetCore.Html
3 | @using Microsoft.AspNetCore.WebUtilities
4 | @using Opserver
5 | @using Opserver.Controllers
6 | @using Opserver.Data
7 | @using Opserver.Helpers
8 | @using Opserver.Helpers.Tag
9 | @using System.Globalization
10 | @using Opserver.Views.Shared
11 | @using Roles = Opserver.Models.Roles;
12 | @inject OpserverSettings Settings
13 | @addTagHelper *, Opserver
14 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
--------------------------------------------------------------------------------
/src/Opserver.Web/Views/_ViewStart.cshtml:
--------------------------------------------------------------------------------
1 | @{
2 | Layout = "~/Views/Shared/Master.cshtml";
3 | }
--------------------------------------------------------------------------------
/src/Opserver.Web/bundleconfig.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "inputFiles": [
4 | "ContentSource/bootstrap/js/bootstrap.min.js",
5 | "ContentSource/js/plugins/*.js",
6 | "ContentSource/js/Scripts.js"
7 | ],
8 | "outputFileName": "wwwroot/Content/js/bundle.js",
9 | "sourceMap": true
10 | }
11 | ]
12 |
--------------------------------------------------------------------------------
/src/Opserver.Web/compilerconfig.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "inputFile": "ContentSource/themes/dark.less",
4 | "outputFile": "wwwroot/Content/themes/dark.css",
5 | "sourceMap": true
6 | },
7 | {
8 | "inputFile": "ContentSource/themes/light.less",
9 | "outputFile": "wwwroot/Content/themes/light.css",
10 | "sourceMap": true
11 | }
12 | ]
--------------------------------------------------------------------------------
/src/Opserver.Web/compilerconfig.json.defaults:
--------------------------------------------------------------------------------
1 | {
2 | "compilers": {
3 | "less": {
4 | "autoPrefix": "",
5 | "cssComb": "none",
6 | "ieCompat": true,
7 | "strictMath": false,
8 | "strictUnits": false,
9 | "relativeUrls": true,
10 | "rootPath": "",
11 | "soruceMapRoot": null,
12 | "soruceMapBasePath": null,
13 | "sourceMap": false
14 | },
15 | "sass": {
16 | "includePath": null,
17 | "indentType": "space",
18 | "indentWidth": 2,
19 | "outputStyle": "nested",
20 | "Precision": 5,
21 | "relativeUrls": true,
22 | "soruceMapRoot": null,
23 | "sourceMap": false
24 | },
25 | "stylus": {
26 | "sourceMap": false
27 | },
28 | "babel": {
29 | "sourceMap": false
30 | },
31 | "coffeescript": {
32 | "bare": false,
33 | "runtimeMode": "node",
34 | "sourceMap": false
35 | }
36 | },
37 | "minifiers": {
38 | "css": {
39 | "enabled": true,
40 | "termSemicolons": true,
41 | "gzip": false
42 | },
43 | "javascript": {
44 | "enabled": true,
45 | "termSemicolons": true,
46 | "gzip": false
47 | }
48 | }
49 | }
--------------------------------------------------------------------------------
/src/Opserver.Web/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/favicon.ico
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/font-awesome/fonts/FontAwesome.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/font-awesome/fonts/FontAwesome.otf
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/font-awesome/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/font-awesome/fonts/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/font-awesome/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/font-awesome/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/font-awesome/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/font-awesome/fonts/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/font-awesome/fonts/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/font-awesome/fonts/fontawesome-webfont.woff2
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/apple-touch-icon.png
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/logo/icon-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/logo/icon-128.png
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/logo/icon-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/logo/icon-16.png
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/logo/icon-256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/logo/icon-256.png
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/logo/icon-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/logo/icon-32.png
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/logo/icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/logo/logo.eps:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/logo/logo.eps
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/arithmetic_expression.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/arithmetic_expression.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/assert.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/assert.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/assign.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/assign.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/bitmap.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/bitmap.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/bookmark_lookup.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/bookmark_lookup.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/clustered_index_delete.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/clustered_index_delete.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/clustered_index_insert.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/clustered_index_insert.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/clustered_index_scan.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/clustered_index_scan.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/clustered_index_seek.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/clustered_index_seek.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/clustered_index_update.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/clustered_index_update.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/collapse.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/collapse.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/compute_scalar.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/compute_scalar.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/concatenation.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/concatenation.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/constant_scan.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/constant_scan.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/convert.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/convert.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/declare.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/declare.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/deleted_scan.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/deleted_scan.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/distribute_streams.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/distribute_streams.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/dynamic.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/dynamic.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/fetch_query.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/fetch_query.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/filter.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/filter.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/gather_streams.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/gather_streams.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/hash_match.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/hash_match.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/icons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/icons.png
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/if.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/if.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/inserted_scan.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/inserted_scan.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/intrinsic.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/intrinsic.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/keyset.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/keyset.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/log_row_scan.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/log_row_scan.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/merge_interval.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/merge_interval.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/merge_join.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/merge_join.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/nested_loops.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/nested_loops.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/nonclustered_index_delete.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/nonclustered_index_delete.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/nonclustered_index_insert.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/nonclustered_index_insert.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/nonclustered_index_scan.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/nonclustered_index_scan.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/nonclustered_index_seek.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/nonclustered_index_seek.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/nonclustered_index_spool.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/nonclustered_index_spool.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/nonclustered_index_update.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/nonclustered_index_update.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/online_index_insert.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/online_index_insert.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/parameter_table_scan.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/parameter_table_scan.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/population_query.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/population_query.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/rdi_lookup.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/rdi_lookup.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/refresh_query.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/refresh_query.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/remote_delete.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/remote_delete.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/remote_insert.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/remote_insert.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/remote_query.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/remote_query.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/remote_scan.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/remote_scan.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/remote_update.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/remote_update.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/repartition_streams.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/repartition_streams.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/result.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/result.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/row_count_spool.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/row_count_spool.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/segment.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/segment.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/sequence.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/sequence.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/sequenceproject.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/sequenceproject.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/snapshot.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/snapshot.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/sort.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/sort.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/split.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/split.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/spool.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/spool.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/stream_aggregate.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/stream_aggregate.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/switch.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/switch.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/table_delete.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/table_delete.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/table_insert.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/table_insert.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/table_scan.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/table_scan.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/table_spool.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/table_spool.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/table_update.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/table_update.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/table_valued_function.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/table_valued_function.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/top.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/top.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/udx.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/udx.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/Content/img/query-plan/while.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/Content/img/query-plan/while.gif
--------------------------------------------------------------------------------
/src/Opserver.Web/wwwroot/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opserver/Opserver/e29191823f426423ffeb6a59fa4ad1aa56855203/src/Opserver.Web/wwwroot/favicon.ico
--------------------------------------------------------------------------------
/src/Opserver.ruleset:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/tests/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | Library
4 | 2.4.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/tests/Opserver.Tests/Opserver.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Opserver.Tests
4 | Opserver.Tests
5 | net8.0
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | all
17 | runtime; build; native; contentfiles; analyzers; buildtransitive
18 |
19 |
20 |
--------------------------------------------------------------------------------