diff --git a/IPPoolService.cs b/IPPoolService.cs new file mode 100644 index 0000000..932affc --- /dev/null +++ b/IPPoolService.cs @@ -0,0 +1,188 @@ +using System; +using ln.application; +using ln.application.service; +using System.Threading; +using ln.types.odb.ng; +using ln.types; +using System.Collections.Generic; +using System.Linq; +using ln.json; +using ln.types.odb.ng.storage.session; +namespace ln.provider +{ + public class IPPoolService : ApplicationServiceBase + { + CoreService CoreService { get; set; } + ProviderApplication Application => (ProviderApplication)base.CurrentApplicationInterface; + + RPC rpc; + + SessionStorageContainer mapperSession; + Mapper mapper; + + IPAllocation v6Root; + IPAllocation v4Root; + + public IPPoolService() + :base("IPPoolService") + { + DependOnService(); + + rpc = new RPC(this); + } + + public override void ServiceMain(IApplicationInterface applicationInterface) + { + CoreService = Dependency(); + + using (mapperSession = new SessionStorageContainer(CoreService.CoreStorageContainer)) + { + mapper = new Mapper(mapperSession); + + mapper.EnsureIndex("CIDR"); + mapper.EnsureIndex("Pool"); + + Application.RPCContainer.Add("ippool", rpc); + + v6Root = mapper.Load(Query.Equals("CIDR",IPv6.ANY)).FirstOrDefault(); + v4Root = mapper.Load(Query.Equals("CIDR", IPv6.V4Space)).FirstOrDefault(); + + if (v6Root == null) + { + v6Root = new IPAllocation(IPv6.ANY, "Global IPv6 Space", null); + mapper.Save(v6Root); + } + + if (v4Root == null) + { + v4Root = new IPAllocation(IPv6.V4Space, "Global IPv4 Space", v6Root.CIDR); + mapper.Save(v4Root); + } + + Ready(); + + while (!StopRequested) + { + lock (Thread.CurrentThread) + { + Monitor.Wait(Thread.CurrentThread); + } + } + } + + mapper = null; + + Application.RPCContainer.Remove("ippool"); + CoreService = null; + } + + public IPAllocation[] GetAllocations() + { + return mapper.Load().ToArray(); + } + public IPAllocation GetAllocation(IPv6 cidr) + { + return mapper.Load(Query.Equals("CIDR", cidr)).FirstOrDefault(); + } + + public IPAllocation[] GetPoolAllocations(IPv6 pool) + { + return mapper.Load(Query.Equals("Pool", pool)).ToArray(); + } + + + + public IPAllocation AllocateAny(IPv6 zone,int width,string usage) + { + return null; + } + public IPAllocation AllocateCIDR(IPv6 zone,IPv6 cidr,string usage,bool splitZones) + { + IPAllocation pool = GetAllocation(zone); + if (pool == null) + throw new KeyNotFoundException(); + + if (!pool.CIDR.Contains(cidr)) + throw new ArgumentException(String.Format("{0} doesn't contain {1}", pool.CIDR.ToCIDR(), cidr.ToCIDR())); + + if (splitZones) + { + IPAllocation allocation = pool; + while (!allocation.CIDR.Equals(cidr)) + { + foreach (IPv6 splitCIDR in allocation.CIDR.Split(1)) + { + if (splitCIDR.Contains(cidr)) + { + IPAllocation split = GetAllocation(splitCIDR); + if (split == null) + { + split = new IPAllocation(splitCIDR,allocation.CIDR); + mapper.Save(split); + allocation = split; + break; + } + } + } + } + return allocation; + } + else + { + IPAllocation allocation = GetAllocation(cidr); + if (allocation != null) + throw new Exception("Allocation already present"); + + allocation = new IPAllocation(cidr, cidr.ToCIDR(), pool.CIDR); + allocation.AllocatedTo = usage; + + mapper.Save(allocation); + + return allocation; + } + return null; + } + + + + + class RPC + { + IPPoolService poolService; + + public RPC(IPPoolService poolService) + { + this.poolService = poolService; + } + + public IPAllocation[] GetAllocations() => poolService.GetAllocations(); + public IPAllocation GetAllocation(IPv6 cidr) => poolService.GetAllocation(cidr); + public IPAllocation[] GetPoolAllocations(IPv6 pool) => poolService.GetPoolAllocations(pool); + + public IPAllocation AllocateAny(IPv6 zone, int width, string usage) => poolService.AllocateAny(zone, width, usage); + public IPAllocation AllocateCIDR(IPv6 zone, IPv6 cidr, string usage, bool splitZones) => poolService.AllocateCIDR(zone, cidr, usage,splitZones); + + } + + public class IPAllocation + { + public IPv6 CIDR { get; } + public string Name { get; set; } = String.Empty; + + public string AllocatedTo { get; set; } = String.Empty; + + public IPv6 Pool { get; } + + private IPAllocation(){} + public IPAllocation(IPv6 cidr): this(cidr, cidr.ToCIDR(), IPv6.ANY) { } + public IPAllocation(IPv6 cidr, IPv6 pool) : this(cidr, cidr.ToCIDR(), pool) { } + public IPAllocation(IPv6 cidr,String name,IPv6 pool) + { + Pool = pool; + CIDR = cidr; + Name = name; + } + } + + } +} diff --git a/ProviderApplication.cs b/ProviderApplication.cs index c9318c2..1b3d2b6 100644 --- a/ProviderApplication.cs +++ b/ProviderApplication.cs @@ -10,6 +10,8 @@ using ln.types.odb.ng.storage; using System.Security.Cryptography; using System.Linq; using ln.types; +using ln.types.odb.ng.storage.session; +using ln.types.odb.ng.storage.fs; namespace ln.provider { public class ProviderApplication : Application @@ -28,12 +30,10 @@ namespace ln.provider if (Directory.Exists("../../www")) WWWPath = "../../www"; - ServiceDefinition coreRPCService = new ServiceDefinition("ln.provider.CoreService"); - coreRPCService.AutoStart = true; + ServiceDefinition coreRPCService = ServiceDefinition.From(true); + ServiceContainer.Add(coreRPCService); - ServiceContainer.Add( - coreRPCService - ); + ServiceContainer.Add(ServiceDefinition.From(true)); } public override void PrepareStart() @@ -66,7 +66,7 @@ namespace ln.provider public ProviderApplication Application => (ProviderApplication)base.CurrentApplicationInterface; public IStorageContainer CoreStorageContainer { get; private set; } - public Session CoreStorageSession { get; private set; } + public SessionStorageContainer CoreStorageSession { get; private set; } public Mapper CoreStorageMapper { get; private set; } public CoreService() @@ -79,7 +79,7 @@ namespace ln.provider CoreStorageContainer = new FSStorageContainer("/var/cache/ln.provider"); CoreStorageContainer.Open(); - CoreStorageSession = new Session(CoreStorageContainer); + CoreStorageSession = new SessionStorageContainer(CoreStorageContainer); CoreStorageMapper = new Mapper(CoreStorageSession); CoreStorageMapper.EnsureIndex("ID"); @@ -90,6 +90,8 @@ namespace ln.provider Application.HttpServer.DefaultApplication = Application.WWWApplication; Application.RPCContainer.Add("core",new RPC(Application)); + Ready(); + while (!StopRequested) { lock (this) diff --git a/ln.provider.csproj b/ln.provider.csproj index 1f9865d..041962e 100644 --- a/ln.provider.csproj +++ b/ln.provider.csproj @@ -35,6 +35,7 @@ + @@ -78,6 +79,11 @@ PreserveNewest + + + + + \ No newline at end of file diff --git a/www/index.html b/www/index.html index 9de08a0..22ea9ee 100644 --- a/www/index.html +++ b/www/index.html @@ -2,38 +2,68 @@ - ln.provider - - - + ln.provider Web Interface + + + + + + + + + + + + + + - +
+ -
- -

{{ serverString }}

+ + +
+ +
+
diff --git a/www/ln.application.js b/www/ln.application.js deleted file mode 100644 index 87e725f..0000000 --- a/www/ln.application.js +++ /dev/null @@ -1,336 +0,0 @@ -var LN = (function(){ - var appInterface; - - var defaultOptions = { - url: null, - - }; - - class LNInterface { - constructor(opt){ - var self = this; - - this.options = {} - Object.assign(this.options,opt); - - if (this.options.url == null) - this.options.url = this.constructURL(); - - - this.rpcCallbacks = []; - this.rpcNextID = 1; - - this.websocket = new WebSocket(this.options.url); - this.websocket.onerror = function(e){ - alert("WebSocket caught error: " + e.date); - } - this.websocket.onmessage = function(e){ - var j = JSON.parse(e.data); - if (j.state){ - updateState(j.state); - } else if (j.id) - { - for (var n=0;n 1000000000) - return ((value / 1000000000) | 0) + "G"; - if (value > 1000000) - return ((value / 1000000) | 0) + "M"; - if (value > 1000) - return ((value / 1000) | 0) + "k"; - return value; -} - - -*/ - diff --git a/www/ln.provider.components.js b/www/ln.provider.components.js new file mode 100644 index 0000000..e2bca73 --- /dev/null +++ b/www/ln.provider.components.js @@ -0,0 +1,25 @@ +Vue.component('toggle-pane',{ + props: { + visible: { + type: Boolean, + default: false + }, + label: String, + class: String, + }, + data: function(){ + return { + }; + }, + template: ` +
+ +
+
+
+ `, +}); diff --git a/www/ln.provider.js b/www/ln.provider.js new file mode 100644 index 0000000..29e3382 --- /dev/null +++ b/www/ln.provider.js @@ -0,0 +1,92 @@ +var LNProvider = (function(){ + + class LNProvider + { + constructor(options){ + let self = this; + + this.serverString = "N/A"; + this.serverTime = "N/A"; + + this.lagDetector = null; + + this.IPAllocations = []; + + LN().option("wsError",(e)=>self.wsError(e)); + LN().option("wsClose",(e)=>self.wsClose(e)); + LN().option("wsUpdate",(e)=>self.wsUpdate(e)); + + LN().connect(); + } + + initialize(){ + return Promise.all(LNProvider.initializers); + } + + loadIPAllocations(){ + let self = this; + console.log("loadIPAllocations()"); + LN().rpc("ippool","GetAllocations",[],function(r,e){ + self.IPAllocations = r; + console.log("IPA: " + JSON.stringify(r)); + }); + } + + allocate(type,cidr,maskWidth,zone,usage,splitZone){ + let self = this; + + if (type == 0) + { + + } else if (type == 1) + { + LN().rpc("ippool","AllocateCIDR",[zone,cidr,usage,splitZone],function(r,e){ + if (e){ + alert("Error: \n" + JSON.stringify(e)); + } else { + self.IPAllocations.push(r); + } + }); + } + + } + + wsUpdate(state) + { + try + { + if (this.lagDetector) + clearTimeout(this.lagDetector); + + this.serverTime = moment(state.currentTime).format(); + + this.lagDetector = setTimeout(function(){ + this.serverTime = "Server lag detected"; + }, 2000); + + } catch (e) + { + console.log(e); + } + } + wsError(e){ + this.serverTime = "WebSocket: Error: " + JSON.stringify(e); + } + wsClose(e){ + this.serverTime = "WebSocket: Connection lost"; + setTimeout(function(){ + LN().connect(); + }, 2500 ); + } + } + + LNProvider.routes = []; + LNProvider.initializers = []; + + + return LNProvider; +})(); + + + + diff --git a/www/ln.provider.pool.html b/www/ln.provider.pool.html new file mode 100644 index 0000000..f8bf8fd --- /dev/null +++ b/www/ln.provider.pool.html @@ -0,0 +1,74 @@ +
+

IP Pools

+ +
+ + +
+ +
+
+
+
+ Maskenlänge: + +
+
+ Netzbreite: + +
+
+
+
+ Zielnetz: + +
+
+ + +
+
+
+
+ Verwendung: + +
+
+
+
+ + + + + + + + + + + + + + + + + + + +
AktionenCIDRNamePoolVerwendungsnachweis
+ + {{ ipa.CIDR }}{{ ipa.Name }}{{ ipa.Pool }}{{ ipa.AllocatedTo }}
+ +
\ No newline at end of file diff --git a/www/ln.provider.pool.js b/www/ln.provider.pool.js new file mode 100644 index 0000000..b077514 --- /dev/null +++ b/www/ln.provider.pool.js @@ -0,0 +1,45 @@ +LNProvider.initializers.push( + new Promise((resolve,reject)=>{ + LN() + .load("/ln.provider.pool.html") + .then((template)=>{ + LNProvider.routes.push( + { + path: "/ippool", + label: "IP Pool", + component: { + props: { + LNP: Object, + }, + template: template, + data: function(){ + return { + allocationWidth: 64, + targetCIDR: "", + independentAllocation: false, + allocationType: 0, + allocationUsage: "", + }; + }, + computed: { + IPAllocations: ()=>LNP.IPAllocations, + subnetWidth: { + get: function(){ + return 128 - this.allocationWidth; + }, + set: function(v){ + this.allocationWidth = 128 - v; + }, + }, + }, + beforeRouteEnter: function(to,from,next){ + LNP.loadIPAllocations(); + next(); + }, + }, + } + ); + resolve(); + }); + }) +); diff --git a/www/page.layout.css b/www/page.layout.css new file mode 100644 index 0000000..396f5b8 --- /dev/null +++ b/www/page.layout.css @@ -0,0 +1,145 @@ + +html { + padding: 0px; + margin: 0px; +} +body { + padding: 0px; + margin: 0px; + + font-family: Arial, Helvetica, sans-serif; + font-size: 12px; + + display: block; + position: absolute; + top: 0px; + bottom: 0px; + left: 0px; + right: 0px; +} + +div { + margin: 0px; + padding: 0px; + flex-grow: 0; +} + +div#body { + display: flex; + height: 100%; + width: 100%; + + flex-direction: column; +} + +#header { + top: 0px; + left: 0px; + right: 0px; + + border-bottom: 1px solid black; + background-color: #bad6ff; + padding: 4px; + +} + +#header > div { + margin: 4px; +} + +#navbar { + padding: 8px; + + border-bottom: 1px solid black; + + font-size: 16px; + color: #808080; +} + +#navbar > a { + display: inline-block; + margin-left: 16px; + margin-right: 16px; + +} +#navbar > a.router-link-active { + color: black; +} + +#page { + padding: 16px; + + flex-grow: 1; + + overflow-y: scroll; +} +#page::after { + content: ''; + display: block; + height: 16px; + width: 100%; +} + +#footer { + height: 20px; + padding: 6px; + padding-left: 24px; + background-color: #bad6ff; + border-top: 1px solid black; + + flex-grow: 0; + flex-shrink: 0; +} + +.hidden { + visibility: hidden; +} + +.p { + margin-bottom: 12px; +} + +.DISABLED { + color: #D0D0D0; +} + +fieldset { + display: inline-flex; + flex-direction: column; + + border: none; +} +fieldset > div { + display: flex; + flex-direction: row; + + width: 100%; + margin: 4px; + + vertical-align: top; +} + +fieldset > div > * { + flex-grow: 0; +} +fieldset > div > *:first-child { + flex-grow: 1; +} + + +.toggle-pane { + margin-bottom: 4px; +} +.toggle-pane > div { + display: block; + margin-top: 4px; +} + +input { + height: 16px; + + border: 1px solid #bad6ff; + border-radius: 3px; + + padding: 4px; +} diff --git a/www/style.css b/www/style.css index 24e4247..1ab31cd 100644 --- a/www/style.css +++ b/www/style.css @@ -25,3 +25,32 @@ div.app { +.logo { + display: inline-block; + border: 1px solid black; + font-size: 18px; + font-weight: 800; + + flex-grow: 0; + flex-shrink: 0; +} +.logo > div { + display: inline-block; + padding: 4px; +} + +.flex { + display: flex; +} + +.flex.row { + flex-direction: row; +} +.flex.column { + flex-direction: column; +} + +.flex > * { + margin: 4px; +} + diff --git a/www/tables.layout.css b/www/tables.layout.css new file mode 100644 index 0000000..526b5e4 --- /dev/null +++ b/www/tables.layout.css @@ -0,0 +1,50 @@ + +table { + border-collapse: collapse; + width: 100%; +} + +table tr { + +} + +table td { + padding: 4px; + border-bottom: 1px solid #D0D0D0; + + vertical-align: top; +} + +table > thead { + font-style: italic; + background-color: #bad6ff; + transition: background-color 1000ms; +} + +table > thead tr { + +} + +table > thead td { +} + +table > tbody tr { + +} + +table > tbody td { + +} + +table > tfoot tr { + +} + +table > tfoot td { + +} + +table > tbody > tr:hover > td { + border-top: 2px solid black; + border-bottom: 2px solid black; +}