mirror of https://bitbucket.org/anguist/ntpa
498 lines
17 KiB
C#
498 lines
17 KiB
C#
//
|
|
// Copyright (c) 2013-2017 Carsten Sonne Larsen <cs@innolan.net>
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Net;
|
|
using System.Threading;
|
|
using Ntp.Analyzer.Config;
|
|
using Ntp.Analyzer.Config.Node;
|
|
using Ntp.Analyzer.Data;
|
|
using Ntp.Analyzer.Data.Import;
|
|
using Ntp.Analyzer.Export;
|
|
using Ntp.Analyzer.Import;
|
|
using Ntp.Analyzer.Monitor.Client;
|
|
using Ntp.Analyzer.Monitor.Server;
|
|
using Ntp.Analyzer.Objects;
|
|
using Ntp.Analyzer.Process.Description;
|
|
using Ntp.Analyzer.Process.Log;
|
|
using Ntp.Common.App;
|
|
using Ntp.Common.IO;
|
|
using Ntp.Common.Log;
|
|
using Ntp.Common.Process;
|
|
using Ntp.Common.System;
|
|
using Ntp.Data;
|
|
using Ntp.Data.Provider;
|
|
|
|
namespace Ntp.Analyzer.Process
|
|
{
|
|
public sealed class Initializer
|
|
{
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="Initializer" /> class.
|
|
/// </summary>
|
|
/// <param name="configFile">Config file to use.</param>
|
|
/// <param name="pid">Process ID.</param>
|
|
/// <param name="pidFile">Where to write Process ID file.</param>
|
|
/// <param name="name">Name of process ID. Used when shutting down.</param>
|
|
/// <param name="initlog">Log used during initialization.</param>
|
|
public Initializer(string configFile, int pid, string pidFile, string name, LogGroup initlog)
|
|
{
|
|
this.configFile = configFile;
|
|
this.pid = pid;
|
|
this.pidFile = pidFile;
|
|
this.name = name;
|
|
this.initlog = initlog;
|
|
Nodes = new List<IRequest>();
|
|
Listeners = new List<Listener>();
|
|
version = "0.8.2";
|
|
}
|
|
|
|
private static bool firstrun = true;
|
|
private readonly string configFile;
|
|
private readonly LogGroup initlog;
|
|
private readonly string name;
|
|
private readonly int pid;
|
|
private readonly string pidFile;
|
|
private readonly string version;
|
|
private Configuration config;
|
|
|
|
public SignalHandler Controller { get; private set; }
|
|
|
|
public Scheduler Scheduler { get; private set; }
|
|
|
|
public LogBase Log { get; private set; }
|
|
|
|
public List<IRequest> Nodes { get; }
|
|
|
|
public List<Listener> Listeners { get; }
|
|
|
|
public bool Ready { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Run the NTP Analyzer.
|
|
/// </summary>
|
|
public void Run()
|
|
{
|
|
// Neutralize.
|
|
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
|
|
|
|
bool proceed = InitializeConfiguration();
|
|
|
|
if (proceed)
|
|
proceed = InitializeSecurity();
|
|
|
|
if (proceed)
|
|
proceed = InitializeLog();
|
|
|
|
if (proceed)
|
|
{
|
|
InitializeApplication();
|
|
InitializeListeners();
|
|
InitializeCluster();
|
|
InitializeScheduler();
|
|
proceed = InitializeDatabase();
|
|
}
|
|
|
|
if (proceed)
|
|
InitializeData();
|
|
|
|
Ready = proceed;
|
|
firstrun = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the application and database state.
|
|
/// </summary>
|
|
private void InitializeApplication()
|
|
{
|
|
// Start signal controller
|
|
Controller = new SignalHandler(name, initlog);
|
|
Controller.Start();
|
|
|
|
// Initialize application state
|
|
ApplicationState.Version = version;
|
|
ApplicationState.Pid = pid;
|
|
ApplicationState.Name = name;
|
|
ApplicationState.Config = config;
|
|
ApplicationState.ConfigFile = configFile;
|
|
ApplicationState.Log = Log;
|
|
|
|
if (config.Heartbeat == null)
|
|
return;
|
|
|
|
var beat = new Heartbeat(Log, config.Heartbeat.Rate);
|
|
beat.Start();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the cluster nodes.
|
|
/// </summary>
|
|
private void InitializeCluster()
|
|
{
|
|
if (config.Cluster == null)
|
|
return;
|
|
|
|
try
|
|
{
|
|
foreach (var node in config.Cluster.Nodes)
|
|
{
|
|
var ip = IPAddress.Parse(node.Ip);
|
|
var req = new TextRequest(ip, node.Port);
|
|
Nodes.Add(req);
|
|
Log.ClusterReady(node);
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
initlog.ClusterError(e);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the configuration.
|
|
/// </summary>
|
|
private bool InitializeConfiguration()
|
|
{
|
|
if (!File.Exists(configFile))
|
|
{
|
|
initlog.NoConfig(configFile);
|
|
return false;
|
|
}
|
|
|
|
var builder = new ConfigBuilder(configFile);
|
|
config = builder.Execute();
|
|
|
|
if (!builder.Errors.Any() && config != null)
|
|
return true;
|
|
|
|
initlog.AddErrors(builder.Errors);
|
|
return false;
|
|
}
|
|
|
|
private void InitializeData()
|
|
{
|
|
if (!config.Database.Initialize)
|
|
return;
|
|
|
|
TimeServerWebAdapter.Initialize(config.Database.Import);
|
|
|
|
try
|
|
{
|
|
// Initialize hosts
|
|
foreach (var server in config.Servers)
|
|
{
|
|
var host = DataFace.Instance.Hosts.SingleOrDefault(h => h.Id == server.HostId);
|
|
if (host == null)
|
|
{
|
|
IPAddress ip;
|
|
try
|
|
{
|
|
ip = Dns.GetHostAddresses(server.ServerName)[0];
|
|
}
|
|
catch
|
|
{
|
|
ip = IPAddress.None;
|
|
}
|
|
|
|
int? orgId = null;
|
|
if (TimeServers.List.ContainsKey(server.ServerName))
|
|
{
|
|
orgId = TimeServers.List[server.ServerName];
|
|
Log.KnownServer(orgId.Value, server.ServerName);
|
|
}
|
|
else if (!Equals(ip, IPAddress.None) && TimeServers.List.ContainsKey(ip.ToString()))
|
|
{
|
|
orgId = TimeServers.List[ip.ToString()];
|
|
Log.KnownServer(orgId.Value, ip.ToString());
|
|
}
|
|
|
|
host = new Host(server.HostId, server.ServerName, ip.ToString(), orgId);
|
|
DataFace.Instance.Hosts.Save(host);
|
|
Log.NewHost(server, ip);
|
|
}
|
|
else if (host.OrgId == null && TimeServers.List.ContainsKey(host.Ip))
|
|
{
|
|
host.OrgId = TimeServers.List[host.Ip];
|
|
Log.KnownServer(host.OrgId.Value, host.Ip);
|
|
DataFace.Instance.Hosts.Save(host);
|
|
}
|
|
else if (host.OrgId == null && TimeServers.List.ContainsKey(host.Name))
|
|
{
|
|
host.OrgId = TimeServers.List[host.Name];
|
|
Log.KnownServer(host.OrgId.Value, host.Name);
|
|
DataFace.Instance.Hosts.Save(host);
|
|
}
|
|
|
|
// Flush old association entries
|
|
DataFace.Instance.AssociationEntries.Delete(server.HostId);
|
|
|
|
// Initialize peers
|
|
var peerImporter = ImportFactory.CreatePeerImporter(server.ServerName, server.ServerType, host, Log);
|
|
|
|
foreach (var entry in peerImporter.ToList())
|
|
{
|
|
var currentEntry = entry;
|
|
var peerList = DataFace.Instance.Peers.Where(p => p.Ip == currentEntry.Remote).ToList();
|
|
|
|
if (!peerList.Any())
|
|
{
|
|
// Create new peer in database
|
|
TimeServer timeServer = null;
|
|
if (TimeServers.List.ContainsKey(entry.Remote))
|
|
{
|
|
int orgId = TimeServers.List[entry.Remote];
|
|
timeServer = DataFace.Instance.Servers[orgId];
|
|
Log.KnownServer(orgId, entry.Remote);
|
|
}
|
|
|
|
var hostName = entry.Remote;
|
|
|
|
try
|
|
{
|
|
var hostEntry = Dns.GetHostEntry(entry.Remote);
|
|
hostName = hostEntry.HostName;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Log.HostNameNotFound(entry.Remote, e);
|
|
}
|
|
|
|
var peer = new Peer(hostName, entry.Remote, timeServer);
|
|
DataFace.Instance.Peers.Save(peer);
|
|
Log.NewPeer(entry);
|
|
}
|
|
else if (TimeServers.List.ContainsKey(entry.Remote))
|
|
{
|
|
// Try to update existing peer
|
|
var peer = peerList.First();
|
|
if (peer.Server != null && peer.Server.IsOrgServer)
|
|
continue;
|
|
|
|
int orgId = TimeServers.List[entry.Remote];
|
|
var timeServer = DataFace.Instance.Servers[orgId];
|
|
Log.KnownServer(orgId, entry.Remote);
|
|
if (timeServer == null)
|
|
continue;
|
|
|
|
peer.Server = timeServer;
|
|
DataFace.Instance.Peers.Save(peer);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Log.TableError(e);
|
|
}
|
|
}
|
|
|
|
private bool InitializeDatabase()
|
|
{
|
|
SqlDatabaseFactory.Initialize(config.Database);
|
|
DataFace.Initialize(Log);
|
|
|
|
var checker = new SqlDatabaseChecker(SqlDatabaseFactory.Instance, Controller, initlog);
|
|
checker.CheckConnection();
|
|
|
|
if (Controller.Stopped)
|
|
return false;
|
|
|
|
try
|
|
{
|
|
var initializer = new DatabaseInitializer(
|
|
config.Database.Initialize,
|
|
config.Database.Upgrade,
|
|
Log);
|
|
|
|
return initializer.Execute();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
initlog.DatabaseError(e);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the listeners.
|
|
/// </summary>
|
|
private void InitializeListeners()
|
|
{
|
|
try
|
|
{
|
|
foreach (var monitor in config.Monitors)
|
|
{
|
|
var listener = new Listener(monitor.Ip, monitor.Port, Log);
|
|
listener.Open();
|
|
Listeners.Add(listener);
|
|
initlog.ListenerReady(listener);
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
initlog.ListenerError(e);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the log.
|
|
/// </summary>
|
|
private bool InitializeLog()
|
|
{
|
|
try
|
|
{
|
|
Log = LogFactory.CreateLog(config.Log);
|
|
Log.Initialize();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
initlog.InitializationError(e);
|
|
return false;
|
|
}
|
|
|
|
initlog.Add(Log);
|
|
|
|
if (firstrun)
|
|
initlog.Starting(version);
|
|
|
|
initlog.ConfigFile(configFile);
|
|
initlog.ProcessId(pid);
|
|
initlog.InstanceName(name);
|
|
|
|
if (config.Permission?.AppUserId == null)
|
|
return true;
|
|
|
|
if (Permission.SetUserId(config.Permission.AppUserId.Value))
|
|
initlog.UserId(config.Permission.AppUserId.Value);
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the scheduler.
|
|
/// </summary>
|
|
private void InitializeScheduler()
|
|
{
|
|
Job.Reset();
|
|
|
|
try
|
|
{
|
|
Scheduler = new Scheduler(Log);
|
|
Log.JobInitStart();
|
|
|
|
foreach (var bulk in config.Bulks)
|
|
Scheduler.Add(new BulkStatJob(bulk, Scheduler.Log));
|
|
|
|
// Add jobs to schedule.
|
|
foreach (var server in config.Servers)
|
|
{
|
|
Scheduler.Add(new HostStatJob(server.HostStats, Scheduler.Log));
|
|
Scheduler.Add(new HostIoStatJob(server.HostIoStats, Scheduler.Log));
|
|
Scheduler.Add(new PeerStatJob(server.PeerStats, Scheduler.Log));
|
|
Scheduler.Add(new DriftStatJob(server.DriftStats, Scheduler.Log));
|
|
Scheduler.Add(new AboutPageJob(server.Menu, server.AboutPage, Scheduler.Log));
|
|
|
|
foreach (var peerPage in server.PeerPages)
|
|
Scheduler.Add(new PeerPageJob(server.Menu, peerPage, Scheduler.Log));
|
|
|
|
foreach (var hostPage in server.HostPages)
|
|
Scheduler.Add(new HostPageJob(server.Menu, hostPage, Scheduler.Log));
|
|
|
|
foreach (var hostGraph in server.HostGraphs)
|
|
Scheduler.Add(new HostGraphJob(hostGraph, Scheduler.Log));
|
|
|
|
foreach (var trafficGraph in server.TrafficGraphs)
|
|
Scheduler.Add(new TrafficGraphJob(trafficGraph, Scheduler.Log));
|
|
|
|
foreach (var peerGraph in server.PeerGraphs)
|
|
Scheduler.Add(new PeerGraphJob(peerGraph, Scheduler.Log));
|
|
|
|
foreach (var peerSummaryPage in server.PeerSummaryPages)
|
|
Scheduler.Add(new PeerSummaryJob(server.Menu, peerSummaryPage, Scheduler.Log));
|
|
|
|
Scheduler.Add(new HostGraphPageJob(server.HostGraphPage, Scheduler.Log));
|
|
Scheduler.Add(new PeerGraphPageJob(server.PeerGraphPage, Scheduler.Log));
|
|
}
|
|
|
|
// Add the surveillance/notify job.
|
|
if (config.Notify != null)
|
|
{
|
|
var adminJob = new NotifyJob(config.Notify, Log, pid, configFile);
|
|
Scheduler.Add(adminJob);
|
|
adminJob.SetJobList(Scheduler);
|
|
}
|
|
|
|
Log.JobInitEnd();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
initlog.SchedulerError(e);
|
|
}
|
|
|
|
ApplicationState.Scheduler = Scheduler;
|
|
ApplicationState.StartupTime = Scheduler.StartTime;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the security settings.
|
|
/// </summary>
|
|
private bool InitializeSecurity()
|
|
{
|
|
// Drop privileges
|
|
if (config.Permission?.AppUserId != null)
|
|
{
|
|
if (!Permission.SetUserId(config.Permission.AppUserId.Value))
|
|
{
|
|
initlog.UserIdError(config.Permission.AppUserId.Value);
|
|
}
|
|
}
|
|
|
|
// Create pid file
|
|
if (pidFile != null)
|
|
{
|
|
try
|
|
{
|
|
File.WriteAllText(pidFile, pid.ToString(CultureInfo.InvariantCulture));
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
initlog.PidFileError(e);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Set permissions
|
|
if (config.Permission == null)
|
|
return true;
|
|
|
|
FileSystemDestination.FileMask = config.Permission.FileMode;
|
|
FileSystemDestination.FileUserId = config.Permission?.UserId;
|
|
FileSystemDestination.FileGroupId = config.Permission?.GroupId;
|
|
|
|
return true;
|
|
}
|
|
}
|
|
} |