first commit
This commit is contained in:
commit
953e266b22
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
.vs
|
||||
CiscoNetboxSync/bin
|
||||
CiscoNetboxSync/obj
|
||||
settings.conf
|
||||
CiscoNetboxSync/Properties
|
||||
31
CiscoNetboxSync.sln
Normal file
31
CiscoNetboxSync.sln
Normal file
@ -0,0 +1,31 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.31112.23
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CiscoNetboxSync", "CiscoNetboxSync\CiscoNetboxSync.csproj", "{35CA8211-46EF-4FEB-9515-335D1D08E704}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "litjson", "..\Librarys\litjson\litjson\litjson.csproj", "{5B043023-3191-4EA9-949F-09C6DBC1F4DA}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{35CA8211-46EF-4FEB-9515-335D1D08E704}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{35CA8211-46EF-4FEB-9515-335D1D08E704}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{35CA8211-46EF-4FEB-9515-335D1D08E704}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{35CA8211-46EF-4FEB-9515-335D1D08E704}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{5B043023-3191-4EA9-949F-09C6DBC1F4DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5B043023-3191-4EA9-949F-09C6DBC1F4DA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5B043023-3191-4EA9-949F-09C6DBC1F4DA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5B043023-3191-4EA9-949F-09C6DBC1F4DA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {90875829-0BA4-4181-93D4-CC5594DACF2B}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
254
CiscoNetboxSync/Cisco/ConfigParser.cs
Normal file
254
CiscoNetboxSync/Cisco/ConfigParser.cs
Normal file
@ -0,0 +1,254 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
using BlubbFish.CiscoNetboxSync.Model;
|
||||
|
||||
namespace BlubbFish.CiscoNetboxSync.Cisco {
|
||||
class ConfigParser {
|
||||
public Dictionary<String, Device> Devices {
|
||||
get;
|
||||
} = new Dictionary<String, Device>();
|
||||
|
||||
enum BlockType {
|
||||
None,
|
||||
Fex,
|
||||
Vlan,
|
||||
InterfaceVlan,
|
||||
InterfacePortChannel,
|
||||
InterfaceEthernet
|
||||
}
|
||||
|
||||
private readonly Dictionary<Int32, Vlan> vlans = new Dictionary<Int32, Vlan>();
|
||||
private readonly Dictionary<String, Interface> interfaces = new Dictionary<String, Interface>();
|
||||
|
||||
private readonly Boolean hasFex = false;
|
||||
private readonly List<Int32> fex_devices = new List<Int32>();
|
||||
private readonly String hostname = "";
|
||||
private readonly Boolean defaultShutdown = false;
|
||||
|
||||
public ConfigParser(String[] configfile) {
|
||||
List<String> block = new List<String>();
|
||||
BlockType blockType = BlockType.None;
|
||||
|
||||
foreach(String line in configfile) {
|
||||
if(block.Count > 0 && line.StartsWith(" ")) {
|
||||
block.Add(line);
|
||||
} else if(!line.StartsWith(" ")) {
|
||||
if(blockType == BlockType.Fex) {
|
||||
if(Int32.TryParse(this.ParseFexBlockSettings(block.ToArray()), out Int32 fexid)) {
|
||||
this.fex_devices.Add(fexid);
|
||||
}
|
||||
} else if(blockType == BlockType.Vlan) {
|
||||
Vlan vlan = this.ParseVlanBlock(block.ToArray());
|
||||
this.vlans.Add(vlan.Id, vlan);
|
||||
} else if(blockType == BlockType.InterfaceVlan) {
|
||||
Interface interf = this.ParseInterfaceVlanBlock(block.ToArray());
|
||||
this.interfaces.Add(interf.Name, interf);
|
||||
} else if(blockType == BlockType.InterfacePortChannel) {
|
||||
Interface interf = this.ParseInterfacePortchannelBlock(block.ToArray());
|
||||
this.interfaces.Add(interf.Name, interf);
|
||||
} else if(blockType == BlockType.InterfaceEthernet) {
|
||||
Tuple<Int32, Interface> interf = this.ParseInterfaceEthernetBlock(block.ToArray());
|
||||
if(!this.Devices.ContainsKey(interf.Item1.ToString())) {
|
||||
Boolean isFex = this.hasFex && this.fex_devices.Contains(interf.Item1);
|
||||
String host = this.hostname + (isFex ? "-FEX-" + interf.Item1 : (this.Devices.Count > 0 ? "-" + interf.Item1:""));
|
||||
this.Devices.Add(interf.Item1.ToString(), new Device(this.interfaces, host, isFex));
|
||||
}
|
||||
this.Devices[interf.Item1.ToString()].Interfaces.Add(interf.Item2.Name, interf.Item2);
|
||||
}
|
||||
blockType = BlockType.None;
|
||||
block.Clear();
|
||||
}
|
||||
|
||||
if(line.StartsWith("hostname")) {
|
||||
this.hostname = Regex.Match(line, "hostname (.*)$").Groups[1].Value;
|
||||
} else if(line.StartsWith("feature-set fex")) {
|
||||
this.hasFex = true;
|
||||
} else if(line.StartsWith("system default switchport shutdown")) {
|
||||
this.defaultShutdown = true;
|
||||
}
|
||||
|
||||
if(Regex.Match(line, "^fex [0-9]+$").Success) {
|
||||
block.Add(line);
|
||||
blockType = BlockType.Fex;
|
||||
} else if(Regex.Match(line, "^vlan [0-9]+$").Success) {
|
||||
block.Add(line);
|
||||
blockType = BlockType.Vlan;
|
||||
} else if(Regex.Match(line, "^interface Vlan[0-9]+$", RegexOptions.IgnoreCase).Success) {
|
||||
block.Add(line);
|
||||
blockType = BlockType.InterfaceVlan;
|
||||
} else if(Regex.Match(line, "^interface port-channel[0-9]+$", RegexOptions.IgnoreCase).Success) {
|
||||
block.Add(line);
|
||||
blockType = BlockType.InterfacePortChannel;
|
||||
} else if(Regex.Match(line, "^interface Ethernet[0-9]+/[0-9/]+$", RegexOptions.IgnoreCase).Success) {
|
||||
block.Add(line);
|
||||
blockType = BlockType.InterfaceEthernet;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Tuple<Int32, Interface> ParseInterfaceEthernetBlock(String[] vs) {
|
||||
Int32 DeviceId = 0;
|
||||
String Name = "";
|
||||
String Description = null;
|
||||
Boolean Shutdown = this.defaultShutdown;
|
||||
Tuple<IPAddress, Int32> Ip = null;
|
||||
Interface.PType porttype = Interface.PType.None;
|
||||
Vlan untagged = null;
|
||||
List<Vlan> tagged = new List<Vlan>();
|
||||
Int32 LagID = 0;
|
||||
foreach(String item in vs) {
|
||||
this.ParseInterfaceDeviceIdBlockSettings(item, ref DeviceId);
|
||||
this.ParseInterfaceNameBlockSettings(item, ref Name);
|
||||
this.ParseDescriptionsSettins(item, ref Description);
|
||||
this.ParseShutdownSettings(item, ref Shutdown);
|
||||
this.ParseIpSettings(item, ref Ip);
|
||||
this.ParseSwitchportModeSettings(item, ref porttype);
|
||||
this.ParseUntaggedVlanSettings(item, ref untagged);
|
||||
this.ParseTaggedVlanSettings(item, ref tagged);
|
||||
this.ParseChannelGroupSettings(item, ref LagID);
|
||||
}
|
||||
return new Tuple<Int32, Interface>(DeviceId, new Interface(Name, Description, Shutdown, Ip, untagged, tagged, porttype, LagID, Interface.IType.Port));
|
||||
}
|
||||
private Interface ParseInterfacePortchannelBlock(String[] vs) {
|
||||
Int32 LagID = 0;
|
||||
String Name = "";
|
||||
String Description = null;
|
||||
Boolean Shutdown = this.defaultShutdown;
|
||||
Tuple<IPAddress, Int32> Ip = null;
|
||||
Interface.PType porttype = Interface.PType.None;
|
||||
Vlan untagged = null;
|
||||
List<Vlan> tagged = new List<Vlan>();
|
||||
foreach(String item in vs) {
|
||||
this.ParseInterfaceIdBlockSettings(item, ref LagID);
|
||||
this.ParseInterfaceNameBlockSettings(item, ref Name);
|
||||
this.ParseDescriptionsSettins(item, ref Description);
|
||||
this.ParseShutdownSettings(item, ref Shutdown);
|
||||
this.ParseIpSettings(item, ref Ip);
|
||||
this.ParseSwitchportModeSettings(item, ref porttype);
|
||||
this.ParseUntaggedVlanSettings(item, ref untagged);
|
||||
this.ParseTaggedVlanSettings(item, ref tagged);
|
||||
}
|
||||
return new Interface(Name, Description, Shutdown, Ip, untagged, tagged, porttype, LagID, Interface.IType.Lag);
|
||||
}
|
||||
private Interface ParseInterfaceVlanBlock(String[] vs) {
|
||||
Int32 VlanId = 0;
|
||||
String Name = "";
|
||||
String Description = null;
|
||||
Boolean Shutdown = this.defaultShutdown;
|
||||
Tuple<IPAddress, Int32> Ip = null;
|
||||
|
||||
foreach(String item in vs) {
|
||||
this.ParseInterfaceIdBlockSettings(item, ref VlanId);
|
||||
this.ParseInterfaceNameBlockSettings(item, ref Name);
|
||||
this.ParseDescriptionsSettins(item, ref Description);
|
||||
this.ParseShutdownSettings(item, ref Shutdown);
|
||||
this.ParseIpSettings(item, ref Ip);
|
||||
}
|
||||
return new Interface(Name, Description, Shutdown, Ip, this.CheckAndGetVlan(VlanId));
|
||||
}
|
||||
private Vlan ParseVlanBlock(String[] vs) {
|
||||
Int32 VlanId = 0;
|
||||
String Name = "";
|
||||
foreach(String item in vs) {
|
||||
this.ParseInterfaceIdBlockSettings(item, ref VlanId);
|
||||
this.ParseDescriptionsSettins(item, ref Name);
|
||||
}
|
||||
return new Vlan(VlanId, Name);
|
||||
}
|
||||
private String ParseFexBlockSettings(String[] vs) => Regex.Match(vs[0], "^fex ([0-9]+)$").Groups[1].Value;
|
||||
|
||||
private List<Int32> ParseVlanBlocks(String value) {
|
||||
List<Int32> ret = new List<Int32>();
|
||||
String[] tags = value.Split(',');
|
||||
foreach(String item in tags) {
|
||||
if(item.Contains("-")) {
|
||||
Match regblock = Regex.Match(item, "^([0-9]+)-([0-9]+)");
|
||||
if(regblock.Success && Int32.TryParse(regblock.Groups[1].Value, out Int32 vlanidstart) && Int32.TryParse(regblock.Groups[2].Value, out Int32 vlanidstop)) {
|
||||
ret.AddRange(Enumerable.Range(vlanidstart, vlanidstop - vlanidstart + 1).ToList());
|
||||
}
|
||||
} else {
|
||||
if(Int32.TryParse(item, out Int32 vlanid)) {
|
||||
ret.Add(vlanid);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
public Vlan CheckAndGetVlan(Int32 vlanid) {
|
||||
if(!this.vlans.ContainsKey(vlanid)) {
|
||||
this.vlans.Add(vlanid, new Vlan(vlanid, "NOT KNOWN"));
|
||||
}
|
||||
return this.vlans[vlanid];
|
||||
}
|
||||
|
||||
public void ParseInterfaceIdBlockSettings(String item, ref Int32 VlanId) {
|
||||
Match regvlanid = Regex.Match(item, "^(vlan |interface Vlan|interface port-channel)([0-9]+)$", RegexOptions.IgnoreCase);
|
||||
if(regvlanid.Success && Int32.TryParse(regvlanid.Groups[2].Value, out Int32 vlanid)) {
|
||||
VlanId = vlanid;
|
||||
}
|
||||
}
|
||||
public void ParseInterfaceNameBlockSettings(String item, ref String Name) {
|
||||
Match regname = Regex.Match(item, "^interface (Vlan[0-9]+|port-channel[0-9]+|Ethernet[0-9/]+)$", RegexOptions.IgnoreCase);
|
||||
if(regname.Success) {
|
||||
Name = regname.Groups[1].Value;
|
||||
}
|
||||
}
|
||||
public void ParseInterfaceDeviceIdBlockSettings(String item, ref Int32 DeviceId) {
|
||||
Match regdeviceid = Regex.Match(item, "^interface Ethernet([0-9]+)/[0-9/]+$", RegexOptions.IgnoreCase);
|
||||
if(regdeviceid.Success && Int32.TryParse(regdeviceid.Groups[1].Value, out Int32 deviceid)) {
|
||||
DeviceId = deviceid;
|
||||
}
|
||||
}
|
||||
public void ParseDescriptionsSettins(String item, ref String Description) {
|
||||
Match regdescription = Regex.Match(item, @"^\s+(name|description) (.*)$");
|
||||
if(regdescription.Success) {
|
||||
Description = regdescription.Groups[2].Value;
|
||||
}
|
||||
}
|
||||
public void ParseShutdownSettings(String item, ref Boolean Shutdown) {
|
||||
if(Regex.Match(item, @"^\s+no shutdown$").Success) {
|
||||
Shutdown = false;
|
||||
}
|
||||
if(Regex.Match(item, @"^\s+shutdown$").Success) {
|
||||
Shutdown = true;
|
||||
}
|
||||
}
|
||||
public void ParseIpSettings(String item, ref Tuple<IPAddress, Int32> Ip) {
|
||||
Match regip = Regex.Match(item, @"^\s+ip address ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})/([0-9]{1,2})$");
|
||||
if(regip.Success && IPAddress.TryParse(regip.Groups[1].Value, out IPAddress ip) && Int32.TryParse(regip.Groups[2].Value, out Int32 mask)) {
|
||||
Ip = new Tuple<IPAddress, Int32>(ip, mask);
|
||||
}
|
||||
}
|
||||
public void ParseSwitchportModeSettings(String item, ref Interface.PType porttype) {
|
||||
Match regporttype = Regex.Match(item, @"^\s+switchport (mode trunk|access vlan)$");
|
||||
if(regporttype.Success) {
|
||||
porttype = regporttype.Groups[1].Value == "mode trunk" ? Interface.PType.Trunk : Interface.PType.Access;
|
||||
}
|
||||
}
|
||||
public void ParseUntaggedVlanSettings(String item, ref Vlan Untagged) {
|
||||
Match reguntagged = Regex.Match(item, @"^\s+switchport (access vlan|trunk native vlan) ([0-9]+)$");
|
||||
if(reguntagged.Success && Int32.TryParse(reguntagged.Groups[2].Value, out Int32 untaggedid)) {
|
||||
Untagged = this.CheckAndGetVlan(untaggedid);
|
||||
}
|
||||
}
|
||||
public void ParseTaggedVlanSettings(String item, ref List<Vlan> Tagged) {
|
||||
Match regtagged = Regex.Match(item, @"^\s+switchport trunk allowed vlan( add|) ([0-9,-]+)$");
|
||||
if(regtagged.Success) {
|
||||
foreach(Int32 taggedid in this.ParseVlanBlocks(regtagged.Groups[2].Value)) {
|
||||
Tagged.Add(this.CheckAndGetVlan(taggedid));
|
||||
}
|
||||
}
|
||||
}
|
||||
public void ParseChannelGroupSettings(String item, ref Int32 ChannelGroup) {
|
||||
Match regchannelgroup = Regex.Match(item, @"^\s+channel-group ([0-9]+) mode active$", RegexOptions.IgnoreCase);
|
||||
if(regchannelgroup.Success && Int32.TryParse(regchannelgroup.Groups[1].Value, out Int32 channelgroupid)) {
|
||||
ChannelGroup = channelgroupid;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
14
CiscoNetboxSync/CiscoNetboxSync.csproj
Normal file
14
CiscoNetboxSync/CiscoNetboxSync.csproj
Normal file
@ -0,0 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<AssemblyName>CiscoNetboxSync</AssemblyName>
|
||||
<RootNamespace>BlubbFish.CiscoNetboxSync</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Librarys\litjson\litjson\litjson.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
34
CiscoNetboxSync/Model/Device.cs
Normal file
34
CiscoNetboxSync/Model/Device.cs
Normal file
@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BlubbFish.CiscoNetboxSync.Model {
|
||||
public class Device {
|
||||
public String Hostname {
|
||||
get;
|
||||
}
|
||||
|
||||
public Boolean IsFex {
|
||||
get;
|
||||
}
|
||||
|
||||
public Dictionary<String, Interface> Interfaces {
|
||||
get;
|
||||
}
|
||||
|
||||
public Device(Dictionary<String, Interface> interfaces, String host, Boolean isFex) {
|
||||
this.Interfaces = new Dictionary<String, Interface>(interfaces);
|
||||
this.Hostname = host;
|
||||
this.IsFex = isFex;
|
||||
}
|
||||
|
||||
public static Boolean Compare(Device config, Device netbox) =>
|
||||
CompareDeviceObject(config, netbox) &&
|
||||
CompareHostname(config, netbox) &&
|
||||
CompareFex(config, netbox) &&
|
||||
Interface.Compare(config.Interfaces, netbox.Interfaces);
|
||||
|
||||
public static Boolean CompareDeviceObject(Device config, Device netbox) => config != null && netbox != null;
|
||||
public static Boolean CompareHostname(Device config, Device netbox) => config.Hostname.ToLower() == netbox.Hostname.ToLower();
|
||||
public static Boolean CompareFex(Device config, Device netbox) => config.IsFex == netbox.IsFex;
|
||||
}
|
||||
}
|
||||
38
CiscoNetboxSync/Model/DiffDevice.cs
Normal file
38
CiscoNetboxSync/Model/DiffDevice.cs
Normal file
@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BlubbFish.CiscoNetboxSync.Model {
|
||||
public class DiffDevice {
|
||||
public DiffDevice(AddedAttributes add) => this.Add = add;
|
||||
|
||||
public AddedAttributes Add {
|
||||
get;
|
||||
}
|
||||
|
||||
public class AddedAttributes {
|
||||
public AddedAttributes(String hostname, Boolean isFex, Dictionary<String, Interface> interfaces) {
|
||||
this.Hostname = hostname;
|
||||
this.IsFex = isFex;
|
||||
this.Interfaces = interfaces;
|
||||
}
|
||||
|
||||
public String Hostname {
|
||||
get;
|
||||
}
|
||||
public Boolean IsFex {
|
||||
get;
|
||||
}
|
||||
public Dictionary<String, Interface> Interfaces {
|
||||
get;
|
||||
}
|
||||
}
|
||||
|
||||
public static DiffDevice GetDiff(Device config, Device netbox) {
|
||||
if(Device.CompareDeviceObject(config, netbox)) {
|
||||
return new DiffDevice(new AddedAttributes(netbox.Hostname, netbox.IsFex, netbox.Interfaces));
|
||||
}
|
||||
if()
|
||||
}
|
||||
}
|
||||
}
|
||||
96
CiscoNetboxSync/Model/Interface.cs
Normal file
96
CiscoNetboxSync/Model/Interface.cs
Normal file
@ -0,0 +1,96 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
|
||||
namespace BlubbFish.CiscoNetboxSync.Model {
|
||||
public class Interface {
|
||||
public String Name {
|
||||
get;
|
||||
}
|
||||
|
||||
public String Description {
|
||||
get;
|
||||
}
|
||||
|
||||
public Boolean Shutdown {
|
||||
get;
|
||||
}
|
||||
|
||||
public String Ip {
|
||||
get;
|
||||
}
|
||||
|
||||
public PType Portmode {
|
||||
get;
|
||||
}
|
||||
|
||||
public Vlan Untagged {
|
||||
get;
|
||||
}
|
||||
|
||||
public List<Vlan> Tagged {
|
||||
get;
|
||||
}
|
||||
|
||||
public IType InterfaceType {
|
||||
get;
|
||||
}
|
||||
|
||||
public Int32 LagId {
|
||||
get;
|
||||
}
|
||||
|
||||
public enum PType {
|
||||
None,
|
||||
Access,
|
||||
Trunk
|
||||
}
|
||||
|
||||
public enum IType {
|
||||
Vlan,
|
||||
Lag,
|
||||
Port
|
||||
}
|
||||
|
||||
public Interface(String name, String description, Boolean shutdown, Tuple<IPAddress, Int32> ip, Vlan untagged, List<Vlan> tagged = null, PType porttype = PType.Access, Int32 lagID = 0, IType interfaceType = IType.Vlan) {
|
||||
this.LagId = lagID;
|
||||
this.Name = name;
|
||||
this.Description = description;
|
||||
this.Shutdown = shutdown;
|
||||
this.Ip = ip != null ? ip.Item1.ToString() + "/" + ip.Item2.ToString() : "";
|
||||
this.Untagged = untagged;
|
||||
this.Portmode = porttype;
|
||||
this.Tagged = tagged;
|
||||
this.InterfaceType = interfaceType;
|
||||
}
|
||||
|
||||
public static Boolean Compare(Dictionary<String, Interface> config, Dictionary<String, Interface> netbox) {
|
||||
if(config.Count != netbox.Count) {
|
||||
return false;
|
||||
}
|
||||
foreach(KeyValuePair<String, Interface> item in config) {
|
||||
if(!netbox.ContainsKey(item.Key)) {
|
||||
return false;
|
||||
}
|
||||
if(!Compare(item.Value, netbox[item.Key])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static Boolean Compare(Interface config, Interface netbox) =>
|
||||
config.Name.ToLower() == netbox.Name.ToLower() &&
|
||||
config.Description.ToLower() == netbox.Description.ToLower() &&
|
||||
config.Ip == netbox.Ip &&
|
||||
config.Portmode == netbox.Portmode &&
|
||||
Vlan.Compare(config.Untagged, netbox.Untagged) &&
|
||||
Vlan.Compare(config.Tagged, netbox.Tagged) &&
|
||||
config.InterfaceType == netbox.InterfaceType &&
|
||||
config.LagId == netbox.LagId;
|
||||
|
||||
public override String ToString() => this.Name + " " + (this.Description != null ? "(" + this.Description + ") " : "") + " " +
|
||||
this.Portmode + " " + (this.Untagged != null ? "A: " + this.Untagged + " " : "") + (this.Tagged.Count > 0 ? "T: " + String.Join(",", this.Tagged) : "");
|
||||
}
|
||||
}
|
||||
61
CiscoNetboxSync/Model/Vlan.cs
Normal file
61
CiscoNetboxSync/Model/Vlan.cs
Normal file
@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BlubbFish.CiscoNetboxSync.Model {
|
||||
public class Vlan {
|
||||
public Int32 Id {
|
||||
get;
|
||||
}
|
||||
|
||||
public String Name {
|
||||
get;
|
||||
}
|
||||
|
||||
public Vlan(Int32 id, String name) {
|
||||
this.Id = id;
|
||||
this.Name = name;
|
||||
}
|
||||
|
||||
public static Boolean Compare(List<Vlan> configs, List<Vlan> netboxs) {
|
||||
if(configs.Count != netboxs.Count) {
|
||||
return false;
|
||||
}
|
||||
foreach(Vlan config in configs) {
|
||||
Boolean eq = false;
|
||||
foreach(Vlan netbox in netboxs) {
|
||||
if(Compare(config, netbox)) {
|
||||
eq = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!eq) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static Boolean Compare(Vlan config, Vlan netbox) => config.Id == netbox.Id;
|
||||
|
||||
public static List<Vlan> GetDeleteNetbox(List<Vlan> configs, List<Vlan> netboxs) {
|
||||
List<Vlan> ret = new List<Vlan>();
|
||||
foreach(Vlan netbox in netboxs) {
|
||||
Boolean todel = true;
|
||||
foreach(Vlan config in configs) {
|
||||
if(Compare(config, netbox)) {
|
||||
todel = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(todel) {
|
||||
ret.Add(netbox);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static List<Vlan> GetAddNetbox(List<Vlan> configs, List<Vlan> netboxs) => GetDeleteNetbox(netboxs, configs);
|
||||
|
||||
public override String ToString() => this.Id + " = " + this.Name;
|
||||
}
|
||||
}
|
||||
148
CiscoNetboxSync/Netbox/NetboxParser.cs
Normal file
148
CiscoNetboxSync/Netbox/NetboxParser.cs
Normal file
@ -0,0 +1,148 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
using BlubbFish.CiscoNetboxSync.lib;
|
||||
using BlubbFish.CiscoNetboxSync.Model;
|
||||
|
||||
using LitJson;
|
||||
|
||||
namespace BlubbFish.CiscoNetboxSync.Netbox {
|
||||
class NetboxParser {
|
||||
private readonly HttpEndpoint http;
|
||||
|
||||
public Device NetboxDevice {
|
||||
get;
|
||||
}
|
||||
|
||||
public NetboxParser(String hostname, String host, String authkey) {
|
||||
this.http = new HttpEndpoint(host, ("Token", authkey));
|
||||
|
||||
(Int32 deviceId, String deviceHostname, Boolean deviceFex) = this.NetboxSearchDevice(hostname);
|
||||
if(deviceId == 0) {
|
||||
Console.WriteLine("Konnte " + hostname + " nicht finden in Netbox!");
|
||||
return;
|
||||
}
|
||||
|
||||
Dictionary<Int32, Vlan> vlans = this.GetVlans();
|
||||
|
||||
Dictionary<Int32, Tuple<IPAddress, Int32>> ips = this.GetIps(deviceId);
|
||||
|
||||
Dictionary<String, Interface> interfaces = this.GetInterfaces(deviceId, vlans, ips);
|
||||
|
||||
this.NetboxDevice = new Device(interfaces, deviceHostname, deviceFex);
|
||||
}
|
||||
|
||||
private Dictionary<Int32, Tuple<IPAddress, Int32>> GetIps(Int32 deviceId) {
|
||||
Dictionary<Int32, Tuple<IPAddress, Int32>> ret = new Dictionary<Int32, Tuple<IPAddress, Int32>>();
|
||||
JsonData json = JsonMapper.ToObject(this.http.RequestString("/api/ipam/ip-addresses/?format=json&device_id="+ deviceId).Result);
|
||||
if(json.ContainsKey("results") && json["results"].IsArray && json["results"].Count > 0) {
|
||||
foreach(JsonData item in json["results"]) {
|
||||
if(item.ContainsKey("address") && item["address"].IsString
|
||||
&& item.ContainsKey("assigned_object") && item["assigned_object"].IsObject && item["assigned_object"].ContainsKey("id") && item["assigned_object"]["id"].IsInt
|
||||
&& !ret.ContainsKey((Int32)item["assigned_object"]["id"])) {
|
||||
Match regip = Regex.Match(item["address"].ToString(), @"^\s+ip address ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})/([0-9]{1,2})$");
|
||||
if(regip.Success && IPAddress.TryParse(regip.Groups[1].Value, out IPAddress ip) && Int32.TryParse(regip.Groups[2].Value, out Int32 mask)) {
|
||||
ret.Add((Int32)item["assigned_object"]["id"], new Tuple<IPAddress, Int32>(ip, mask));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private Dictionary<Int32, Vlan> GetVlans() {
|
||||
Dictionary<Int32, Vlan> ret = new Dictionary<Int32, Vlan>();
|
||||
JsonData json = JsonMapper.ToObject(this.http.RequestString("/api/ipam/vlans/?format=json").Result);
|
||||
if(json.ContainsKey("results") && json["results"].IsArray && json["results"].Count > 0) {
|
||||
foreach(JsonData item in json["results"]) {
|
||||
if(item.ContainsKey("id") && item["id"].IsInt
|
||||
&& item.ContainsKey("vid") && item["vid"].IsInt
|
||||
&& item.ContainsKey("name") && item["name"].IsString) {
|
||||
ret.Add((Int32)item["id"], new Vlan((Int32)item["vid"], item["name"].ToString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private Dictionary<String, Interface> GetInterfaces(Int32 deviceId, Dictionary<Int32, Vlan> vlans, Dictionary<Int32, Tuple<IPAddress, Int32>> ips) {
|
||||
Dictionary<String, Interface> ret = new Dictionary<String, Interface>();
|
||||
JsonData json = JsonMapper.ToObject(this.http.RequestString("/api/dcim/interfaces/?format=json&device_id=" + deviceId).Result);
|
||||
if(json.ContainsKey("results") && json["results"].IsArray && json["results"].Count > 0) {
|
||||
foreach(JsonData item in json["results"]) {
|
||||
if(item.ContainsKey("name") && item["name"].IsString
|
||||
&& item.ContainsKey("description") && item["description"].IsString
|
||||
&& item.ContainsKey("enabled") && item["enabled"].IsBoolean
|
||||
&& item.ContainsKey("id") && item["id"].IsInt
|
||||
&& item.ContainsKey("mode") && (item["mode"] == null || item["mode"].IsObject && item["mode"].ContainsKey("value") && item["mode"]["value"].IsString)
|
||||
&& item.ContainsKey("untagged_vlan") && (item["untagged_vlan"] == null || item["untagged_vlan"].IsObject && item["untagged_vlan"].ContainsKey("id") && item["untagged_vlan"]["id"].IsInt)
|
||||
&& item.ContainsKey("tagged_vlans") && item["tagged_vlans"].IsArray
|
||||
&& item.ContainsKey("type") && item["type"].IsObject && item["type"].ContainsKey("value") && item["type"]["value"].IsString
|
||||
&& item.ContainsKey("lag") && (item["lag"] == null || item["lag"].IsObject && item["lag"].ContainsKey("name") && item["lag"]["name"].IsString)) {
|
||||
Int32 interfaceId = (Int32)item["id"];
|
||||
String name = item["name"].ToString();
|
||||
String description = item["description"].ToString();
|
||||
Boolean shutdown = !(Boolean)item["enabled"];
|
||||
Tuple<IPAddress, Int32> ip = ips.ContainsKey(interfaceId) ? ips[interfaceId] : null;
|
||||
Interface.PType portmode = Interface.PType.None;
|
||||
if(item["mode"] != null && item["mode"]["value"].ToString().ToLower() == "tagged") {
|
||||
portmode = Interface.PType.Trunk;
|
||||
} else if(item["mode"] != null && item["mode"]["value"].ToString().ToLower() == "access") {
|
||||
portmode = Interface.PType.Access;
|
||||
}
|
||||
Vlan untagged = null;
|
||||
if(item["untagged_vlan"] != null) {
|
||||
untagged = vlans[(Int32)item["untagged_vlan"]["id"]];
|
||||
}
|
||||
List<Vlan> tagged = new List<Vlan>();
|
||||
foreach(JsonData jsonTagged in item["tagged_vlans"]) {
|
||||
if(jsonTagged.IsObject && jsonTagged.ContainsKey("id") && jsonTagged["id"].IsInt) {
|
||||
tagged.Add(vlans[(Int32)jsonTagged["id"]]);
|
||||
}
|
||||
}
|
||||
Int32 lagid = 0;
|
||||
if(item["lag"] != null) {
|
||||
Match r = Regex.Match(item["lag"]["name"].ToString(), "[0-9]+");
|
||||
if(r.Success) {
|
||||
lagid = Int32.Parse(r.Groups[0].Value);
|
||||
}
|
||||
}
|
||||
Interface.IType interfacetype = Interface.IType.Port;
|
||||
if(item["type"]["value"].ToString().ToLower() == "virtual") {
|
||||
interfacetype = Interface.IType.Vlan;
|
||||
} else if(item["type"]["value"].ToString().ToLower() == "lag") {
|
||||
interfacetype = Interface.IType.Lag;
|
||||
Match r = Regex.Match(item["name"].ToString(), "[0-9]+");
|
||||
if(r.Success) {
|
||||
lagid = Int32.Parse(r.Groups[0].Value);
|
||||
}
|
||||
}
|
||||
ret.Add(name, new Interface(name, description, shutdown, ip, untagged, tagged, portmode, lagid, interfacetype));
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private (Int32 deviceID, String hostname, Boolean isFex) NetboxSearchDevice(String hostname) {
|
||||
JsonData json = JsonMapper.ToObject(this.http.RequestString("/api/dcim/devices/?format=json&name="+hostname).Result);
|
||||
if(json.ContainsKey("results") && json["results"].IsArray && json["results"].Count == 1
|
||||
&& json["results"][0].ContainsKey("id") && json["results"][0]["id"].IsInt
|
||||
&& json["results"][0].ContainsKey("name") && json["results"][0]["name"].IsString
|
||||
&& json["results"][0].ContainsKey("tags") && json["results"][0]["tags"].IsArray
|
||||
&& (json["results"][0]["tags"].Count == 0 || json["results"][0]["tags"].Count > 0)) {
|
||||
Boolean isFex = false;
|
||||
foreach(JsonData item in json["results"][0]["tags"]) {
|
||||
if(item.ContainsKey("name") && item["name"].ToString().ToLower() == "fex") {
|
||||
isFex = true;
|
||||
}
|
||||
}
|
||||
return ((Int32)json["results"][0]["id"], json["results"][0]["name"].ToString(), isFex);
|
||||
}
|
||||
return (0, "", false);
|
||||
}
|
||||
}
|
||||
}
|
||||
36
CiscoNetboxSync/Program.cs
Normal file
36
CiscoNetboxSync/Program.cs
Normal file
@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
using BlubbFish.CiscoNetboxSync.Cisco;
|
||||
using BlubbFish.CiscoNetboxSync.Model;
|
||||
using BlubbFish.CiscoNetboxSync.Netbox;
|
||||
|
||||
using LitJson;
|
||||
|
||||
namespace BlubbFish.CiscoNetboxSync {
|
||||
class Program {
|
||||
static void Main(String[] args) {
|
||||
StreamReader file = new StreamReader(args[0]);
|
||||
List<String> buf = new List<String>();
|
||||
String fline = "";
|
||||
while(fline != null) {
|
||||
fline = file.ReadLine();
|
||||
if(fline != null && fline.Length > 0 && fline.Substring(0, 1) != "!") {
|
||||
buf.Add(fline);
|
||||
}
|
||||
}
|
||||
file.Close();
|
||||
Dictionary<String, Device> config = new ConfigParser(buf.ToArray()).Devices;
|
||||
String config_json = JsonMapper.ToJson(config);
|
||||
foreach(KeyValuePair<String, Device> item in config) {
|
||||
Device netbox = new NetboxParser(item.Value.Hostname, "url", "api-key").NetboxDevice;
|
||||
if(!Device.Compare(item.Value, netbox)) {
|
||||
DiffDevice deviceChanges = DiffDevice.GetDiff(item.Value, netbox);
|
||||
Console.WriteLine("nicht gleich");
|
||||
}
|
||||
}
|
||||
Console.WriteLine(config_json);
|
||||
}
|
||||
}
|
||||
}
|
||||
3
CiscoNetboxSync/config-example/settings.conf.example
Normal file
3
CiscoNetboxSync/config-example/settings.conf.example
Normal file
@ -0,0 +1,3 @@
|
||||
[netbox]
|
||||
url=<netbox-url>
|
||||
api_key=<netbox-api-key>
|
||||
62
CiscoNetboxSync/lib/HttpEndpoint.cs
Normal file
62
CiscoNetboxSync/lib/HttpEndpoint.cs
Normal file
@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BlubbFish.CiscoNetboxSync.lib {
|
||||
public class HttpEndpoint {
|
||||
private static readonly HttpClient client = new HttpClient();
|
||||
private readonly String server = "";
|
||||
|
||||
public HttpEndpoint(String server, String auth = null) {
|
||||
this.server = server;
|
||||
if(auth != null) {
|
||||
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(auth);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public HttpEndpoint(String server, (String scheme, String parameter) auth) {
|
||||
this.server = server;
|
||||
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(auth.scheme, auth.parameter);
|
||||
}
|
||||
|
||||
public async Task<String> RequestString(String address, String json = "", Boolean withoutput = true, RequestMethod method = RequestMethod.GET) {
|
||||
String ret = null;
|
||||
try {
|
||||
HttpResponseMessage response = null;
|
||||
if(method == RequestMethod.POST || method == RequestMethod.PUT) {
|
||||
HttpContent content = new StringContent(json);
|
||||
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
|
||||
//content.Headers.Add("Content-Type", "application/json");
|
||||
if(method == RequestMethod.POST) {
|
||||
response = await client.PostAsync(this.server + address, content);
|
||||
} else if(method == RequestMethod.PUT) {
|
||||
response = await client.PutAsync(this.server + address, content);
|
||||
}
|
||||
content.Dispose();
|
||||
} else if(method == RequestMethod.GET) {
|
||||
response = await client.GetAsync(this.server + address);
|
||||
}
|
||||
if(!response.IsSuccessStatusCode) {
|
||||
throw new Exception(response.StatusCode + ": " + response.ReasonPhrase);
|
||||
}
|
||||
if(withoutput && response != null) {
|
||||
ret = await response.Content.ReadAsStringAsync();
|
||||
}
|
||||
} catch(Exception e) {
|
||||
throw new WebException("Error while uploading to Scal. Resource: \"" + this.server + address + "\" Method: " + method + " Data: " + json + " Fehler: " + e.Message);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public enum RequestMethod {
|
||||
GET,
|
||||
POST,
|
||||
PUT
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user