Commit 3a68ae73 authored by Simon Redman's avatar Simon Redman

Add partial controller for ospf daemon

parent e286490b
#!/usr/bin/env python3
# Copyright (C) 2018 Simon Redman <sredman@cs.utah.edu>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import argparse
import getpass
from netdiff import NetJsonParser
import networkx
import re
from typing import List, Optional
import ssh_helper
DEFAULT_CLONE_PATH="~/sniffer/"
DEFAULT_CONTROLLER='core0'
DEFAULT_EXECUTABLE='main.py'
DEFAULT_PIDFILE="/tmp/sniffer.pid"
DEFAULT_REPO_BRANCH='release'
DEFAULT_REPO_URL="https://gitlab.flux.utah.edu/safeedge/ospfv3_monitor.git"
def generate_repo_clone_command(repo: str, branch: str, path: str) -> str:
return "git clone {repo} -b {branch} --single-branch {path}".format(repo=repo, branch=branch, path=path)
def generate_start_command(path:str=DEFAULT_CLONE_PATH,
executable: str=DEFAULT_EXECUTABLE,
controller: str=DEFAULT_CONTROLLER,
pidfile: str=DEFAULT_PIDFILE,
) -> str:
"""
Generate a command which launches the OSPF sniffer on all requested nodes in the network
Does not do any initialization, such as downloading the sniffer
:param path: Path into which the sniffer was cloned
:param executable: Filename of the sniffer daemon from the repository root
:param controller: Hostname or IP address to which sniffer reports should be sent
:param pidfile: File to which the PID will be written
:return:
"""
return "{path}/{executable} --controller={controller} & echo $! > {pidfile} && disown".format(
path=path,
executable=executable,
controller=controller,
pidfile=pidfile,
)
def clone_repo_on_network(graph: networkx.Graph,
repo: str=DEFAULT_REPO_URL,
branch: str=DEFAULT_REPO_BRANCH,
path: str=DEFAULT_CLONE_PATH,
ignore_nodes: List[str]=None,
) -> Optional[List[str]]:
"""
Clone the specified repository and branch on each non-ignored node in the network
:param graph: networkx.Graph representation of the network
:param repo: Repository to clone
:param branch: Branch in the repository to clone
:param path: Path into which to clone the repository
:param ignore_nodes: Nodes in the network which should not have the repository cloned
:return:
"""
command = generate_repo_clone_command(repo=repo, branch=branch, path=path)
hosts = [host for host in graph.nodes if host not in ignore_nodes]
commands = [command for host in hosts]
sessions = [graph._node[host]['session'] for host in hosts]
outputs = ssh_helper.run_commands_on_many_hosts(sessions, commands)
codes = ssh_helper.get_exit_codes(sessions)
errors = []
for index in range(0, len(codes)):
code = codes[index]
if code != 0:
errors.append(outputs[index])
if len(errors) == 0:
return None
else:
return errors
def start_sniffer_on_network(graph: networkx.Graph,
path:str=DEFAULT_CLONE_PATH,
executable: str=DEFAULT_EXECUTABLE,
controller: str=DEFAULT_CONTROLLER,
pidfile: str=DEFAULT_PIDFILE,
ignore_nodes: List[str]=None) -> Optional[str]:
"""
Start the sniffer daemon on all non-ignored nodes in the network
:param graph: networkx.Graph representation of the network
:param path: path into which the repository has been cloned
:param executable: executable to execute
:param controller: node which is listening for the OSPF reports
:param pidfile: file to write the PID of the daemon
:param ignore_nodes: nodes which should not have the daemon run on them
:return:
"""
command = generate_start_command(path=path, executable=executable, controller=controller, pidfile=pidfile)
hosts = [host for host in graph.nodes if host not in ignore_nodes]
commands = [command for host in hosts]
sessions = [graph._node[host]['session'] for host in hosts]
outputs = ssh_helper.run_commands_on_many_hosts(sessions, commands)
codes = ssh_helper.get_exit_codes(sessions)
errors = []
for index in range(0, len(codes)):
code = codes[index]
if code != 0:
errors.append(outputs[index])
if len(errors) == 0:
return None
else:
return errors
def stop_sniffer_on_network(graph: networkx.Graph, pidfile: str=DEFAULT_PIDFILE) -> Optional[str]:
pass
if __name__ == "__main__":
parser = argparse.ArgumentParser("Configure and control the OSPF path sniffer for all nodes in the network")
parser.add_argument("--in-file", action='store', type=str, required=True,
help="Path to the NetJSON file to parse")
parser.add_argument("--username", action='store', type=str, default=getpass.getuser(),
help="Username to use on all hosts. Defaults to current user's username")
parser.add_argument("--stop", action='store_true',
help="Stop all sniffers")
parser.add_argument("--ignore-regex", action='store', type=str, default='.*ovs.*',
help="Regex used to determine which nodes, if any, should be ignored (Default: \".*ovs.*\")")
parser.add_argument("--pid-file", action='store', type=str, default=DEFAULT_PIDFILE,
help="File to write sniffer's PID (Default: {default})".format(default=DEFAULT_PIDFILE))
parser.add_argument("--controller-name", action='store', type=str, default=DEFAULT_CONTROLLER,
help="Hostname or IP of the node which is listening to the OSPF reports (Default: {default})".format(default=DEFAULT_CONTROLLER))
parser.add_argument("--repo-path", action='store', type=str, default=DEFAULT_REPO_URL,
help="(git) Repository which should be downloaded for the OSPF sniffer (Default: {default})".format(default=DEFAULT_REPO_URL))
parser.add_argument("--repo-branch", action='store', type=str, default=DEFAULT_REPO_BRANCH,
help="Name of the branch in the repository to check out (Default: {default})".format(default=DEFAULT_REPO_BRANCH))
parser.add_argument("--clone-path", action='store', type=str, default=DEFAULT_CLONE_PATH,
help="Location where the repository should be downloaded (Default: {default})".format(default=DEFAULT_CLONE_PATH))
args = parser.parse_args()
netgraph = NetJsonParser(file=args.in_file)
ssh_helper.network_graph_login(netgraph.graph, args.username)
ignore_nodes = [node for node in netgraph.graph.nodes
if re.match(args.ignore_regex, netgraph.graph._node[node]['label'])]
if args.stop:
stop_sniffer_on_network(netgraph.graph)
else:
clone_repo_on_network(netgraph.graph,
repo=args.repo_path,
branch=args.repo_branch,
path=args.clone_path,
ignore_nodes=ignore_nodes)
start_sniffer_on_network(netgraph.graph,
path=args.repo_path,
controller=args.controller_name,
pidfile=args.pid_file,
ignore_nodes=ignore_nodes)
ssh_helper.network_graph_logout(netgraph.graph)
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment