mirror of https://bitbucket.org/anguist/ntpa
483 lines
17 KiB
C#
483 lines
17 KiB
C#
//
|
|
// Copyright (c) 2013-2016 Carsten Sonne Larsen <cs@innolan.dk>
|
|
//
|
|
// 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.Common;
|
|
using Ntp.Analyzer.Config;
|
|
using Ntp.Analyzer.Config.Node;
|
|
using Ntp.Analyzer.Config.Node.Graph;
|
|
using Ntp.Analyzer.Config.Node.Page;
|
|
using Ntp.Analyzer.Data;
|
|
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.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 intitialization.</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 = VersionInfo.Number;
|
|
}
|
|
|
|
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();
|
|
InitializeListerners();
|
|
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)
|
|
{
|
|
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 (NodeConfiguration node in config.Cluster.Nodes)
|
|
{
|
|
IPAddress ip = IPAddress.Parse(node.Ip);
|
|
|
|
IRequest req = new TextRequest(ip, node.Port);
|
|
Nodes.Add(req);
|
|
|
|
Log.WriteLine(string.Format(
|
|
InitializationMessage.ClusterReady, node.Address), Severity.Notice);
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
initlog.WriteLine(InitializationMessage.ClusterError, Severity.Error);
|
|
initlog.WriteLine(e.Message, Severity.Debug);
|
|
initlog.WriteLine(e, Severity.Trace);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the configuration.
|
|
/// </summary>
|
|
private bool InitializeConfiguration()
|
|
{
|
|
if (!File.Exists(configFile))
|
|
{
|
|
initlog.WriteLine(string.Format(
|
|
InitializationMessage.NoConfig, configFile),
|
|
Severity.Error);
|
|
|
|
return false;
|
|
}
|
|
|
|
var builder = new ConfigBuilder(configFile);
|
|
config = builder.Execute();
|
|
if (builder.Errors.Count() != 0 || config == null)
|
|
{
|
|
foreach (string error in builder.Errors)
|
|
{
|
|
initlog.WriteLine(error, Severity.Error);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private void InitializeData()
|
|
{
|
|
if (!config.Database.Initialize)
|
|
return;
|
|
|
|
try
|
|
{
|
|
// Initialize hosts
|
|
foreach (HostConfiguration server in config.Servers)
|
|
{
|
|
Host 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;
|
|
}
|
|
host = new Host(server.HostId, server.ServerName, ip.ToString(), null);
|
|
DataFace.Instance.Hosts.Save(host);
|
|
Log.WriteLine(string.Format(
|
|
InitializationMessage.NewHost, server.HostId, server.ServerName, ip),
|
|
Severity.Info);
|
|
}
|
|
|
|
// Flush old association entries
|
|
DataFace.Instance.AssociationEntries.Delete(server.HostId);
|
|
|
|
// Initialize peers
|
|
Importer<AssociationEntry> peerImporter = ImportFactory.CreatePeerImporter(
|
|
server.ServerName, server.ServerType, host, Log);
|
|
|
|
foreach (AssociationEntry entry in peerImporter.ToList())
|
|
{
|
|
AssociationEntry entry1 = entry;
|
|
IEnumerable<Peer> peerList = DataFace.Instance.Peers.Where(p => p.Ip == entry1.Remote);
|
|
if (peerList.Any())
|
|
continue;
|
|
|
|
var peer = new Peer(entry.Remote, entry.Remote, null);
|
|
DataFace.Instance.Peers.Save(peer);
|
|
Log.WriteLine(string.Format(
|
|
InitializationMessage.NewPeer, entry.Remote, entry.Remote),
|
|
Severity.Info);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Log.WriteLine(InitializationMessage.TableError, Severity.Error);
|
|
Log.WriteLine(e.Message, Severity.Debug);
|
|
Log.WriteLine(e, Severity.Trace);
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
if (!config.Database.Initialize)
|
|
return true;
|
|
|
|
// Initialize database schema
|
|
try
|
|
{
|
|
var initializer = new DatabaseInitializer(config.Database.Upgrade, Log);
|
|
initializer.Execute();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
initlog.WriteLine(InitializationMessage.DatabaseError, Severity.Error);
|
|
initlog.WriteLine(e.Message, Severity.Debug);
|
|
initlog.WriteLine(e, Severity.Trace);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the listerners.
|
|
/// </summary>
|
|
private void InitializeListerners()
|
|
{
|
|
try
|
|
{
|
|
foreach (ListenerConfiguration monitor in config.Monitors)
|
|
{
|
|
var listener = new Listener(monitor.Ip, monitor.Port, Log);
|
|
listener.Open();
|
|
Listeners.Add(listener);
|
|
|
|
initlog.WriteLine(string.Format(
|
|
InitializationMessage.ListenerReady, listener), Severity.Notice);
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
initlog.WriteLine(InitializationMessage.ListenerError, Severity.Warn);
|
|
initlog.WriteLine(e.Message, Severity.Debug);
|
|
initlog.WriteLine(e, Severity.Trace);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the log.
|
|
/// </summary>
|
|
private bool InitializeLog()
|
|
{
|
|
// Create log.
|
|
try
|
|
{
|
|
Log = LogFactory.CreateLog(config.Log);
|
|
Log.Initialize();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
initlog.WriteLine(string.Format(
|
|
InitializationMessage.NoConfig, ex.Message),
|
|
Severity.Error);
|
|
|
|
return false;
|
|
}
|
|
|
|
initlog.Add(Log);
|
|
|
|
// Initialize log.
|
|
if (firstrun)
|
|
{
|
|
initlog.WriteLine(string.Format(
|
|
InitializationMessage.Welcome, version), Severity.Notice);
|
|
}
|
|
|
|
initlog.WriteLine(string.Format(
|
|
InitializationMessage.UsingConfig, configFile), Severity.Notice);
|
|
|
|
initlog.WriteLine(string.Format(
|
|
InitializationMessage.UsingPid, pid), Severity.Notice);
|
|
|
|
initlog.WriteLine(string.Format(
|
|
InitializationMessage.UsingName, name), Severity.Notice);
|
|
|
|
if (config.Permission?.AppUserId == null)
|
|
return true;
|
|
|
|
if (Permission.SetUserId(config.Permission.AppUserId.Value))
|
|
{
|
|
initlog.WriteLine(string.Format(
|
|
InitializationMessage.UsingUserId, config.Permission.AppUserId.Value),
|
|
Severity.Info);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the scheduler.
|
|
/// </summary>
|
|
private void InitializeScheduler()
|
|
{
|
|
Job.Reset();
|
|
|
|
try
|
|
{
|
|
Scheduler = new Scheduler(Log);
|
|
Log.WriteLine(InitializationMessage.JobInitStart, Severity.Info);
|
|
|
|
foreach (ReadingBulkConfiguration bulk in config.Bulks)
|
|
Scheduler.Add(new BulkStatJob(bulk, Scheduler.Log));
|
|
|
|
// Add jobs to schedule.
|
|
foreach (HostConfiguration 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 AboutPageJob(server.Menu, server.AboutPage, Scheduler.Log));
|
|
|
|
foreach (PeerPageConfiguration peerPage in server.PeerPages)
|
|
Scheduler.Add(new PeerPageJob(server.Menu, peerPage, Scheduler.Log));
|
|
|
|
foreach (HostPageConfiguration hostPage in server.HostPages)
|
|
Scheduler.Add(new HostPageJob(server.Menu, hostPage, Scheduler.Log));
|
|
|
|
foreach (HostGraphConfiguration hostGraph in server.HostGraphs)
|
|
Scheduler.Add(new HostGraphJob(hostGraph, Scheduler.Log));
|
|
|
|
foreach (TrafficGraphConfiguration trafficGraph in server.TrafficGraphs)
|
|
Scheduler.Add(new TrafficGraphJob(trafficGraph, Scheduler.Log));
|
|
|
|
foreach (PeerGraphConfiguration peerGraph in server.PeerGraphs)
|
|
Scheduler.Add(new PeerGraphJob(peerGraph, Scheduler.Log));
|
|
|
|
foreach (PeerSummaryPageConfiguration 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 survaliance/notify job.
|
|
if (config.Notify != null)
|
|
{
|
|
var adminJob = new NotifyJob(config.Notify, Log, pid, configFile);
|
|
Scheduler.Add(adminJob);
|
|
adminJob.SetJobList(Scheduler);
|
|
|
|
// Done
|
|
Log.WriteLine(InitializationMessage.JobInitEnd, Severity.Debug);
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
initlog.WriteLine(InitializationMessage.SchedulerError, Severity.Error);
|
|
initlog.WriteLine(e.Message, Severity.Debug);
|
|
initlog.WriteLine(e, Severity.Trace);
|
|
}
|
|
|
|
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.WriteLine(string.Format(
|
|
InitializationMessage.UserIdError, config.Permission.AppUserId.Value),
|
|
Severity.Warn);
|
|
}
|
|
}
|
|
|
|
// Create pid file
|
|
if (pidFile != null)
|
|
{
|
|
try
|
|
{
|
|
File.WriteAllText(pidFile, pid.ToString(CultureInfo.InvariantCulture));
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
initlog.WriteLine(string.Format(
|
|
InitializationMessage.PidFileError, e.Message),
|
|
Severity.Warn);
|
|
|
|
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;
|
|
}
|
|
}
|
|
} |