Beta 4.4.
parent
c1174d29a4
commit
488f72a940
|
@ -10,6 +10,7 @@ using System.Threading;
|
||||||
using sharp.logging;
|
using sharp.logging;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Converters;
|
using Newtonsoft.Json.Converters;
|
||||||
|
using ln.skyscanner.checks;
|
||||||
namespace ln.skyscanner
|
namespace ln.skyscanner
|
||||||
{
|
{
|
||||||
[JsonConverter(typeof(StringEnumConverter))]
|
[JsonConverter(typeof(StringEnumConverter))]
|
||||||
|
@ -27,6 +28,7 @@ namespace ln.skyscanner
|
||||||
public HTTPServer HTTPServer { get; private set; }
|
public HTTPServer HTTPServer { get; private set; }
|
||||||
public Crawler Crawler { get; private set; }
|
public Crawler Crawler { get; private set; }
|
||||||
public SkyEntities Entities { get; private set; }
|
public SkyEntities Entities { get; private set; }
|
||||||
|
public SkyChecker Checker { get; private set; }
|
||||||
|
|
||||||
public MemoryLogger MemoryLogger { get; private set; }
|
public MemoryLogger MemoryLogger { get; private set; }
|
||||||
|
|
||||||
|
@ -47,6 +49,7 @@ namespace ln.skyscanner
|
||||||
Arguments = args;
|
Arguments = args;
|
||||||
|
|
||||||
Entities = new SkyEntities(this);
|
Entities = new SkyEntities(this);
|
||||||
|
Checker = new SkyChecker();
|
||||||
}
|
}
|
||||||
private SkyScanner()
|
private SkyScanner()
|
||||||
{
|
{
|
||||||
|
@ -63,6 +66,7 @@ namespace ln.skyscanner
|
||||||
StartHttpServer();
|
StartHttpServer();
|
||||||
StartCrawler();
|
StartCrawler();
|
||||||
|
|
||||||
|
Checker.Start();
|
||||||
|
|
||||||
lock (this)
|
lock (this)
|
||||||
{
|
{
|
||||||
|
@ -74,6 +78,8 @@ namespace ln.skyscanner
|
||||||
{
|
{
|
||||||
Logging.Log(LogLevel.INFO, "SkyScanner: Stop()");
|
Logging.Log(LogLevel.INFO, "SkyScanner: Stop()");
|
||||||
|
|
||||||
|
Checker.Stop();
|
||||||
|
|
||||||
StopCrawler();
|
StopCrawler();
|
||||||
|
|
||||||
new Thread(() => ConveniencePool.Close()).Start();
|
new Thread(() => ConveniencePool.Close()).Start();
|
||||||
|
|
|
@ -1,72 +0,0 @@
|
||||||
// /**
|
|
||||||
// * File: Hostalive.cs
|
|
||||||
// * Author: haraldwolff
|
|
||||||
// *
|
|
||||||
// * This file and it's content is copyrighted by the Author and / or copyright holder.
|
|
||||||
// * Any use wihtout proper permission is illegal and may lead to legal actions.
|
|
||||||
// *
|
|
||||||
// *
|
|
||||||
// **/
|
|
||||||
using System;
|
|
||||||
using ln.types;
|
|
||||||
using ln.skyscanner.entities;
|
|
||||||
using ln.perfdb;
|
|
||||||
using System.Net.NetworkInformation;
|
|
||||||
using ln.logging;
|
|
||||||
using ln.perfdb.storage;
|
|
||||||
namespace ln.skyscanner.check
|
|
||||||
{
|
|
||||||
public class Hostalive : Check
|
|
||||||
{
|
|
||||||
public static String CalcCheckName(Node host)
|
|
||||||
{
|
|
||||||
return String.Format("hostalive-{0}", host.PrimaryIP.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public Node Host;
|
|
||||||
|
|
||||||
public Hostalive(Node node)
|
|
||||||
:base(CalcCheckName(node))
|
|
||||||
{
|
|
||||||
Host = node;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void CheckImplementation(IPerfFileProvider perfProvider)
|
|
||||||
{
|
|
||||||
Ping ping = new Ping();
|
|
||||||
long roundTripTime = 0;
|
|
||||||
int success = 0;
|
|
||||||
int n;
|
|
||||||
|
|
||||||
for (n = 0; n < 4; n++)
|
|
||||||
{
|
|
||||||
PingReply reply = ping.Send(Host.PrimaryIP, 250);
|
|
||||||
if (reply.Status == IPStatus.Success)
|
|
||||||
{
|
|
||||||
success++;
|
|
||||||
roundTripTime += reply.RoundtripTime;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
float fSuccess = (float)success / (float)n;
|
|
||||||
|
|
||||||
PerfFile pfSuccess = perfProvider.GetPerfFile(GetPerfName("LOSS"));
|
|
||||||
pfSuccess.Write(new PerfValue(fSuccess));
|
|
||||||
|
|
||||||
if (success > 0)
|
|
||||||
{
|
|
||||||
roundTripTime /= success;
|
|
||||||
Logging.Log(LogLevel.INFO, "HOSTALIVE: IP {0} reachable ({1}/10) {2}ms", Host.PrimaryIP, success, roundTripTime);
|
|
||||||
|
|
||||||
PerfFile pfRoundTrip = perfProvider.GetPerfFile(GetPerfName("RTA"));
|
|
||||||
pfRoundTrip.Write(new PerfValue(roundTripTime));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logging.Log(LogLevel.INFO, "HOSTALIVE: IP {0} unreachable", Host.PrimaryIP);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
// /**
|
||||||
|
// * File: CheckTask.cs
|
||||||
|
// * Author: haraldwolff
|
||||||
|
// *
|
||||||
|
// * This file and it's content is copyrighted by the Author and / or copyright holder.
|
||||||
|
// * Any use wihtout proper permission is illegal and may lead to legal actions.
|
||||||
|
// *
|
||||||
|
// *
|
||||||
|
// **/
|
||||||
|
using System;
|
||||||
|
using ln.types.threads;
|
||||||
|
using ln.skyscanner.entities;
|
||||||
|
using ln.logging;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
namespace ln.skyscanner.checks
|
||||||
|
{
|
||||||
|
public class CheckJob : PoolJob
|
||||||
|
{
|
||||||
|
[JsonIgnore]
|
||||||
|
public Node Node { get; }
|
||||||
|
|
||||||
|
public CheckJob(Node node)
|
||||||
|
{
|
||||||
|
Name = String.Format("Interval check: {0} [{1}]",node.UniqueIdentity,node.PrimaryIP.ToString());
|
||||||
|
Node = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void RunJob()
|
||||||
|
{
|
||||||
|
SkyCheck[] checks = SkyCheck.SkyChecks;
|
||||||
|
|
||||||
|
for (int n=0;n<checks.Length;n++)
|
||||||
|
{
|
||||||
|
setState("current check: {0}", checks[n].Name);
|
||||||
|
Progress = (double)n / (double)checks.Length;
|
||||||
|
|
||||||
|
if (checks[n].IsValid(Node))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
checks[n].Check(SkyScanner.Instance.Checker, Node);
|
||||||
|
} catch (Exception e)
|
||||||
|
{
|
||||||
|
Logging.Log(e);
|
||||||
|
if (checks[n].IsCritical)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (checks[n].IsCritical)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return Node.UniqueIdentity.GetHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
if (obj is CheckJob)
|
||||||
|
{
|
||||||
|
return Node.UniqueIdentity.Equals((obj as CheckJob).Node.UniqueIdentity);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
// /**
|
||||||
|
// * File: Hostalive.cs
|
||||||
|
// * Author: haraldwolff
|
||||||
|
// *
|
||||||
|
// * This file and it's content is copyrighted by the Author and / or copyright holder.
|
||||||
|
// * Any use wihtout proper permission is illegal and may lead to legal actions.
|
||||||
|
// *
|
||||||
|
// *
|
||||||
|
// **/
|
||||||
|
using System;
|
||||||
|
using ln.types;
|
||||||
|
using ln.skyscanner.entities;
|
||||||
|
using ln.perfdb;
|
||||||
|
using System.Net.NetworkInformation;
|
||||||
|
using ln.logging;
|
||||||
|
using ln.perfdb.storage;
|
||||||
|
namespace ln.skyscanner.checks
|
||||||
|
{
|
||||||
|
public class Hostalive : SkyCheck
|
||||||
|
{
|
||||||
|
Ping Ping { get; }
|
||||||
|
|
||||||
|
public Hostalive()
|
||||||
|
: base("hostalive")
|
||||||
|
{
|
||||||
|
Ping = new Ping();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool IsValid(Node node) => node.PrimaryIP != null;
|
||||||
|
|
||||||
|
public override void Check(SkyChecker skyChecker, Node node)
|
||||||
|
{
|
||||||
|
long roundTripTime = 0;
|
||||||
|
int success = 0;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
for (n = 0; n < 4; n++)
|
||||||
|
{
|
||||||
|
PingReply reply = Ping.Send(node.PrimaryIP, 250);
|
||||||
|
if (reply.Status == IPStatus.Success)
|
||||||
|
{
|
||||||
|
success++;
|
||||||
|
roundTripTime += reply.RoundtripTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float fSuccess = (float)success / (float)n;
|
||||||
|
|
||||||
|
skyChecker.WritePerfValue(this,"replies",node,fSuccess);
|
||||||
|
if (success > 0)
|
||||||
|
{
|
||||||
|
roundTripTime /= success;
|
||||||
|
skyChecker.WritePerfValue(this, "rta", node, roundTripTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
// /**
|
||||||
|
// * File: SkyCheck.cs
|
||||||
|
// * Author: haraldwolff
|
||||||
|
// *
|
||||||
|
// * This file and it's content is copyrighted by the Author and / or copyright holder.
|
||||||
|
// * Any use wihtout proper permission is illegal and may lead to legal actions.
|
||||||
|
// *
|
||||||
|
// *
|
||||||
|
// **/
|
||||||
|
using System;
|
||||||
|
using ln.skyscanner.entities;
|
||||||
|
using System.IO;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
namespace ln.skyscanner.checks
|
||||||
|
{
|
||||||
|
public abstract class SkyCheck
|
||||||
|
{
|
||||||
|
/* Static */
|
||||||
|
static List<SkyCheck> skyChecks = new List<SkyCheck>();
|
||||||
|
public static SkyCheck[] SkyChecks => skyChecks.ToArray();
|
||||||
|
public static void AddSkyCheck(SkyCheck skyCheck) => skyChecks.Add(skyCheck);
|
||||||
|
|
||||||
|
/* Instance */
|
||||||
|
|
||||||
|
public SkyCheck(String checkName)
|
||||||
|
{
|
||||||
|
Name = checkName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name { get; }
|
||||||
|
|
||||||
|
public virtual bool IsCritical => false;
|
||||||
|
|
||||||
|
public abstract bool IsValid(Node node);
|
||||||
|
public abstract void Check(SkyChecker skyChecker,Node node);
|
||||||
|
|
||||||
|
|
||||||
|
static SkyCheck()
|
||||||
|
{
|
||||||
|
AddSkyCheck(new Hostalive());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,167 @@
|
||||||
|
// /**
|
||||||
|
// * File: SkyChecker.cs
|
||||||
|
// * Author: haraldwolff
|
||||||
|
// *
|
||||||
|
// * This file and it's content is copyrighted by the Author and / or copyright holder.
|
||||||
|
// * Any use wihtout proper permission is illegal and may lead to legal actions.
|
||||||
|
// *
|
||||||
|
// *
|
||||||
|
// **/
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using ln.types.btree;
|
||||||
|
using ln.perfdb.storage;
|
||||||
|
using ln.skyscanner.entities;
|
||||||
|
using ln.types.threads;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Linq;
|
||||||
|
using ln.logging;
|
||||||
|
namespace ln.skyscanner.checks
|
||||||
|
{
|
||||||
|
public class SkyChecker
|
||||||
|
{
|
||||||
|
public string BasePath { get; }
|
||||||
|
|
||||||
|
public PoolJob[] CurrentJobs => checkPool.CurrentPoolJobs;
|
||||||
|
public PoolJob[] QueuedJobs => checkPool.QueuedJobs;
|
||||||
|
|
||||||
|
public string[] PerfNames => perfFiles.Keys.ToArray();
|
||||||
|
public bool ContainsPerfFile(String perfName) => perfFiles.ContainsKey(perfName);
|
||||||
|
|
||||||
|
BTree<string, PerfFile> perfFiles = new BTree<string, PerfFile>();
|
||||||
|
|
||||||
|
Pool checkPool = new Pool(0);
|
||||||
|
Thread threadScheduler;
|
||||||
|
|
||||||
|
bool stopping;
|
||||||
|
|
||||||
|
public SkyChecker()
|
||||||
|
{
|
||||||
|
BasePath = Path.Combine(SkyScanner.Instance.BasePath, "perfdb");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Start()
|
||||||
|
{
|
||||||
|
if ((threadScheduler != null) && (threadScheduler.IsAlive))
|
||||||
|
throw new NotSupportedException("SkyChecker.scheduler() already running");
|
||||||
|
|
||||||
|
checkPool.SetPoolSize(64);
|
||||||
|
|
||||||
|
threadScheduler = new Thread(scheduler);
|
||||||
|
threadScheduler.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Stop()
|
||||||
|
{
|
||||||
|
stopping = true;
|
||||||
|
|
||||||
|
if (threadScheduler!=null && threadScheduler.IsAlive)
|
||||||
|
{
|
||||||
|
threadScheduler.Interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
checkPool.Abort();
|
||||||
|
checkPool.Close();
|
||||||
|
|
||||||
|
foreach (PerfFile perfFile in perfFiles.Values)
|
||||||
|
{
|
||||||
|
if (perfFile.IsOpen)
|
||||||
|
perfFile.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
perfFiles.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WritePerfValue(SkyCheck skyCheck,String vName,Node node,double value)
|
||||||
|
{
|
||||||
|
PerfFile perfFile = GetPerfFile(skyCheck, vName, node);
|
||||||
|
lock (perfFile)
|
||||||
|
{
|
||||||
|
perfFile.EnsureOpen();
|
||||||
|
perfFile.Write(DateTimeOffset.Now.ToUnixTimeSeconds(), value);
|
||||||
|
perfFile.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public PerfFile GetPerfFile(string perfName)
|
||||||
|
{
|
||||||
|
return perfFiles[perfName];
|
||||||
|
}
|
||||||
|
|
||||||
|
private PerfFile GetPerfFile(SkyCheck skyCheck, String vName, Node node)
|
||||||
|
{
|
||||||
|
String checkPath = String.Format("{0}_{1}", node.UniqueIdentity, skyCheck.Name);
|
||||||
|
String perfName = String.Format("{0}_{1}",checkPath,vName);
|
||||||
|
|
||||||
|
if (node.AddCheck(perfName))
|
||||||
|
{
|
||||||
|
SkyScanner.Instance.Entities.nodeCollection.Upsert(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (this)
|
||||||
|
{
|
||||||
|
if (!perfFiles.ContainsKey(perfName))
|
||||||
|
{
|
||||||
|
String perfDirectory = Path.Combine(BasePath, node.UniqueIdentity, skyCheck.Name);
|
||||||
|
if (!Directory.Exists(perfDirectory))
|
||||||
|
Directory.CreateDirectory(perfDirectory);
|
||||||
|
|
||||||
|
String perfFileName = Path.Combine(BasePath, node.UniqueIdentity, skyCheck.Name, String.Format("{0}.perf", vName));
|
||||||
|
|
||||||
|
PerfFile perfFile = new PerfFile(perfFileName);
|
||||||
|
perfFile.Open();
|
||||||
|
|
||||||
|
if (perfFile.FirstSection == null)
|
||||||
|
{
|
||||||
|
PerfFile.PerfFileSection s1 = new PerfFile.PerfFileSection(perfFile, null, TimeSpan.FromDays(28), 60, AggregationMethod.AVERAGE);
|
||||||
|
PerfFile.PerfFileSection s2 = new PerfFile.PerfFileSection(perfFile, s1, TimeSpan.FromDays(56), 300, AggregationMethod.AVERAGE);
|
||||||
|
PerfFile.PerfFileSection s3 = new PerfFile.PerfFileSection(perfFile, s2, TimeSpan.FromDays(84), 900, AggregationMethod.AVERAGE);
|
||||||
|
PerfFile.PerfFileSection s4 = new PerfFile.PerfFileSection(perfFile, s3, TimeSpan.FromDays(168), 1800, AggregationMethod.AVERAGE);
|
||||||
|
PerfFile.PerfFileSection s5 = new PerfFile.PerfFileSection(perfFile, s4, TimeSpan.FromDays(750), 3600, AggregationMethod.AVERAGE);
|
||||||
|
}
|
||||||
|
perfFile.Close();
|
||||||
|
|
||||||
|
perfFiles.Add(perfName, perfFile);
|
||||||
|
}
|
||||||
|
return perfFiles[perfName];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void scheduler()
|
||||||
|
{
|
||||||
|
long nextMinute = DateTimeOffset.Now.ToUnixTimeMilliseconds();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (!stopping)
|
||||||
|
{
|
||||||
|
int n = 0;
|
||||||
|
foreach (Node node in SkyScanner.Instance.Entities.GlobalNetwork.Nodes)
|
||||||
|
{
|
||||||
|
CheckJob checkJob = new CheckJob(node);
|
||||||
|
if (checkPool.Enqueue(checkJob))
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
Logging.Log(LogLevel.INFO, "SkyChecker.scheduler(): scheduled {0} checks", n);
|
||||||
|
|
||||||
|
while ((nextMinute - DateTimeOffset.Now.ToUnixTimeMilliseconds()) < 0)
|
||||||
|
nextMinute += 60000;
|
||||||
|
|
||||||
|
Thread.Sleep((int)(nextMinute - DateTimeOffset.Now.ToUnixTimeMilliseconds()));
|
||||||
|
}
|
||||||
|
} catch (ThreadInterruptedException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public ComponentState State
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return ComponentState.INITIALIZED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,6 +22,7 @@ using Newtonsoft.Json;
|
||||||
using ln.skyscanner.crawl.tests;
|
using ln.skyscanner.crawl.tests;
|
||||||
using System.Runtime.Remoting.Messaging;
|
using System.Runtime.Remoting.Messaging;
|
||||||
using ln.skyscanner.crawl.service;
|
using ln.skyscanner.crawl.service;
|
||||||
|
using ln.types.net;
|
||||||
namespace ln.skyscanner.crawl
|
namespace ln.skyscanner.crawl
|
||||||
{
|
{
|
||||||
public class Crawl : PoolJob
|
public class Crawl : PoolJob
|
||||||
|
@ -114,6 +115,9 @@ namespace ln.skyscanner.crawl
|
||||||
|
|
||||||
bool updated = Crawler.CrawledHosts.Upsert( Host );
|
bool updated = Crawler.CrawledHosts.Upsert( Host );
|
||||||
|
|
||||||
|
foreach (Network4 network in Host.Networks)
|
||||||
|
Crawler.EnsureSubnet(network);
|
||||||
|
|
||||||
setState("Updating global network");
|
setState("Updating global network");
|
||||||
SkyScanner.Instance.Entities.GlobalNetwork.EnqueueUpdate(Host);
|
SkyScanner.Instance.Entities.GlobalNetwork.EnqueueUpdate(Host);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,24 +13,13 @@ using System.Net;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using ln.logging;
|
using ln.logging;
|
||||||
using ln.types;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using ln.types.serialize;
|
|
||||||
using ln.skyscanner.entities;
|
|
||||||
using System.Net.NetworkInformation;
|
|
||||||
using ln.snmp;
|
|
||||||
using ln.snmp.endpoint;
|
|
||||||
using ln.perfdb;
|
|
||||||
using ln.perfdb.storage;
|
|
||||||
using ln.skyscanner.check;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using ln.snmp.types;
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Converters;
|
using Newtonsoft.Json.Converters;
|
||||||
using ln.types.odb;
|
using ln.types.odb;
|
||||||
using ln.skyscanner.crawl.service;
|
using ln.skyscanner.crawl.service;
|
||||||
using ln.skyscanner.crawl.tests;
|
using ln.skyscanner.crawl.tests;
|
||||||
using LiteDB;
|
|
||||||
using ln.types.net;
|
using ln.types.net;
|
||||||
|
|
||||||
namespace ln.skyscanner.crawl
|
namespace ln.skyscanner.crawl
|
||||||
|
@ -43,12 +32,12 @@ namespace ln.skyscanner.crawl
|
||||||
CrawlService.RegisterService(new SNMP(new string[] { "VhclfC7lfIojYZ", "Vhclf(C7$lfIojYZ", "ByFR4oW98hap", "qVy3hnZJ2fov" }));
|
CrawlService.RegisterService(new SNMP(new string[] { "VhclfC7lfIojYZ", "Vhclf(C7$lfIojYZ", "ByFR4oW98hap", "qVy3hnZJ2fov" }));
|
||||||
CrawlService.RegisterService(new RFC1213());
|
CrawlService.RegisterService(new RFC1213());
|
||||||
CrawlService.RegisterService(new HTTP());
|
CrawlService.RegisterService(new HTTP());
|
||||||
|
CrawlService.RegisterService(new SSH());
|
||||||
}
|
}
|
||||||
|
|
||||||
public SkyScanner SkyScanner { get; }
|
public SkyScanner SkyScanner { get; }
|
||||||
|
|
||||||
public String BasePath { get; set; }
|
public String BasePath { get; set; }
|
||||||
public String PerfPath => Path.Combine(BasePath, "perfdb");
|
|
||||||
public string PoolPath => Path.Combine(BasePath, "pool");
|
public string PoolPath => Path.Combine(BasePath, "pool");
|
||||||
public String DBFileName => Path.Combine(PoolPath, "crawler.db");
|
public String DBFileName => Path.Combine(PoolPath, "crawler.db");
|
||||||
|
|
||||||
|
@ -86,8 +75,6 @@ namespace ln.skyscanner.crawl
|
||||||
|
|
||||||
if (!Directory.Exists(BasePath))
|
if (!Directory.Exists(BasePath))
|
||||||
Directory.CreateDirectory(BasePath);
|
Directory.CreateDirectory(BasePath);
|
||||||
if (!Directory.Exists(PerfPath))
|
|
||||||
Directory.CreateDirectory(PerfPath);
|
|
||||||
if (!Directory.Exists(PoolPath))
|
if (!Directory.Exists(PoolPath))
|
||||||
Directory.CreateDirectory(PoolPath);
|
Directory.CreateDirectory(PoolPath);
|
||||||
|
|
||||||
|
@ -173,7 +160,10 @@ namespace ln.skyscanner.crawl
|
||||||
|
|
||||||
public void EnsureSubnet(Network4 network)
|
public void EnsureSubnet(Network4 network)
|
||||||
{
|
{
|
||||||
FindSubnet(network);
|
lock (this)
|
||||||
|
{
|
||||||
|
FindSubnet(network);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Enqueue(JobDelegate job)
|
public void Enqueue(JobDelegate job)
|
||||||
|
|
|
@ -67,7 +67,6 @@ namespace ln.skyscanner.crawl.service
|
||||||
}
|
}
|
||||||
catch (WebException e)
|
catch (WebException e)
|
||||||
{
|
{
|
||||||
Logging.Log(e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -13,9 +13,10 @@ using ln.types;
|
||||||
using Renci.SshNet.Common;
|
using Renci.SshNet.Common;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using ln.types.net;
|
using ln.types.net;
|
||||||
|
using ln.skyscanner.crawl.service;
|
||||||
namespace ln.skyscanner.crawl.tests
|
namespace ln.skyscanner.crawl.tests
|
||||||
{
|
{
|
||||||
public static class SSH
|
public class SSH : CrawlService
|
||||||
{
|
{
|
||||||
static string[] defaultPasswords = new string[]
|
static string[] defaultPasswords = new string[]
|
||||||
{
|
{
|
||||||
|
@ -34,6 +35,10 @@ namespace ln.skyscanner.crawl.tests
|
||||||
"DVqxof1JQ9at"
|
"DVqxof1JQ9at"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public SSH()
|
||||||
|
: base("ssh")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public static bool CanConnect(CrawledHost crawledHost)
|
public static bool CanConnect(CrawledHost crawledHost)
|
||||||
{
|
{
|
||||||
|
@ -81,22 +86,6 @@ namespace ln.skyscanner.crawl.tests
|
||||||
|
|
||||||
private static bool CanConnect(CrawledHost crawledHost, IPv4 host, int port, string username, string password,bool throwe = false)
|
private static bool CanConnect(CrawledHost crawledHost, IPv4 host, int port, string username, string password,bool throwe = false)
|
||||||
{
|
{
|
||||||
using (TcpClient tcp = new TcpClient())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
tcp.Connect(host.ToString(), port);
|
|
||||||
if (!tcp.Connected)
|
|
||||||
return false;
|
|
||||||
tcp.Close();
|
|
||||||
} catch (SocketException)
|
|
||||||
{
|
|
||||||
if (throwe)
|
|
||||||
throw;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
using (SshClient client = new SshClient(host.ToString(), port, username, password))
|
using (SshClient client = new SshClient(host.ToString(), port, username, password))
|
||||||
{
|
{
|
||||||
client.ConnectionInfo.Timeout = TimeSpan.FromSeconds(1);
|
client.ConnectionInfo.Timeout = TimeSpan.FromSeconds(1);
|
||||||
|
@ -124,7 +113,15 @@ namespace ln.skyscanner.crawl.tests
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool Check(Crawl crawl)
|
||||||
|
{
|
||||||
|
return CanConnect(crawl.Host);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool HostProvidesOption(Crawl crawl, params object[] parameters)
|
||||||
|
{
|
||||||
|
return crawl.Host.GetHint<IPv4>("ssh.ip", null) != null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
// /**
|
||||||
|
// * File: Ubiquity.cs
|
||||||
|
// * Author: haraldwolff
|
||||||
|
// *
|
||||||
|
// * This file and it's content is copyrighted by the Author and / or copyright holder.
|
||||||
|
// * Any use wihtout proper permission is illegal and may lead to legal actions.
|
||||||
|
// *
|
||||||
|
// *
|
||||||
|
// **/
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using ln.snmp.endpoint;
|
||||||
|
using ln.skyscanner.crawl.tests;
|
||||||
|
using ln.snmp;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using ln.snmp.types;
|
||||||
|
namespace ln.skyscanner.crawl.service
|
||||||
|
{
|
||||||
|
public class Ubiquity : CrawlService
|
||||||
|
{
|
||||||
|
public Ubiquity()
|
||||||
|
: base("ubiquity")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Check(Crawl crawl)
|
||||||
|
{
|
||||||
|
if (crawl.CheckRequiredOption("snmp"))
|
||||||
|
{
|
||||||
|
using (SnmpInterface snmp = SNMP.GetSnmpInterface(crawl.Host))
|
||||||
|
{
|
||||||
|
if (crawl.Host.GetHint("snmp.orids", new string[0]).Contains("1.3.6.1.4.1.41112"))
|
||||||
|
{
|
||||||
|
List<Sequence> test = snmp.snmpWalk("1.3.6.1.4.1.41112.1.3.2.1.11");
|
||||||
|
List<Sequence> test2 = snmp.snmpWalk("1.3.6.1.4.1.41112.1.3.3.1.66");
|
||||||
|
|
||||||
|
|
||||||
|
crawl.Host.SetHint("ubiquity.ptp", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool HostProvidesOption(Crawl crawl, params object[] parameters)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -43,8 +43,6 @@ namespace ln.skyscanner.entities
|
||||||
|
|
||||||
public void EnsureSubnet(Network4 network)
|
public void EnsureSubnet(Network4 network)
|
||||||
{
|
{
|
||||||
SkyScanner.Crawler.EnsureSubnet(network);
|
|
||||||
|
|
||||||
Subnet subnet = SkyEntities.subnetCollection.Where(s => s.Network.Equals(network)).FirstOrDefault();
|
Subnet subnet = SkyEntities.subnetCollection.Where(s => s.Network.Equals(network)).FirstOrDefault();
|
||||||
if (subnet == null)
|
if (subnet == null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -48,7 +48,11 @@ namespace ln.skyscanner.entities
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public IEnumerable<Subnet> Subnets => SkyScanner.Instance.Entities.subnetCollection.Select("Network", Networks.Select(net => net.Network));
|
public IEnumerable<Subnet> Subnets => SkyScanner.Instance.Entities.subnetCollection.Select("Network", Networks.Select(net => net.Network));
|
||||||
|
|
||||||
|
public String UniqueIdentity => PrimaryIP.ToString();
|
||||||
|
public String[] Checks => checks.ToArray();
|
||||||
|
|
||||||
private HashSet<URI> uris = new HashSet<URI>();
|
private HashSet<URI> uris = new HashSet<URI>();
|
||||||
|
private HashSet<string> checks = new HashSet<string>();
|
||||||
|
|
||||||
private Node()
|
private Node()
|
||||||
{
|
{
|
||||||
|
@ -61,6 +65,13 @@ namespace ln.skyscanner.entities
|
||||||
Created = DateTime.Now;
|
Created = DateTime.Now;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool AddCheck(string perfName)
|
||||||
|
{
|
||||||
|
if (checks == null)
|
||||||
|
checks = new HashSet<string>();
|
||||||
|
return checks.Add(perfName);
|
||||||
|
}
|
||||||
|
|
||||||
public void RemoveURI(string scheme)
|
public void RemoveURI(string scheme)
|
||||||
{
|
{
|
||||||
RemoveURI(FindURIs(scheme));
|
RemoveURI(FindURIs(scheme));
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
// /**
|
||||||
|
// * File: CheckerApi.cs
|
||||||
|
// * Author: haraldwolff
|
||||||
|
// *
|
||||||
|
// * This file and it's content is copyrighted by the Author and / or copyright holder.
|
||||||
|
// * Any use wihtout proper permission is illegal and may lead to legal actions.
|
||||||
|
// *
|
||||||
|
// *
|
||||||
|
// **/
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using ln.http;
|
||||||
|
using ln.http.resources;
|
||||||
|
using ln.types.threads;
|
||||||
|
using ln.perfdb.storage;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
namespace ln.skyscanner.http
|
||||||
|
{
|
||||||
|
public class CheckerApi : JsonCallResource
|
||||||
|
{
|
||||||
|
public CheckerApi(Resource container)
|
||||||
|
: base(container, "checker")
|
||||||
|
{
|
||||||
|
new Checks(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Callable]
|
||||||
|
public PoolJob[] CurrentPoolJobs => SkyScanner.Instance.Checker.CurrentJobs;
|
||||||
|
[Callable]
|
||||||
|
public PoolJob[] QueuedPoolJobs => SkyScanner.Instance.Checker.QueuedJobs;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Checks : BaseResource
|
||||||
|
{
|
||||||
|
public Checks(CheckerApi container)
|
||||||
|
:base(container,"checks")
|
||||||
|
{ }
|
||||||
|
|
||||||
|
public override bool Contains(string name)
|
||||||
|
{
|
||||||
|
return base.Contains(name) || SkyScanner.Instance.Checker.ContainsPerfFile(name);
|
||||||
|
}
|
||||||
|
public override Resource GetResource(string name)
|
||||||
|
{
|
||||||
|
if (base.Contains(name))
|
||||||
|
return base.GetResource(name);
|
||||||
|
|
||||||
|
if (SkyScanner.Instance.Checker.ContainsPerfFile(name))
|
||||||
|
{
|
||||||
|
return new Check(this, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new KeyNotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override HttpResponse GetResponse(HttpRequest httpRequest)
|
||||||
|
{
|
||||||
|
HttpResponse response = new HttpResponse(httpRequest);
|
||||||
|
response.SetHeader("content-type", "application/json");
|
||||||
|
response.ContentWriter.Write(JsonConvert.SerializeObject(SkyScanner.Instance.Checker.PerfNames));
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Check : Resource
|
||||||
|
{
|
||||||
|
public Check(Checks container,String perfName)
|
||||||
|
:base(container,perfName)
|
||||||
|
{}
|
||||||
|
|
||||||
|
public override void AddResource(Resource resource) => throw new NotImplementedException();
|
||||||
|
public override bool Contains(string name) => throw new NotImplementedException();
|
||||||
|
public override IEnumerable<Resource> GetResources() => throw new NotImplementedException();
|
||||||
|
public override void RemoveResource(Resource resource) => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public override HttpResponse GetResponse(HttpRequest httpRequest)
|
||||||
|
{
|
||||||
|
PerfFile perfFile = SkyScanner.Instance.Checker.GetPerfFile(Name);
|
||||||
|
PerfValue[] perfValues = null;
|
||||||
|
|
||||||
|
lock (perfFile)
|
||||||
|
{
|
||||||
|
perfFile.EnsureOpen();
|
||||||
|
perfValues = perfFile.QueryTime((int)(TimeSpan.FromHours(1).TotalSeconds), 0);
|
||||||
|
perfFile.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpResponse response = new HttpResponse(httpRequest);
|
||||||
|
response.SetHeader("content-type", "application/json");
|
||||||
|
response.ContentWriter.Write(JsonConvert.SerializeObject(perfValues));
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,6 +17,7 @@ namespace ln.skyscanner.http
|
||||||
new SkyScannerHttpManagement(this,SkyScanner);
|
new SkyScannerHttpManagement(this,SkyScanner);
|
||||||
new CrawlerApi(this, SkyScanner);
|
new CrawlerApi(this, SkyScanner);
|
||||||
new NetworkApi(this);
|
new NetworkApi(this);
|
||||||
|
new CheckerApi(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,7 @@
|
||||||
<Compile Include="entities\Subnet.cs" />
|
<Compile Include="entities\Subnet.cs" />
|
||||||
<Compile Include="entities\NetworkInterface.cs" />
|
<Compile Include="entities\NetworkInterface.cs" />
|
||||||
<Compile Include="Check.cs" />
|
<Compile Include="Check.cs" />
|
||||||
<Compile Include="check\Hostalive.cs" />
|
<Compile Include="checks\Hostalive.cs" />
|
||||||
<Compile Include="SkyScanner.cs" />
|
<Compile Include="SkyScanner.cs" />
|
||||||
<Compile Include="http\SkyScannerHttpApplication.cs" />
|
<Compile Include="http\SkyScannerHttpApplication.cs" />
|
||||||
<Compile Include="http\SkyScannerHttpApi.cs" />
|
<Compile Include="http\SkyScannerHttpApi.cs" />
|
||||||
|
@ -77,6 +77,11 @@
|
||||||
<Compile Include="crawl\service\TCP.cs" />
|
<Compile Include="crawl\service\TCP.cs" />
|
||||||
<Compile Include="entities\ConfiguredIP.cs" />
|
<Compile Include="entities\ConfiguredIP.cs" />
|
||||||
<Compile Include="crawl\service\HTTP.cs" />
|
<Compile Include="crawl\service\HTTP.cs" />
|
||||||
|
<Compile Include="crawl\service\Ubiquity.cs" />
|
||||||
|
<Compile Include="checks\SkyChecker.cs" />
|
||||||
|
<Compile Include="checks\SkyCheck.cs" />
|
||||||
|
<Compile Include="checks\CheckJob.cs" />
|
||||||
|
<Compile Include="http\CheckerApi.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
|
@ -147,10 +152,19 @@
|
||||||
<None Include="templates\static\network\hoptable.html">
|
<None Include="templates\static\network\hoptable.html">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
|
<None Include="templates\static\css\Chart.min.css">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Include="templates\static\dist\Chart.min.js">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Include="templates\static\checks\index.html">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="identify\" />
|
<Folder Include="identify\" />
|
||||||
<Folder Include="check\" />
|
<Folder Include="checks\" />
|
||||||
<Folder Include="entities\" />
|
<Folder Include="entities\" />
|
||||||
<Folder Include="crawl\" />
|
<Folder Include="crawl\" />
|
||||||
<Folder Include="http\" />
|
<Folder Include="http\" />
|
||||||
|
@ -160,6 +174,8 @@
|
||||||
<Folder Include="templates\static\css\" />
|
<Folder Include="templates\static\css\" />
|
||||||
<Folder Include="crawl\service\" />
|
<Folder Include="crawl\service\" />
|
||||||
<Folder Include="templates\static\network\" />
|
<Folder Include="templates\static\network\" />
|
||||||
|
<Folder Include="http\check\" />
|
||||||
|
<Folder Include="templates\static\checks\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\ln.snmp\ln.snmp.csproj">
|
<ProjectReference Include="..\ln.snmp\ln.snmp.csproj">
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
<%frame "frame.html"%>
|
||||||
|
|
||||||
|
<h1>Checks</h1>
|
||||||
|
|
||||||
|
<div class="flex row">
|
||||||
|
<div>
|
||||||
|
<select size="25" id="perfList"></select>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<canvas id="perfChart" width="1000" height="500"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
moment.defaultFormat = "DD.MM.YYYY HH:mm";
|
||||||
|
|
||||||
|
var chartCTX = $("#perfChart");
|
||||||
|
var chart = new Chart( chartCTX, {
|
||||||
|
type: 'bar',
|
||||||
|
data: {
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: "-",
|
||||||
|
data: []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
scales: {
|
||||||
|
xAxes: [{
|
||||||
|
type: 'time',
|
||||||
|
time: {
|
||||||
|
unit: "minute",
|
||||||
|
tooltipFormat: "DD.MM.YYYY HH:mm",
|
||||||
|
displayFormats: {
|
||||||
|
minute: "DD.MM.YYYY HH:mm"
|
||||||
|
},
|
||||||
|
parser: moment.unix
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
function drawChart(perfValues)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
var perfList = skyapi().getJson("api/checker/checks", function(checks){
|
||||||
|
$.each( checks, function(){
|
||||||
|
$("#perfList").append( $( "<option value='"+ this +"'>" + this + "</option>" ) );
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#perfList").change( function(e){
|
||||||
|
var perfName = $(this).children("option:selected").val();
|
||||||
|
skyapi().getJson("api/checker/checks/" + perfName, function(perfValues){
|
||||||
|
chart.data.labels.length = 0;
|
||||||
|
chart.data.datasets[0].data.length = 0;
|
||||||
|
chart.data.datasets[0].label = perfName;
|
||||||
|
$.each( perfValues, function(){
|
||||||
|
if (this.TimeStamp != 0)
|
||||||
|
{
|
||||||
|
//chart.data.labels.push(this.TimeStamp);
|
||||||
|
chart.data.datasets[0].data.push( { x: this.TimeStamp, y: this.Value } );
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
chart.update();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
|
@ -0,0 +1 @@
|
||||||
|
@keyframes chartjs-render-animation{from{opacity:.99}to{opacity:1}}.chartjs-render-monitor{animation:chartjs-render-animation 1ms}.chartjs-size-monitor,.chartjs-size-monitor-expand,.chartjs-size-monitor-shrink{position:absolute;direction:ltr;left:0;top:0;right:0;bottom:0;overflow:hidden;pointer-events:none;visibility:hidden;z-index:-1}.chartjs-size-monitor-expand>div{position:absolute;width:1000000px;height:1000000px;left:0;top:0}.chartjs-size-monitor-shrink>div{position:absolute;width:200%;height:200%;left:0;top:0}
|
File diff suppressed because one or more lines are too long
|
@ -6,7 +6,8 @@
|
||||||
|
|
||||||
<link href="/static/css/jquery-ui.min.css" rel="stylesheet" />
|
<link href="/static/css/jquery-ui.min.css" rel="stylesheet" />
|
||||||
<link href="/static/css/datatables.min.css" rel="stylesheet" />
|
<link href="/static/css/datatables.min.css" rel="stylesheet" />
|
||||||
|
<link href="/static/css/Chart.min.css" rel="stylesheet" />
|
||||||
|
|
||||||
<link href="/static/css/style.css" rel="stylesheet" />
|
<link href="/static/css/style.css" rel="stylesheet" />
|
||||||
|
|
||||||
<script type="text/javascript" src="/static/skyapi.js"></script>
|
<script type="text/javascript" src="/static/skyapi.js"></script>
|
||||||
|
@ -16,6 +17,7 @@
|
||||||
<script type="text/javascript" src="/static/dist/moment-with-locales.js"></script>
|
<script type="text/javascript" src="/static/dist/moment-with-locales.js"></script>
|
||||||
<script type="text/javascript" src="/static/dist/datatables.min.js"></script>
|
<script type="text/javascript" src="/static/dist/datatables.min.js"></script>
|
||||||
<script type="text/javascript" src="/static/dist/percentageBars.js"></script>
|
<script type="text/javascript" src="/static/dist/percentageBars.js"></script>
|
||||||
|
<script type="text/javascript" src="/static/dist/Chart.min.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
|
@ -28,6 +28,9 @@
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h1>Überwachung</h1>
|
<h1>Überwachung</h1>
|
||||||
|
<div>
|
||||||
|
<a href="/static/checks/index.html" onclick="skyapi().loadPage('static/checks/index.html'); return false;"><div>Checks</div></a>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<a href="#" onclick="skyapi().loadPage('static/checker.html'); return false;"><div>Hosts</div></a>
|
<a href="#" onclick="skyapi().loadPage('static/checker.html'); return false;"><div>Hosts</div></a>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue