Compare commits
10 commits
meshviewer
...
master
Author | SHA1 | Date | |
---|---|---|---|
95b3b20a43 | |||
07223d6cb6 | |||
8d4021f122 | |||
0632593675 | |||
67da82fb38 | |||
9c16434c0e | |||
1c8600435e | |||
cce68e8da6 | |||
1a01718538 | |||
666cd20c04 |
|
@ -1,7 +1,8 @@
|
||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
import argparse
|
import argparse
|
||||||
from parser.NodesParser import NodesParser
|
# from parser.NodesParser import NodesParser
|
||||||
from parser.GraphParser import GraphParser
|
# from parser.GraphParser import GraphParser
|
||||||
|
from parser.Hopglass import Hopglass
|
||||||
from cloud.Node import Node
|
from cloud.Node import Node
|
||||||
from cloud.Link import Link
|
from cloud.Link import Link
|
||||||
from cloud.GlobalGraph import GlobalGraph
|
from cloud.GlobalGraph import GlobalGraph
|
||||||
|
@ -13,8 +14,9 @@ from info.Info import Info
|
||||||
class NodeHierarchy(object):
|
class NodeHierarchy(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.__args__ = self.__parseArguments__()
|
self.__args__ = self.__parseArguments__()
|
||||||
self.__nodesJson__ = NodesParser(self.__args__.json_path)
|
self.__hopglass = Hopglass(self.__args__.raw_json)
|
||||||
self.__graphJson__ = GraphParser(self.__args__.json_path)
|
# self.__nodesJson__ = NodesParser(self.__args__.json_path)
|
||||||
|
# self.__graphJson__ = GraphParser(self.__args__.json_path)
|
||||||
self.__shapesJson__ = self.__parseShapes__()
|
self.__shapesJson__ = self.__parseShapes__()
|
||||||
self.nodes = self.__createNodeObjects__()
|
self.nodes = self.__createNodeObjects__()
|
||||||
self.links = self.__createLinkObjects__()
|
self.links = self.__createLinkObjects__()
|
||||||
|
@ -40,26 +42,29 @@ class NodeHierarchy(object):
|
||||||
|
|
||||||
def __createNodeObjects__(self):
|
def __createNodeObjects__(self):
|
||||||
nodes = {}
|
nodes = {}
|
||||||
for nodeID, nodeValue in self.__nodesJson__.nodes.items():
|
for nodeID, nodeValue in self.__hopglass.nodes.items():
|
||||||
print('Create Node object #',len(nodes), '\r',end = '')
|
if nodeValue['nodeinfo']['node_id']:
|
||||||
nodes[nodeID] = Node(nodeValue)
|
print('Create Node object #',len(nodes), '\r',end = '')
|
||||||
|
nodes[nodeID] = Node(self.__prepareNodeData__(nodeValue))
|
||||||
print('')
|
print('')
|
||||||
return nodes
|
return nodes
|
||||||
|
|
||||||
|
def __prepareNodeData__(self, nodeValue):
|
||||||
|
if self.__args__.site_to_target_prefix:
|
||||||
|
pref = self.__args__.site_to_target_prefix.split(',')
|
||||||
|
try:
|
||||||
|
nodeValue['nodeinfo']['system']['site_code'] = nodeValue['nodeinfo']['system']['site_code'].replace(pref[0],pref[1])
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return nodeValue
|
||||||
|
|
||||||
def __createLinkObjects__(self):
|
def __createLinkObjects__(self):
|
||||||
links = []
|
links = []
|
||||||
for link in self.__graphJson__.links:
|
for linkParID, linkPar in self.__hopglass.links.items():
|
||||||
try:
|
for linkID, link in linkPar.items():
|
||||||
srcNode = self.nodes[link['source']['node_id']]
|
print('Create Link object #',len(links), '\r',end = '')
|
||||||
except:
|
if linkParID[0] != 'null' and linkParID[1] != 'null':
|
||||||
srcNode = None
|
links.append(Link(link, (self.nodes[linkParID[0]], self.nodes[linkParID[1]])))
|
||||||
try:
|
|
||||||
dstNode = self.nodes[link['target']['node_id']]
|
|
||||||
except:
|
|
||||||
dstNode = None
|
|
||||||
|
|
||||||
print('Create Link object #',len(links), '\r',end = '')
|
|
||||||
links.append(Link(link, srcNode, dstNode))
|
|
||||||
print('')
|
print('')
|
||||||
return links
|
return links
|
||||||
|
|
||||||
|
@ -68,12 +73,13 @@ class NodeHierarchy(object):
|
||||||
|
|
||||||
def __parseArguments__(self):
|
def __parseArguments__(self):
|
||||||
parser = argparse.ArgumentParser(description='This Script generates a hierarchical nodes list for node migration using nginx geo feature.')
|
parser = argparse.ArgumentParser(description='This Script generates a hierarchical nodes list for node migration using nginx geo feature.')
|
||||||
parser.add_argument('-j', '--json-path', required=False, default='https://service.freifunk-muensterland.de/maps/data/', help='Path of nodes.json and graph.json (can be local folder or remote URL).')
|
parser.add_argument('-r', '--raw-json', required=False, default='https://karte.freifunk-muensterland.de/data/raw.json', help='Location of raw.json file (can be local folder or remote URL).')
|
||||||
parser.add_argument('-s', '--shapes-path', required=False, default='https://freifunk-muensterland.de/md-fw-dl/shapes/', help='Path of shapefiles (can be local folder or remote URL).')
|
parser.add_argument('-s', '--shapes-path', required=False, default='https://freifunk-muensterland.de/md-fw-dl/shapes/', help='Path of shapefiles (can be local folder or remote URL).')
|
||||||
parser.add_argument('-t', '--targets', nargs='+', required=True, help='List of targets which should be proceeded. Example: -t citya cityb ...')
|
parser.add_argument('-t', '--targets', nargs='+', required=True, help='List of targets which should be proceeded. Example: -t citya cityb ...')
|
||||||
|
parser.add_argument('-sttp', '--site-to-target-prefix', required=False, help='Used to match site and target also when prefixes are different. Example: -sttp "ffmsd,domaene"')
|
||||||
parser.add_argument('-o', '--out-file', default='./webserver-configuration', required=False, help='Filename where the generated Output should stored.')
|
parser.add_argument('-o', '--out-file', default='./webserver-configuration', required=False, help='Filename where the generated Output should stored.')
|
||||||
parser.add_argument('-v', '--debug', required=False, action='store_true', help='Enable debugging output.')
|
parser.add_argument('-v', '--debug', required=False, action='store_true', help='Enable debugging output.')
|
||||||
parser.add_argument('-f', '--filters', nargs='*', required=False, choices=('exclude_clouds_with_lan_links', 'no_lan'), help='Filter out nodes and local clouds based on filter rules.')
|
parser.add_argument('-f', '--filters', nargs='*', required=False, choices=('exclude_clouds_with_lan_links', 'no_lan', 'domain_transitions_only'), help='Filter out nodes and local clouds based on filter rules.')
|
||||||
parser.add_argument('-i', '--info', nargs='*', required=False, choices=('get_offline_nodes','offline'), help='Get infos about the graph, links and nodes.')
|
parser.add_argument('-i', '--info', nargs='*', required=False, choices=('get_offline_nodes','offline'), help='Get infos about the graph, links and nodes.')
|
||||||
parser.add_argument('-if', '--info-filters', nargs='*', required=False, help='Filter info results. Currently supported: min_age:TIME_RANGE, max_age:TIME_RANGE. Examples: -if min_age:1d max_age:2w')
|
parser.add_argument('-if', '--info-filters', nargs='*', required=False, help='Filter info results. Currently supported: min_age:TIME_RANGE, max_age:TIME_RANGE. Examples: -if min_age:1d max_age:2w')
|
||||||
parser.add_argument('-iop', '--info-out-path', required=False, default='./', help='Folder where info files should be written. Default: ./')
|
parser.add_argument('-iop', '--info-out-path', required=False, default='./', help='Folder where info files should be written. Default: ./')
|
||||||
|
|
29
README.md
29
README.md
|
@ -1,5 +1,5 @@
|
||||||
# Node Hierarchy
|
# Node Hierarchy
|
||||||
Dieses Tool generiert auf Basis einer ``graph.json`` und ``nodes.json`` des [Meshviewers](https://github.com/ffnord/meshviewer/) sowie (Multi-)Polygonen (im [geojson](http://geojson.org/) Format) der einzelnen Zieldomänen eine [nginx](http://nginx.org/) Konfigurationsdatei (auf Basis des [Geo-Moduls](http://nginx.org/en/docs/http/ngx_http_geo_module.html)), um Knoten in der richtigen Reihenfolge umzuziehen.
|
Dieses Tool generiert auf Basis einer ``raw.json`` des [hopglass-server](https://github.com/hopglass/hopglass-server) sowie (Multi-)Polygonen (im [geojson](http://geojson.org/) Format) der einzelnen Zieldomänen eine [nginx](http://nginx.org/) Konfigurationsdatei (auf Basis des [Geo-Moduls](http://nginx.org/en/docs/http/ngx_http_geo_module.html)), um Knoten in der richtigen Reihenfolge umzuziehen.
|
||||||
|
|
||||||
|
|
||||||
## Vorgehensweise
|
## Vorgehensweise
|
||||||
|
@ -28,9 +28,10 @@ Die Hilfe liefert folgendes:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ ./NodeHierarchy.py --help
|
$ ./NodeHierarchy.py --help
|
||||||
usage: NodeHierarchy.py [-h] [-j JSON_PATH] [-s SHAPES_PATH] -t TARGETS
|
usage: NodeHierarchy.py [-h] [-r RAW_JSON] [-s SHAPES_PATH] -t TARGETS
|
||||||
[TARGETS ...] [-o OUT_FILE] [-v]
|
[TARGETS ...] [-sttp SITE_TO_TARGET_PREFIX]
|
||||||
[-f [{exclude_clouds_with_lan_links,no_lan} [{exclude_clouds_with_lan_links,no_lan} ...]]]
|
[-o OUT_FILE] [-v]
|
||||||
|
[-f [{exclude_clouds_with_lan_links,no_lan,domain_transitions_only} [{exclude_clouds_with_lan_links,no_lan,domain_transitions_only} ...]]]
|
||||||
[-i [{get_offline_nodes,offline} [{get_offline_nodes,offline} ...]]]
|
[-i [{get_offline_nodes,offline} [{get_offline_nodes,offline} ...]]]
|
||||||
[-if [INFO_FILTERS [INFO_FILTERS ...]]]
|
[-if [INFO_FILTERS [INFO_FILTERS ...]]]
|
||||||
[-iop INFO_OUT_PATH]
|
[-iop INFO_OUT_PATH]
|
||||||
|
@ -41,19 +42,22 @@ geo feature.
|
||||||
|
|
||||||
optional arguments:
|
optional arguments:
|
||||||
-h, --help show this help message and exit
|
-h, --help show this help message and exit
|
||||||
-j JSON_PATH, --json-path JSON_PATH
|
-r RAW_JSON, --raw-json RAW_JSON
|
||||||
Path of nodes.json and graph.json (can be local folder
|
Location of raw.json file (can be local folder or
|
||||||
or remote URL).
|
remote URL).
|
||||||
-s SHAPES_PATH, --shapes-path SHAPES_PATH
|
-s SHAPES_PATH, --shapes-path SHAPES_PATH
|
||||||
Path of shapefiles (can be local folder or remote
|
Path of shapefiles (can be local folder or remote
|
||||||
URL).
|
URL).
|
||||||
-t TARGETS [TARGETS ...], --targets TARGETS [TARGETS ...]
|
-t TARGETS [TARGETS ...], --targets TARGETS [TARGETS ...]
|
||||||
List of targets which should be proceeded. Example: -t
|
List of targets which should be proceeded. Example: -t
|
||||||
citya cityb ...
|
citya cityb ...
|
||||||
|
-sttp SITE_TO_TARGET_PREFIX, --site-to-target-prefix SITE_TO_TARGET_PREFIX
|
||||||
|
Used to match site and target also when prefixes are
|
||||||
|
different. Example: -sttp "ffmsd,domaene"
|
||||||
-o OUT_FILE, --out-file OUT_FILE
|
-o OUT_FILE, --out-file OUT_FILE
|
||||||
Filename where the generated Output should stored.
|
Filename where the generated Output should stored.
|
||||||
-v, --debug Enable debugging output.
|
-v, --debug Enable debugging output.
|
||||||
-f [{exclude_clouds_with_lan_links,no_lan} [{exclude_clouds_with_lan_links,no_lan} ...]], --filters [{exclude_clouds_with_lan_links,no_lan} [{exclude_clouds_with_lan_links,no_lan} ...]]
|
-f [{exclude_clouds_with_lan_links,no_lan,domain_transitions_only} [{exclude_clouds_with_lan_links,no_lan,domain_transitions_only} ...]], --filters [{exclude_clouds_with_lan_links,no_lan,domain_transitions_only} [{exclude_clouds_with_lan_links,no_lan,domain_transitions_only} ...]]
|
||||||
Filter out nodes and local clouds based on filter
|
Filter out nodes and local clouds based on filter
|
||||||
rules.
|
rules.
|
||||||
-i [{get_offline_nodes,offline} [{get_offline_nodes,offline} ...]], --info [{get_offline_nodes,offline} [{get_offline_nodes,offline} ...]]
|
-i [{get_offline_nodes,offline} [{get_offline_nodes,offline} ...]], --info [{get_offline_nodes,offline} [{get_offline_nodes,offline} ...]]
|
||||||
|
@ -72,7 +76,7 @@ optional arguments:
|
||||||
### Anmerkungen
|
### Anmerkungen
|
||||||
|
|
||||||
- ``--targets`` Gibt die Namen der Ziele (Zieldomänen) an. Der Geo-Schalter in der nginx-Konfiguration wird ebenfalls diesen Namen tragen.
|
- ``--targets`` Gibt die Namen der Ziele (Zieldomänen) an. Der Geo-Schalter in der nginx-Konfiguration wird ebenfalls diesen Namen tragen.
|
||||||
- ``--json-path`` Gibt das Daten-Verzeichnis eures Meshviewers an. Default: ``https://service.freifunk-muensterland.de/maps/data/``
|
- ``--raw-json`` Gibt den Ort der raw.json (hopglass-server) an. Default: ``https://karte.freifunk-muensterland.de/data/raw.json``
|
||||||
- ``--shapes-path`` Verzeichnis an dem die Shapefiles der einzelnen Ziel-Domänen liegen. Default: ``https://freifunk-muensterland.de/md-fw-dl/shapes/``
|
- ``--shapes-path`` Verzeichnis an dem die Shapefiles der einzelnen Ziel-Domänen liegen. Default: ``https://freifunk-muensterland.de/md-fw-dl/shapes/``
|
||||||
- *Anmerkung:* Es werden Dateien in Abhängigkeit mit den Target-Namen im Verzeichnis erwartet.
|
- *Anmerkung:* Es werden Dateien in Abhängigkeit mit den Target-Namen im Verzeichnis erwartet.
|
||||||
- *Beispiel:* Bei ``-targets domaene01 domaene02`` werden die Dateien ``domaene01.geojson`` und ``domaene02.geojson`` erwartet.
|
- *Beispiel:* Bei ``-targets domaene01 domaene02`` werden die Dateien ``domaene01.geojson`` und ``domaene02.geojson`` erwartet.
|
||||||
|
@ -89,7 +93,8 @@ Weitere Filterungen lassen sich über das ``--filters`` Attribut aktivieren.
|
||||||
|
|
||||||
Folgende Filter sind derzeit implementiert (zukünftig folgen noch weitere):
|
Folgende Filter sind derzeit implementiert (zukünftig folgen noch weitere):
|
||||||
|
|
||||||
- ``exclude_clouds_with_lan_links`` bzw. ``no_lan`` Filtert alle lokalen Wolken aus, in denen sich mindestens ein Mesh-on-LAN Link befindet
|
- ``exclude_clouds_with_lan_links`` bzw. ``no_lan`` filtert alle lokalen Wolken aus, in denen sich mindestens ein Mesh-on-LAN Link befindet
|
||||||
|
- ``domain_transitions_only`` filtert alle Knoten aus, die sich bereits in der richtigen Domäne befinden / die Firmware der richtigen Domäne besitzen
|
||||||
|
|
||||||
|
|
||||||
## Nginx Konfiguration
|
## Nginx Konfiguration
|
||||||
|
@ -130,9 +135,9 @@ schreibt in die Datei ``./offline_nodes.csv`` (default-Einstellung der Schalter
|
||||||
|
|
||||||
|
|
||||||
## Bekannte Probleme
|
## Bekannte Probleme
|
||||||
Wenn es sich bei der Quell-Domäne um eine L2TP-Domäne handelt, läuft das Tool derzeit nur, wenn [alfred](https://github.com/ffnord/ffnord-alfred-announce) auf allen Gateway-Servern läuft.
|
Wenn es sich bei der Quell-Domäne um eine L2TP-Domäne handelt, läuft das Tool derzeit nur, wenn [alfred](https://github.com/ffnord/ffnord-alfred-announce) oder respondd auf allen Gateway-Servern läuft.
|
||||||
|
|
||||||
*Anmerkung:* Wenn in der ``nodes.json`` und ``graph.json`` mehrere Domänen vorhanden sind und dort teilweise L2TP-Domänen vorhanden sind (dieses aber nicht das Gebiet eurer Zieldomäne betrifft), kann das sehr negative Auswirkungen auf die Laufzeit haben (> 30 Sekunden).
|
*Anmerkung:* Wenn in der ``graph.json`` mehrere Domänen vorhanden sind und dort teilweise L2TP-Domänen vorhanden sind (dieses aber nicht das Gebiet eurer Zieldomäne betrifft), kann das sehr negative Auswirkungen auf die Laufzeit haben (> 30 Sekunden).
|
||||||
|
|
||||||
|
|
||||||
## Lizenz
|
## Lizenz
|
||||||
|
|
|
@ -26,18 +26,19 @@ class GlobalGraph(Graph):
|
||||||
|
|
||||||
def __getConnectedNodes__(self, nodeID, trace = []):
|
def __getConnectedNodes__(self, nodeID, trace = []):
|
||||||
neighNodeIDs = self.getNeighbourNodeIDsForNodeID(nodeID)
|
neighNodeIDs = self.getNeighbourNodeIDsForNodeID(nodeID)
|
||||||
trace_new = trace[:] + [x for x in neighNodeIDs if x not in trace]
|
trace_new = list(set(trace + neighNodeIDs))
|
||||||
for neighNodeID in neighNodeIDs:
|
for neighNodeID in neighNodeIDs:
|
||||||
if neighNodeID not in trace:
|
if neighNodeID not in trace:
|
||||||
trace_new = trace_new + [x for x in self.__getConnectedNodes__(neighNodeID, trace_new) if x not in trace_new]
|
trace_new = list(set(trace_new + self.__getConnectedNodes__(neighNodeID, trace_new)))
|
||||||
return trace_new
|
return trace_new
|
||||||
|
|
||||||
def __createLocalCloudByNodesList__(self, nodesIDList):
|
def __createLocalCloudByNodesList__(self, nodesIDList):
|
||||||
nodes = {}
|
nodes = {}
|
||||||
links = []
|
links = []
|
||||||
for nodeID in nodesIDList:
|
for nodeID in nodesIDList:
|
||||||
nodes[nodeID] = self.__nodes__[nodeID]
|
if nodeID:
|
||||||
links = links + [x for x in self.getLinksByNodeID(nodeID) if x not in links]
|
nodes[nodeID] = self.__nodes__[nodeID]
|
||||||
|
links = list(set(links + self.getLinksByNodeID(nodeID)))
|
||||||
return LocalGraph(nodes, links, self.__enableDebugPrinting__)
|
return LocalGraph(nodes, links, self.__enableDebugPrinting__)
|
||||||
|
|
||||||
def __debugPrint__(self):
|
def __debugPrint__(self):
|
||||||
|
|
|
@ -26,7 +26,7 @@ class Graph(object):
|
||||||
if link.isVpn == False:
|
if link.isVpn == False:
|
||||||
endpoints = link.getEndpointNodeIDs(getGateways = False)
|
endpoints = link.getEndpointNodeIDs(getGateways = False)
|
||||||
if nodeID in endpoints:
|
if nodeID in endpoints:
|
||||||
neighNodeIDs = neighNodeIDs + [x for x in endpoints if x not in neighNodeIDs]
|
neighNodeIDs = list(set(neighNodeIDs + endpoints))
|
||||||
return neighNodeIDs
|
return neighNodeIDs
|
||||||
|
|
||||||
def getLinksByNodeID(self, nodeID):
|
def getLinksByNodeID(self, nodeID):
|
||||||
|
|
|
@ -1,69 +1,31 @@
|
||||||
class Link(object):
|
class Link(object):
|
||||||
def __init__(self, LinkJsonObject, srcNode, dstNode):
|
def __init__(self, LinkJsonObject, nodes):
|
||||||
self.__jsonObject__ = LinkJsonObject
|
self.__jsonObject = LinkJsonObject
|
||||||
self.__srcNode__ = srcNode
|
self.linkType, self.isVpn = self.__getLinkType__()
|
||||||
self.__dstNode__ = dstNode
|
self.__nodes = nodes
|
||||||
self.linkType = self.__getLinkType__()
|
|
||||||
self.isVpn = self.__getLinkVpnState__()
|
|
||||||
|
|
||||||
|
|
||||||
def __getLinkType__(self):
|
def __getLinkType__(self):
|
||||||
type_src = None
|
types = [x['type'] for x in self.__jsonObject]
|
||||||
type_dst = None
|
ltype = types[0]
|
||||||
if self.__srcNode__ != None:
|
lvpn = False
|
||||||
for k, v in self.__srcNode__.interfaces.items():
|
for x in types:
|
||||||
if self.__jsonObject__['source']['interface_mac'] in v:
|
if x != 'unknown' and x != 'other':
|
||||||
type_src = k
|
if x == 'l2tp' or x == 'tunnel':
|
||||||
if self.__dstNode__ != None:
|
lvpn = True
|
||||||
for k, v in self.__dstNode__.interfaces.items():
|
val = x
|
||||||
if self.__jsonObject__['target']['interface_mac'] in v:
|
return ltype, lvpn
|
||||||
type_dst = k
|
|
||||||
|
|
||||||
if type_src == type_dst:
|
|
||||||
if type_src == None:
|
|
||||||
return 'unknown'
|
|
||||||
return type_src
|
|
||||||
else:
|
|
||||||
if type_src == None:
|
|
||||||
return type_dst
|
|
||||||
elif type_dst == None:
|
|
||||||
return type_src
|
|
||||||
else:
|
|
||||||
#print(self.__srcNode__.hostname, type_src, '<-->', self.__dstNode__.hostname, type_dst)
|
|
||||||
if type_src == 'wireless':
|
|
||||||
return type_dst
|
|
||||||
else:
|
|
||||||
return type_src
|
|
||||||
|
|
||||||
def __getLinkVpnState__(self):
|
|
||||||
if self.__jsonObject__['vpn'] == True:
|
|
||||||
return True
|
|
||||||
for node in self.getEndpointNodes(getGateways = True):
|
|
||||||
if node.isGateway == True:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def getEndpointNodes(self, getGateways = False):
|
def getEndpointNodes(self, getGateways = False):
|
||||||
nodes = []
|
return self.__nodes
|
||||||
if self.__srcNode__ != None:
|
|
||||||
if getGateways == True or self.__srcNode__.isGateway == False:
|
|
||||||
nodes.append(self.__srcNode__)
|
|
||||||
if self.__dstNode__ != None:
|
|
||||||
if getGateways == True or self.__dstNode__.isGateway == False:
|
|
||||||
nodes.append(self.__dstNode__)
|
|
||||||
return nodes
|
|
||||||
|
|
||||||
def getEndpointNodeIDs(self, getGateways = True):
|
def getEndpointNodeIDs(self, getGateways = True):
|
||||||
nodeIDs = []
|
return [x.nodeID for x in self.__nodes]
|
||||||
for node in self.getEndpointNodes(getGateways):
|
|
||||||
nodeIDs.append(node.nodeID)
|
|
||||||
return nodeIDs
|
|
||||||
|
|
||||||
def isNodeIDinLink(self, nodeID):
|
def isNodeIDinLink(self, nodeID):
|
||||||
for endpoint in self.getEndpointNodes():
|
for x in self.__nodes:
|
||||||
if endpoint.nodeID == nodeID:
|
if nodeID == x.nodeID:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def isNodeInLink(self, node):
|
def isNodeInLink(self, node):
|
||||||
return self.isNodeIDinLink(node.nodeID)
|
return self.isNodeIDinLink(node.nodeID)
|
||||||
|
|
|
@ -125,6 +125,9 @@ class LocalGraph(Graph):
|
||||||
print('BranchesThatExistsInCloud:', self.getBranchesThatExistsInCloud())
|
print('BranchesThatExistsInCloud:', self.getBranchesThatExistsInCloud())
|
||||||
print('lan links in cloud:')
|
print('lan links in cloud:')
|
||||||
for link in self.getLanLinksInCloud():
|
for link in self.getLanLinksInCloud():
|
||||||
if link.__srcNode__ != None and link.__dstNode__ != None:
|
hosts = link.getEndpointNodes()
|
||||||
print(' ', link.__srcNode__.hostname, '<--->', link.__dstNode__.hostname)
|
if len(hosts) == 1:
|
||||||
|
print(' ', hosts.hostname, 'has unknown neighbour.')
|
||||||
|
else:
|
||||||
|
print(' ', hosts[0].hostname, '<--->', hosts[1].hostname)
|
||||||
print('=====')
|
print('=====')
|
||||||
|
|
|
@ -4,13 +4,13 @@ class NodeInit(object):
|
||||||
self.nodeID = self.__jsonObject__['nodeinfo']['node_id']
|
self.nodeID = self.__jsonObject__['nodeinfo']['node_id']
|
||||||
self.interfaces = self.__getInterfaces__()
|
self.interfaces = self.__getInterfaces__()
|
||||||
self.hostname = self.__jsonObject__['nodeinfo']['hostname']
|
self.hostname = self.__jsonObject__['nodeinfo']['hostname']
|
||||||
self.isGateway = self.__jsonObject__['flags']['gateway']
|
self.isGateway = self.__jsonObject__['nodeinfo']['isGateway']
|
||||||
self.geo = self.__getGeo__()
|
self.geo = self.__getGeo__()
|
||||||
self.isAutoupdaterEnabled = self.__getAutoupdaterStatus__()
|
self.isAutoupdaterEnabled = self.__getAutoupdaterStatus__()
|
||||||
self.autoupdaterBranch = self.__getBranch__()
|
self.autoupdaterBranch = self.__getBranch__()
|
||||||
self.isOnline = self.__jsonObject__['flags']['online']
|
self.isOnline = self.__jsonObject__['nodeinfo']['isOnline']
|
||||||
self.publicIPv6Addresses = self.__getPublicAddresses__()
|
self.publicIPv6Addresses = self.__getPublicAddresses__()
|
||||||
self.domID = self.__getSiteCode__()
|
self.domName = self.__getSiteCode__()
|
||||||
|
|
||||||
def __getInterfaces__(self):
|
def __getInterfaces__(self):
|
||||||
try:
|
try:
|
||||||
|
@ -19,31 +19,34 @@ class NodeInit(object):
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def __getAutoupdaterStatus__(self):
|
def __getAutoupdaterStatus__(self):
|
||||||
if 'autoupdater' in self.__jsonObject__['nodeinfo']['software']:
|
try:
|
||||||
return self.__jsonObject__['nodeinfo']['software']['autoupdater']['enabled']
|
return self.__jsonObject__['nodeinfo']['software']['autoupdater']['enabled']
|
||||||
else:
|
except:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def __getBranch__(self):
|
def __getBranch__(self):
|
||||||
if 'autoupdater' in self.__jsonObject__['nodeinfo']['software']:
|
try:
|
||||||
return self.__jsonObject__['nodeinfo']['software']['autoupdater']['branch']
|
return self.__jsonObject__['nodeinfo']['software']['autoupdater']['branch']
|
||||||
else:
|
except:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def __getGeo__(self):
|
def __getGeo__(self):
|
||||||
geo = {}
|
try:
|
||||||
if 'location' in self.__jsonObject__['nodeinfo'] and 'latitude' in self.__jsonObject__['nodeinfo']['location'] and 'longitude' in self.__jsonObject__['nodeinfo']['location']:
|
return {
|
||||||
geo['lat'] = self.__jsonObject__['nodeinfo']['location']['latitude']
|
'lat' : self.__jsonObject__['nodeinfo']['location']['latitude'],
|
||||||
geo['lon'] = self.__jsonObject__['nodeinfo']['location']['longitude']
|
'lon' : self.__jsonObject__['nodeinfo']['location']['longitude']
|
||||||
return geo
|
}
|
||||||
return None
|
except:
|
||||||
|
return None
|
||||||
|
|
||||||
def __getPublicAddresses__(self):
|
def __getPublicAddresses__(self):
|
||||||
addresses = []
|
addresses = []
|
||||||
if 'addresses' in self.__jsonObject__['nodeinfo']['network']:
|
try:
|
||||||
for address in self.__jsonObject__['nodeinfo']['network']['addresses']:
|
for address in self.__jsonObject__['nodeinfo']['network']['addresses']:
|
||||||
if not address.startswith('fe80'):
|
if not address.startswith('fe80'):
|
||||||
addresses.append(address)
|
addresses.append(address)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
return addresses
|
return addresses
|
||||||
|
|
||||||
def __getSiteCode__(self):
|
def __getSiteCode__(self):
|
||||||
|
|
|
@ -3,7 +3,7 @@ class Filter(object):
|
||||||
self.__args__ = args
|
self.__args__ = args
|
||||||
self.__filters__ = self.__getFilters()
|
self.__filters__ = self.__getFilters()
|
||||||
|
|
||||||
def filterLocalGraphs(self, localGraphs):
|
def filterLocalGraphs(self, domain, localGraphs):
|
||||||
filteredGraphs = []
|
filteredGraphs = []
|
||||||
for localGraph in localGraphs:
|
for localGraph in localGraphs:
|
||||||
if localGraph.isAutoupdaterEnabledOnAllNodes() == False:
|
if localGraph.isAutoupdaterEnabledOnAllNodes() == False:
|
||||||
|
@ -21,8 +21,14 @@ class Filter(object):
|
||||||
def __getFilters(self):
|
def __getFilters(self):
|
||||||
return [] if self.__args__.filters == None else self.__args__.filters
|
return [] if self.__args__.filters == None else self.__args__.filters
|
||||||
|
|
||||||
def filterNodes(self, nodes):
|
def filterNodes(self, domain, nodes):
|
||||||
filteredNodes = []
|
filteredNodes = []
|
||||||
for node in nodes:
|
for node in nodes:
|
||||||
|
if 'domain_transitions_only' in self.__filters__:
|
||||||
|
try:
|
||||||
|
if domain.name == node.domName:
|
||||||
|
continue
|
||||||
|
except:
|
||||||
|
pass
|
||||||
filteredNodes.append(node)
|
filteredNodes.append(node)
|
||||||
return filteredNodes
|
return filteredNodes
|
||||||
|
|
|
@ -17,9 +17,9 @@ class NginxConfGen(object):
|
||||||
|
|
||||||
def __genDomain__(self, domain):
|
def __genDomain__(self, domain):
|
||||||
nodes = {}
|
nodes = {}
|
||||||
for localGraph in self.__filter__.filterLocalGraphs(domain.localGraphs):
|
for localGraph in self.__filter__.filterLocalGraphs(domain, domain.localGraphs):
|
||||||
try:
|
try:
|
||||||
for node in self.__filter__.filterNodes(localGraph.getNodesWithNoDependencies()):
|
for node in self.__filter__.filterNodes(domain, localGraph.getNodesWithNoDependencies()):
|
||||||
nodes[node.nodeID] = {
|
nodes[node.nodeID] = {
|
||||||
'hostname' : node.hostname,
|
'hostname' : node.hostname,
|
||||||
'ipv6_addresses' : node.publicIPv6Addresses
|
'ipv6_addresses' : node.publicIPv6Addresses
|
||||||
|
|
84
parser/Hopglass.py
Normal file
84
parser/Hopglass.py
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
from parser.JsonParser import JsonParser
|
||||||
|
import collections
|
||||||
|
import json
|
||||||
|
|
||||||
|
class Hopglass(JsonParser):
|
||||||
|
def __init__(self, filePath):
|
||||||
|
super().__init__(filePath)
|
||||||
|
self.ifIDs = {}
|
||||||
|
self.links = collections.defaultdict(dict)
|
||||||
|
self.nodes = {}
|
||||||
|
self.gatewayMacs = []
|
||||||
|
self.gateways = []
|
||||||
|
self.__aggregateData__()
|
||||||
|
# print(self.ifIDs)
|
||||||
|
# for k, v in self.links.items():
|
||||||
|
# print(k,v,'\n')
|
||||||
|
|
||||||
|
def __aggregateData__(self):
|
||||||
|
for nodeID, nodeData in self.__jsonData__.items():
|
||||||
|
|
||||||
|
# let pass nodes that provide all required informations only
|
||||||
|
if not set(('nodeinfo', 'neighbours', 'statistics')) <= set(nodeData):
|
||||||
|
continue
|
||||||
|
|
||||||
|
nodeInfo = nodeData['nodeinfo']
|
||||||
|
neighbours = nodeData['neighbours']
|
||||||
|
statistics = nodeData['statistics']
|
||||||
|
|
||||||
|
if not 'batadv' in neighbours:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not 'mesh' in nodeInfo.get('network', {}):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if statistics.get('gateway', False):
|
||||||
|
self.gatewayMacs.append(statistics['gateway'])
|
||||||
|
|
||||||
|
for batID, batVal in nodeInfo['network']['mesh'].items():
|
||||||
|
if not 'interfaces' in batVal:
|
||||||
|
continue
|
||||||
|
for ifType, ifVal in batVal['interfaces'].items():
|
||||||
|
for mac in ifVal:
|
||||||
|
self.ifIDs[mac] = {
|
||||||
|
'type' : ifType,
|
||||||
|
'nodeID' : nodeID
|
||||||
|
}
|
||||||
|
|
||||||
|
self.nodes[nodeID] = nodeData
|
||||||
|
|
||||||
|
for nodeID, nodeData in self.nodes.items():
|
||||||
|
nodeData['nodeinfo']['isGateway'] = False
|
||||||
|
nodeData['nodeinfo']['isOnline'] = True # Todo: implement detection
|
||||||
|
for iname, ivalue in nodeData['neighbours']['batadv'].items():
|
||||||
|
if 'neighbours' not in ivalue:
|
||||||
|
continue
|
||||||
|
if iname in self.gatewayMacs:
|
||||||
|
nodeData['nodeinfo']['isGateway'] = True
|
||||||
|
if not iname in self.ifIDs:
|
||||||
|
continue
|
||||||
|
for nname, nvalue in ivalue['neighbours'].items():
|
||||||
|
if nname not in self.ifIDs:
|
||||||
|
continue
|
||||||
|
nifID = self.ifIDs[nname]['nodeID']
|
||||||
|
partID = (nodeID, nifID) if nodeID > nifID else (nifID, nodeID)
|
||||||
|
linkID = (iname, nname) if iname > nname else (nname, iname)
|
||||||
|
|
||||||
|
linkNode = {
|
||||||
|
'nodeID' : nodeID,
|
||||||
|
'type' : self.ifIDs[iname]['type'],
|
||||||
|
'tq' : nvalue['tq']
|
||||||
|
}
|
||||||
|
|
||||||
|
if linkID in self.links[partID]:
|
||||||
|
self.links[partID][linkID].append(linkNode)
|
||||||
|
else:
|
||||||
|
self.links[partID][linkID] = [linkNode]
|
||||||
|
|
||||||
|
|
||||||
|
def getLinksForNodeID(self, nodeID):
|
||||||
|
links = []
|
||||||
|
for link in self.links:
|
||||||
|
if link['target']['node_id'] == nodeID or link['source']['node_id'] == nodeID:
|
||||||
|
links.append(link)
|
||||||
|
return links
|
|
@ -9,7 +9,7 @@ class JsonParser(object):
|
||||||
def __getFile__(self, fileName):
|
def __getFile__(self, fileName):
|
||||||
if fileName.startswith('https://') or fileName.startswith('http://'):
|
if fileName.startswith('https://') or fileName.startswith('http://'):
|
||||||
if self.printStatus:
|
if self.printStatus:
|
||||||
print('Download', fileName.rsplit('/', 1)[1] , 'from URL:', fileName)
|
print('Download', fileName, 'from URL:', fileName)
|
||||||
resource = urllib.request.urlopen(fileName)
|
resource = urllib.request.urlopen(fileName)
|
||||||
try:
|
try:
|
||||||
data = json.loads(resource.read().decode('utf-8'))
|
data = json.loads(resource.read().decode('utf-8'))
|
||||||
|
@ -19,7 +19,7 @@ class JsonParser(object):
|
||||||
resource.close()
|
resource.close()
|
||||||
else:
|
else:
|
||||||
if self.printStatus:
|
if self.printStatus:
|
||||||
print('Open', fileName.rsplit('/', 1)[1] , 'from file:', fileName)
|
print('Open', fileName, 'from file:', fileName)
|
||||||
with open(fileName) as data_file:
|
with open(fileName) as data_file:
|
||||||
try:
|
try:
|
||||||
data = json.load(data_file)
|
data = json.load(data_file)
|
||||||
|
|
|
@ -7,6 +7,12 @@ class ShapesParser(JsonParser):
|
||||||
|
|
||||||
def __createShapes__(self):
|
def __createShapes__(self):
|
||||||
shapes = []
|
shapes = []
|
||||||
for feature in self.__jsonData__['features']:
|
if 'features' in self.__jsonData__:
|
||||||
shapes.append(shape(feature['geometry']))
|
for feature in self.__jsonData__['features']:
|
||||||
|
if feature['geometry']:
|
||||||
|
shapes.append(shape(feature['geometry']))
|
||||||
|
elif 'geometries' in self.__jsonData__:
|
||||||
|
for geometry in self.__jsonData__['geometries']:
|
||||||
|
shapes.append(shape(geometry))
|
||||||
|
|
||||||
return shapes
|
return shapes
|
||||||
|
|
1
requirements.txt
Normal file
1
requirements.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
shapely
|
Loading…
Reference in a new issue