ntpa/Ntp.Analyzer.Process/Initializer.cs

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;
}
}
}