NTP Analyzer  0.8.2
Analyze the operation of time servers
Initializer.cs
Go to the documentation of this file.
1 //
2 // Copyright (c) 2013-2017 Carsten Sonne Larsen <cs@innolan.net>
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to deal
6 // in the Software without restriction, including without limitation the rights
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 // copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 // THE SOFTWARE.
21 
22 using System;
23 using System.Collections.Generic;
24 using System.Globalization;
25 using System.IO;
26 using System.Linq;
27 using System.Net;
28 using System.Threading;
29 using Ntp.Analyzer.Config;
30 using Ntp.Analyzer.Config.Node;
31 using Ntp.Analyzer.Data;
32 using Ntp.Analyzer.Data.Import;
33 using Ntp.Analyzer.Export;
34 using Ntp.Analyzer.Import;
37 using Ntp.Analyzer.Objects;
39 using Ntp.Analyzer.Process.Log;
40 using Ntp.Common.App;
41 using Ntp.Common.IO;
42 using Ntp.Common.Log;
43 using Ntp.Common.Process;
44 using Ntp.Common.System;
45 using Ntp.Data;
46 using Ntp.Data.Provider;
47 
48 namespace Ntp.Analyzer.Process
49 {
50  public sealed class Initializer
51  {
60  public Initializer(string configFile, int pid, string pidFile, string name, LogGroup initlog)
61  {
62  this.configFile = configFile;
63  this.pid = pid;
64  this.pidFile = pidFile;
65  this.name = name;
66  this.initlog = initlog;
67  Nodes = new List<IRequest>();
68  Listeners = new List<Listener>();
69  version = "0.8.2";
70  }
71 
72  private static bool firstrun = true;
73  private readonly string configFile;
74  private readonly LogGroup initlog;
75  private readonly string name;
76  private readonly int pid;
77  private readonly string pidFile;
78  private readonly string version;
80 
81  public SignalHandler Controller { get; private set; }
82 
83  public Scheduler Scheduler { get; private set; }
84 
85  public LogBase Log { get; private set; }
86 
87  public List<IRequest> Nodes { get; }
88 
89  public List<Listener> Listeners { get; }
90 
91  public bool Ready { get; private set; }
92 
96  public void Run()
97  {
98  // Neutralize.
99  Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
100 
101  bool proceed = InitializeConfiguration();
102 
103  if (proceed)
104  proceed = InitializeSecurity();
105 
106  if (proceed)
107  proceed = InitializeLog();
108 
109  if (proceed)
110  {
111  InitializeApplication();
112  InitializeListeners();
113  InitializeCluster();
114  InitializeScheduler();
115  proceed = InitializeDatabase();
116  }
117 
118  if (proceed)
119  InitializeData();
120 
121  Ready = proceed;
122  firstrun = false;
123  }
124 
128  private void InitializeApplication()
129  {
130  // Start signal controller
131  Controller = new SignalHandler(name, initlog);
132  Controller.Start();
133 
134  // Initialize application state
136  ApplicationState.Pid = pid;
137  ApplicationState.Name = name;
138  ApplicationState.Config = config;
139  ApplicationState.ConfigFile = configFile;
140  ApplicationState.Log = Log;
141 
142  if (config.Heartbeat == null)
143  return;
144 
145  var beat = new Heartbeat(Log, config.Heartbeat.Rate);
146  beat.Start();
147  }
148 
152  private void InitializeCluster()
153  {
154  if (config.Cluster == null)
155  return;
156 
157  try
158  {
159  foreach (var node in config.Cluster.Nodes)
160  {
161  var ip = IPAddress.Parse(node.Ip);
162  var req = new TextRequest(ip, node.Port);
163  Nodes.Add(req);
164  Log.ClusterReady(node);
165  }
166  }
167  catch (Exception e)
168  {
169  initlog.ClusterError(e);
170  }
171  }
172 
176  private bool InitializeConfiguration()
177  {
178  if (!File.Exists(configFile))
179  {
180  initlog.NoConfig(configFile);
181  return false;
182  }
183 
184  var builder = new ConfigBuilder(configFile);
185  config = builder.Execute();
186 
187  if (!builder.Errors.Any() && config != null)
188  return true;
189 
190  initlog.AddErrors(builder.Errors);
191  return false;
192  }
193 
194  private void InitializeData()
195  {
196  if (!config.Database.Initialize)
197  return;
198 
200 
201  try
202  {
203  // Initialize hosts
204  foreach (var server in config.Servers)
205  {
206  var host = DataFace.Instance.Hosts.SingleOrDefault(h => h.Id == server.HostId);
207  if (host == null)
208  {
209  IPAddress ip;
210  try
211  {
212  ip = Dns.GetHostAddresses(server.ServerName)[0];
213  }
214  catch
215  {
216  ip = IPAddress.None;
217  }
218 
219  int? orgId = null;
220  if (TimeServers.List.ContainsKey(server.ServerName))
221  {
222  orgId = TimeServers.List[server.ServerName];
223  Log.KnownServer(orgId.Value, server.ServerName);
224  }
225  else if (!Equals(ip, IPAddress.None) && TimeServers.List.ContainsKey(ip.ToString()))
226  {
227  orgId = TimeServers.List[ip.ToString()];
228  Log.KnownServer(orgId.Value, ip.ToString());
229  }
230 
231  host = new Host(server.HostId, server.ServerName, ip.ToString(), orgId);
232  DataFace.Instance.Hosts.Save(host);
233  Log.NewHost(server, ip);
234  }
235  else if (host.OrgId == null && TimeServers.List.ContainsKey(host.Ip))
236  {
237  host.OrgId = TimeServers.List[host.Ip];
238  Log.KnownServer(host.OrgId.Value, host.Ip);
239  DataFace.Instance.Hosts.Save(host);
240  }
241  else if (host.OrgId == null && TimeServers.List.ContainsKey(host.Name))
242  {
243  host.OrgId = TimeServers.List[host.Name];
244  Log.KnownServer(host.OrgId.Value, host.Name);
245  DataFace.Instance.Hosts.Save(host);
246  }
247 
248  // Flush old association entries
249  DataFace.Instance.AssociationEntries.Delete(server.HostId);
250 
251  // Initialize peers
252  var peerImporter = ImportFactory.CreatePeerImporter(server.ServerName, server.ServerType, host, Log);
253 
254  foreach (var entry in peerImporter.ToList())
255  {
256  var currentEntry = entry;
257  var peerList = DataFace.Instance.Peers.Where(p => p.Ip == currentEntry.Remote).ToList();
258 
259  if (!peerList.Any())
260  {
261  // Create new peer in database
262  TimeServer timeServer = null;
263  if (TimeServers.List.ContainsKey(entry.Remote))
264  {
265  int orgId = TimeServers.List[entry.Remote];
266  timeServer = DataFace.Instance.Servers[orgId];
267  Log.KnownServer(orgId, entry.Remote);
268  }
269 
270  var hostName = entry.Remote;
271 
272  try
273  {
274  var hostEntry = Dns.GetHostEntry(entry.Remote);
275  hostName = hostEntry.HostName;
276  }
277  catch (Exception e)
278  {
279  Log.HostNameNotFound(entry.Remote, e);
280  }
281 
282  var peer = new Peer(hostName, entry.Remote, timeServer);
283  DataFace.Instance.Peers.Save(peer);
284  Log.NewPeer(entry);
285  }
286  else if (TimeServers.List.ContainsKey(entry.Remote))
287  {
288  // Try to update existing peer
289  var peer = peerList.First();
290  if (peer.Server != null && peer.Server.IsOrgServer)
291  continue;
292 
293  int orgId = TimeServers.List[entry.Remote];
294  var timeServer = DataFace.Instance.Servers[orgId];
295  Log.KnownServer(orgId, entry.Remote);
296  if (timeServer == null)
297  continue;
298 
299  peer.Server = timeServer;
300  DataFace.Instance.Peers.Save(peer);
301  }
302  }
303  }
304  }
305  catch (Exception e)
306  {
307  Log.TableError(e);
308  }
309  }
310 
311  private bool InitializeDatabase()
312  {
314  DataFace.Initialize(Log);
315 
316  var checker = new SqlDatabaseChecker(SqlDatabaseFactory.Instance, Controller, initlog);
317  checker.CheckConnection();
318 
319  if (Controller.Stopped)
320  return false;
321 
322  try
323  {
324  var initializer = new DatabaseInitializer(
325  config.Database.Initialize,
326  config.Database.Upgrade,
327  Log);
328 
329  return initializer.Execute();
330  }
331  catch (Exception e)
332  {
333  initlog.DatabaseError(e);
334  }
335 
336  return false;
337  }
338 
342  private void InitializeListeners()
343  {
344  try
345  {
346  foreach (var monitor in config.Monitors)
347  {
348  var listener = new Listener(monitor.Ip, monitor.Port, Log);
349  listener.Open();
350  Listeners.Add(listener);
351  initlog.ListenerReady(listener);
352  }
353  }
354  catch (Exception e)
355  {
356  initlog.ListenerError(e);
357  }
358  }
359 
363  private bool InitializeLog()
364  {
365  try
366  {
367  Log = LogFactory.CreateLog(config.Log);
368  Log.Initialize();
369  }
370  catch (Exception e)
371  {
372  initlog.InitializationError(e);
373  return false;
374  }
375 
376  initlog.Add(Log);
377 
378  if (firstrun)
379  initlog.Starting(version);
380 
381  initlog.ConfigFile(configFile);
382  initlog.ProcessId(pid);
383  initlog.InstanceName(name);
384 
385  if (config.Permission?.AppUserId == null)
386  return true;
387 
388  if (Permission.SetUserId(config.Permission.AppUserId.Value))
389  initlog.UserId(config.Permission.AppUserId.Value);
390 
391  return true;
392  }
393 
397  private void InitializeScheduler()
398  {
399  Job.Reset();
400 
401  try
402  {
403  Scheduler = new Scheduler(Log);
404  Log.JobInitStart();
405 
406  foreach (var bulk in config.Bulks)
407  Scheduler.Add(new BulkStatJob(bulk, Scheduler.Log));
408 
409  // Add jobs to schedule.
410  foreach (var server in config.Servers)
411  {
412  Scheduler.Add(new HostStatJob(server.HostStats, Scheduler.Log));
413  Scheduler.Add(new HostIoStatJob(server.HostIoStats, Scheduler.Log));
414  Scheduler.Add(new PeerStatJob(server.PeerStats, Scheduler.Log));
415  Scheduler.Add(new DriftStatJob(server.DriftStats, Scheduler.Log));
416  Scheduler.Add(new AboutPageJob(server.Menu, server.AboutPage, Scheduler.Log));
417 
418  foreach (var peerPage in server.PeerPages)
419  Scheduler.Add(new PeerPageJob(server.Menu, peerPage, Scheduler.Log));
420 
421  foreach (var hostPage in server.HostPages)
422  Scheduler.Add(new HostPageJob(server.Menu, hostPage, Scheduler.Log));
423 
424  foreach (var hostGraph in server.HostGraphs)
425  Scheduler.Add(new HostGraphJob(hostGraph, Scheduler.Log));
426 
427  foreach (var trafficGraph in server.TrafficGraphs)
428  Scheduler.Add(new TrafficGraphJob(trafficGraph, Scheduler.Log));
429 
430  foreach (var peerGraph in server.PeerGraphs)
431  Scheduler.Add(new PeerGraphJob(peerGraph, Scheduler.Log));
432 
433  foreach (var peerSummaryPage in server.PeerSummaryPages)
434  Scheduler.Add(new PeerSummaryJob(server.Menu, peerSummaryPage, Scheduler.Log));
435 
436  Scheduler.Add(new HostGraphPageJob(server.HostGraphPage, Scheduler.Log));
437  Scheduler.Add(new PeerGraphPageJob(server.PeerGraphPage, Scheduler.Log));
438  }
439 
440  // Add the surveillance/notify job.
441  if (config.Notify != null)
442  {
443  var adminJob = new NotifyJob(config.Notify, Log, pid, configFile);
444  Scheduler.Add(adminJob);
445  adminJob.SetJobList(Scheduler);
446  }
447 
448  Log.JobInitEnd();
449  }
450  catch (Exception e)
451  {
452  initlog.SchedulerError(e);
453  }
454 
457  }
458 
462  private bool InitializeSecurity()
463  {
464  // Drop privileges
465  if (config.Permission?.AppUserId != null)
466  {
467  if (!Permission.SetUserId(config.Permission.AppUserId.Value))
468  {
469  initlog.UserIdError(config.Permission.AppUserId.Value);
470  }
471  }
472 
473  // Create pid file
474  if (pidFile != null)
475  {
476  try
477  {
478  File.WriteAllText(pidFile, pid.ToString(CultureInfo.InvariantCulture));
479  }
480  catch (Exception e)
481  {
482  initlog.PidFileError(e);
483  return false;
484  }
485  }
486 
487  // Set permissions
488  if (config.Permission == null)
489  return true;
490 
494 
495  return true;
496  }
497  }
498 }
static LogBase CreateLog(ILogConfiguration config)
Definition: LogFactory.cs:68
LogBase Log
Gets the log used by this Scheduler.
Definition: Scheduler.cs:82
A scheduler performs scheduling of jobs according to job schedule descriptions.
Definition: Scheduler.cs:35
bool InitializeConfiguration()
Initializes the configuration.
Definition: Initializer.cs:176
void Add(JobDescription description)
Add the specified job to the scheduler queue.
Definition: Scheduler.cs:130
HostDatabaseMapper Hosts
Gets the host mapper.
Definition: DataFace.cs:57
void InitializeCluster()
Initializes the cluster nodes.
Definition: Initializer.cs:152
bool InitializeSecurity()
Initializes the security settings.
Definition: Initializer.cs:462
Job which read statistics from an ntp drift file and saves the result to a database.
Definition: DriftStatJob.cs:37
static Importer< AssociationEntry > CreatePeerImporter(string address, ServerType type, Host host, LogBase log)
TimeServerDatabaseMapper Servers
Gets the time server mapper.
Definition: DataFace.cs:69
static void Initialize(IDatabaseConfiguration factoryConfiguration)
static void Reset()
Definition: Job.cs:167
void InitializeScheduler()
Initializes the scheduler.
Definition: Initializer.cs:397
Job which read statistics about an ntp host and saves the result to a database.
Definition: HostStatJob.cs:37
uint FileMode
The file mode to apply for created files.
bool InitializeLog()
Initializes the log.
Definition: Initializer.cs:363
void InitializeListeners()
Initializes the listeners.
Definition: Initializer.cs:342
var e
Definition: bootstrap.min.js:6
IEnumerable< ListenerConfiguration > Monitors
static bool SetUserId(uint userId)
Definition: Permission.cs:68
IEnumerable< ReadingBulkConfiguration > Bulks
IEnumerable< HostConfiguration > Servers
AssociationEntryMapper AssociationEntries
Gets the association entry mapper.
Definition: DataFace.cs:111
PeerDatabaseMapper Peers
Gets the peer mapper.
Definition: DataFace.cs:63
static void Initialize(LogBase log)
Definition: DataFace.cs:113
static SqlDatabaseFactory Instance
void Run()
Run the NTP Analyzer.
Definition: Initializer.cs:96
Singleton facade class used to access memory persistent data.
Definition: DataFace.cs:30
void InitializeApplication()
Initializes the application and database state.
Definition: Initializer.cs:128
static readonly Dictionary< string, int > List
Definition: TimeServers.cs:28
Job which read statistics about peers and saves the result to a database.
Definition: PeerStatJob.cs:38
static DataFace Instance
Gets the Singleton instance.
Definition: DataFace.cs:51
var version
Definition: bootstrap.js:13
DateTime StartTime
Gets the start up time of this Scheduler.
Definition: Scheduler.cs:76
abstract void Initialize()
PermissionConfiguration Permission
Initializer(string configFile, int pid, string pidFile, string name, LogGroup initlog)
Initializes a new instance of the Initializer class.
Definition: Initializer.cs:60
IEnumerable< LogConfiguration > Log