mirror of
https://github.com/checktheroads/hyperglass-frr.git
synced 2024-05-11 05:55:16 +00:00
pylint cleanups
This commit is contained in:
97
.pylintrc
97
.pylintrc
@ -1,3 +1,26 @@
|
||||
# Hyperglass PyLint: Notes
|
||||
#
|
||||
# This is a mostly default pylintrc file, generated by PyLint. Only cosmetic parameters have been
|
||||
# changed, mostly naming-style standards.
|
||||
#
|
||||
# Additionally, the "cyclic-import" and "logging-fstring-interpolation" messages have been disabled.
|
||||
#
|
||||
# "cyclic-import" was disabled due to the structure of the project; almost all modules rely on or
|
||||
# pass data back and forth between other modules.
|
||||
#
|
||||
# "logging-fstring-interpolation" was disabled due to me thinking it's stupid. I find fstrings
|
||||
# extremely valuable, and while I could get around this default setting by setting variables for
|
||||
# each log message, e.g.:
|
||||
# log_message = f"Error: {var1}, {var2}, {var3}"
|
||||
# logger.error(log_message)
|
||||
# I find this to be needlessly obtuse, and therefore log fstrings directly:
|
||||
# logger.error(f"Error: {var1}, {var2}, {var3}")
|
||||
# Perhaps this is "incorrect", but it works well and is more elegant, in my uneducated opinion.
|
||||
#
|
||||
# "duplicate-code" was disabled due to PyLint complaining about using the same logzero debug
|
||||
# configuration in two files. Apparently having a consistent logging configuration is "bad".
|
||||
|
||||
|
||||
[MASTER]
|
||||
|
||||
# A comma-separated list of package or module names from where C extensions may
|
||||
@ -13,10 +36,6 @@ ignore=CVS
|
||||
# regex matches against base names, not paths.
|
||||
ignore-patterns=
|
||||
|
||||
# Python code to execute, usually for sys.path manipulation such as
|
||||
# pygtk.require().
|
||||
#init-hook=
|
||||
|
||||
# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the
|
||||
# number of processors available to use.
|
||||
jobs=1
|
||||
@ -33,9 +52,6 @@ load-plugins=
|
||||
# Pickle collected data for later comparisons.
|
||||
persistent=yes
|
||||
|
||||
# Specify a configuration file.
|
||||
#rcfile=
|
||||
|
||||
# When enabled, pylint would attempt to guess common misconfiguration and emit
|
||||
# user-friendly hints instead of false-positive error messages.
|
||||
suggestion-mode=yes
|
||||
@ -138,7 +154,11 @@ disable=print-statement,
|
||||
xreadlines-attribute,
|
||||
deprecated-sys-function,
|
||||
exception-escape,
|
||||
comprehension-escape
|
||||
comprehension-escape,
|
||||
bad-continuation,
|
||||
cyclic-import,
|
||||
logging-fstring-interpolation,
|
||||
duplicate-code
|
||||
|
||||
# Enable the message, report, category or checker with the given id(s). You can
|
||||
# either give multiple identifier separated by comma (,) or put this option
|
||||
@ -156,10 +176,6 @@ enable=c-extension-no-member
|
||||
# (RP0004).
|
||||
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
|
||||
|
||||
# Template used to display messages. This is a python new-style format string
|
||||
# used to format the message information. See doc for all details.
|
||||
#msg-template=
|
||||
|
||||
# Set the output format. Available formats are text, parseable, colorized, json
|
||||
# and msvs (visual studio). You can also give a reporter class, e.g.
|
||||
# mypackage.mymodule.MyReporterClass.
|
||||
@ -362,17 +378,9 @@ min-similarity-lines=4
|
||||
# Naming style matching correct argument names.
|
||||
argument-naming-style=any
|
||||
|
||||
# Regular expression matching correct argument names. Overrides argument-
|
||||
# naming-style.
|
||||
#argument-rgx=
|
||||
|
||||
# Naming style matching correct attribute names.
|
||||
attr-naming-style=any
|
||||
|
||||
# Regular expression matching correct attribute names. Overrides attr-naming-
|
||||
# style.
|
||||
#attr-rgx=
|
||||
|
||||
# Bad variable names which should always be refused, separated by a comma.
|
||||
bad-names=foo,
|
||||
bar,
|
||||
@ -382,36 +390,20 @@ bad-names=foo,
|
||||
tata
|
||||
|
||||
# Naming style matching correct class attribute names.
|
||||
class-attribute-naming-style=any
|
||||
|
||||
# Regular expression matching correct class attribute names. Overrides class-
|
||||
# attribute-naming-style.
|
||||
#class-attribute-rgx=
|
||||
class-attribute-naming-style=snake_case
|
||||
|
||||
# Naming style matching correct class names.
|
||||
class-naming-style=any
|
||||
|
||||
# Regular expression matching correct class names. Overrides class-naming-
|
||||
# style.
|
||||
#class-rgx=
|
||||
class-naming-style=PascalCase
|
||||
|
||||
# Naming style matching correct constant names.
|
||||
const-naming-style=any
|
||||
|
||||
# Regular expression matching correct constant names. Overrides const-naming-
|
||||
# style.
|
||||
#const-rgx=
|
||||
const-naming-style=snake_case
|
||||
|
||||
# Minimum line length for functions/classes that require docstrings, shorter
|
||||
# ones are exempt.
|
||||
docstring-min-length=-1
|
||||
|
||||
# Naming style matching correct function names.
|
||||
function-naming-style=any
|
||||
|
||||
# Regular expression matching correct function names. Overrides function-
|
||||
# naming-style.
|
||||
#function-rgx=
|
||||
function-naming-style=snake_case
|
||||
|
||||
# Good variable names which should always be accepted, separated by a comma.
|
||||
good-names=i,
|
||||
@ -422,28 +414,16 @@ good-names=i,
|
||||
_
|
||||
|
||||
# Include a hint for the correct naming format with invalid-name.
|
||||
include-naming-hint=no
|
||||
include-naming-hint=yes
|
||||
|
||||
# Naming style matching correct inline iteration names.
|
||||
inlinevar-naming-style=any
|
||||
|
||||
# Regular expression matching correct inline iteration names. Overrides
|
||||
# inlinevar-naming-style.
|
||||
#inlinevar-rgx=
|
||||
|
||||
# Naming style matching correct method names.
|
||||
method-naming-style=any
|
||||
|
||||
# Regular expression matching correct method names. Overrides method-naming-
|
||||
# style.
|
||||
#method-rgx=
|
||||
method-naming-style=snake_case
|
||||
|
||||
# Naming style matching correct module names.
|
||||
module-naming-style=any
|
||||
|
||||
# Regular expression matching correct module names. Overrides module-naming-
|
||||
# style.
|
||||
#module-rgx=
|
||||
module-naming-style=snake_case
|
||||
|
||||
# Colon-delimited sets of names that determine each other's naming style when
|
||||
# the name regexes allow several styles.
|
||||
@ -459,12 +439,7 @@ no-docstring-rgx=^_
|
||||
property-classes=abc.abstractproperty
|
||||
|
||||
# Naming style matching correct variable names.
|
||||
variable-naming-style=any
|
||||
|
||||
# Regular expression matching correct variable names. Overrides variable-
|
||||
# naming-style.
|
||||
#variable-rgx=
|
||||
|
||||
variable-naming-style=snake_case
|
||||
|
||||
[STRING]
|
||||
|
||||
|
@ -1,10 +1,15 @@
|
||||
"""
|
||||
Exports constructed commands and API variables from configuration file based
|
||||
Exports constructed commands and API variables from configuration file based \
|
||||
on input query
|
||||
"""
|
||||
# Module Imports
|
||||
# Standard Imports
|
||||
import os
|
||||
import logging
|
||||
|
||||
# Module Imports
|
||||
import toml
|
||||
import logzero
|
||||
from logzero import logger
|
||||
|
||||
# Project Directories
|
||||
this_directory = os.path.dirname(os.path.abspath(__file__))
|
||||
@ -13,44 +18,62 @@ this_directory = os.path.dirname(os.path.abspath(__file__))
|
||||
conf = toml.load(os.path.join(this_directory, "configuration.toml"))
|
||||
|
||||
|
||||
def debug_state():
|
||||
"""Returns string for logzero log level"""
|
||||
state = conf.get("debug", False)
|
||||
return state
|
||||
|
||||
|
||||
# Logzero Configuration
|
||||
if debug_state():
|
||||
logzero.loglevel(logging.DEBUG)
|
||||
else:
|
||||
logzero.loglevel(logging.INFO)
|
||||
|
||||
|
||||
def api():
|
||||
"""Imports & exports configured API parameters from configuration file"""
|
||||
a = conf["api"]
|
||||
listen_addr = a.get("listen_addr", "*")
|
||||
port = a.get("port", 8080)
|
||||
key = a.get("key", 0)
|
||||
return dict(listen_addr=listen_addr, port=port, key=key)
|
||||
api_dict = {
|
||||
"listen_addr": conf["api"].get("listen_addr", "*"),
|
||||
"port": conf["api"].get("port", 8080),
|
||||
"key": conf["api"].get("key", 0),
|
||||
}
|
||||
return api_dict
|
||||
|
||||
|
||||
class command:
|
||||
class Command:
|
||||
"""Imports & exports configured command syntax from configuration file"""
|
||||
|
||||
def __init__(self, query):
|
||||
self.cmd = query.get("cmd")
|
||||
self.query_type = query.get("query_type")
|
||||
self.afi = query.get("afi")
|
||||
self.source = query.get("source")
|
||||
self.target = query.get("target", 0)
|
||||
c = conf["commands"][self.afi]
|
||||
fc = c.get(self.cmd)
|
||||
self.command = fc.format(source=self.source, target=self.target)
|
||||
raw_command = conf["commands"][self.afi].get(self.query_type)
|
||||
self.command = raw_command.format(source=self.source, target=self.target)
|
||||
logger.debug(
|
||||
f"Command class initialized with paramaters:\nQuery Type: {self.query_type}\nAFI: \
|
||||
{self.afi}\nSource: {self.source}\nTarget: {self.target}\nConstructed command: \
|
||||
{self.command}"
|
||||
)
|
||||
|
||||
def is_string(self):
|
||||
"""Returns command as single string"""
|
||||
c = self.command
|
||||
return c
|
||||
command_string = self.command
|
||||
logger.debug(f"Constructed command as string: {command_string}")
|
||||
return command_string
|
||||
|
||||
def is_split(self):
|
||||
"""Returns bash command as a list of arguments"""
|
||||
c = self.command
|
||||
cs = c.split(" ")
|
||||
return cs
|
||||
command_split = self.command.split(" ")
|
||||
logger.debug(f"Constructed bash command as list: {command_split}")
|
||||
return command_split
|
||||
|
||||
def vtysh(self):
|
||||
"""
|
||||
Returns bash command as a list of arguments, with the vtysh command itself as a separate
|
||||
list element
|
||||
"""
|
||||
c = self.command
|
||||
v = "vtysh -u -c".split(" ")
|
||||
v.append(c)
|
||||
return v
|
||||
"""Returns bash command as a list of arguments, with the vtysh command itself as a \
|
||||
separate list element"""
|
||||
vtysh_pre = "vtysh -u -c".split(" ")
|
||||
logger.debug(f"vtysh command & argument list: {vtysh_pre}")
|
||||
vtysh_pre.append(self.command)
|
||||
logger.debug(f"vtysh command & argument list with command: {vtysh_pre}")
|
||||
return vtysh_pre
|
||||
|
@ -1,26 +1,40 @@
|
||||
"""
|
||||
Execute the constructed command
|
||||
"""
|
||||
# Module Imports
|
||||
# Standard Imports
|
||||
import logging
|
||||
import subprocess
|
||||
|
||||
# Module Imports
|
||||
import logzero
|
||||
from logzero import logger
|
||||
|
||||
# Project Imports
|
||||
from hyperglass_frr import configuration
|
||||
|
||||
# Logzero Configuration
|
||||
if configuration.debug_state():
|
||||
logzero.loglevel(logging.DEBUG)
|
||||
else:
|
||||
logzero.loglevel(logging.INFO)
|
||||
|
||||
|
||||
def execute(query):
|
||||
"""Gets constructed command string and runs the command via subprocess"""
|
||||
cmd = query.get("cmd")
|
||||
logger.debug(f"Received query: {query}")
|
||||
query_type = query.get("query_type")
|
||||
try:
|
||||
c = configuration.command(query)
|
||||
if cmd in ["bgp_route", "bgp_community", "bgp_aspath"]:
|
||||
output = subprocess.check_output(c.vtysh())
|
||||
return (output, 200)
|
||||
if cmd in ["ping", "traceroute"]:
|
||||
output = subprocess.check_output(c.is_split())
|
||||
return (output, 200)
|
||||
except subprocess.CalledProcessError as e:
|
||||
msg = "Error running query for %s. Error: %s" % (query, e)
|
||||
logger.error(msg)
|
||||
return (msg, 501)
|
||||
command = configuration.Command(query)
|
||||
if query_type in ["bgp_route", "bgp_community", "bgp_aspath"]:
|
||||
logger.debug(f'Running vtysh command "{command}"')
|
||||
output = subprocess.check_output(command.vtysh())
|
||||
status = 200
|
||||
if query_type in ["ping", "traceroute"]:
|
||||
logger.debug(f'Running bash command "{command}"')
|
||||
output = subprocess.check_output(command.is_split())
|
||||
status = 200
|
||||
except subprocess.CalledProcessError as error_exception:
|
||||
output = f"Error running query for {query}."
|
||||
status = 500
|
||||
logger.error(f"Error running query for {query}. Error:\n{error_exception}")
|
||||
return (output, status)
|
||||
|
@ -1,39 +1,63 @@
|
||||
"""API Controller"""
|
||||
# Module Imports
|
||||
"""
|
||||
hyperglass API Controller
|
||||
"""
|
||||
# Standard Imports
|
||||
import json
|
||||
from waitress import serve
|
||||
import logging
|
||||
from pprint import pprint
|
||||
|
||||
# Module Imports
|
||||
import logzero
|
||||
from logzero import logger
|
||||
from waitress import serve
|
||||
from passlib.hash import pbkdf2_sha256
|
||||
from flask import Flask, request, Response, jsonify
|
||||
from flask import Flask, request, Response
|
||||
|
||||
# Project Imports
|
||||
from hyperglass_frr import execute
|
||||
from hyperglass_frr import configuration
|
||||
import execute
|
||||
import configuration
|
||||
|
||||
app = Flask(__name__)
|
||||
# Logzero Configuration
|
||||
if configuration.debug_state():
|
||||
logzero.loglevel(logging.DEBUG)
|
||||
else:
|
||||
logzero.loglevel(logging.INFO)
|
||||
|
||||
# Import API Parameters
|
||||
api = configuration.api()
|
||||
logger.debug(f"API parameters: {api}")
|
||||
|
||||
# Flask Configuration
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
@app.route("/frr", methods=["POST"])
|
||||
def frr():
|
||||
"""
|
||||
Main Flask route ingests JSON parameters and API key hash from hyperglass and passes it to
|
||||
execute module for execution
|
||||
"""
|
||||
"""Main Flask route ingests JSON parameters and API key hash from hyperglass and passes it to \
|
||||
execute module for execution"""
|
||||
headers = request.headers
|
||||
logger.debug(f"Request headers:\n{pprint(headers)}")
|
||||
api_key_hash = headers.get("X-Api-Key")
|
||||
# Verify API key hash against plain text value in configuration.py
|
||||
if pbkdf2_sha256.verify(api["key"], api_key_hash) is True:
|
||||
logger.debug("Verified API Key")
|
||||
|
||||
query_json = request.get_json()
|
||||
query = json.loads(query_json)
|
||||
|
||||
logger.debug(f"Input query data:\n{pprint(query)}")
|
||||
logger.debug("Executing query...")
|
||||
|
||||
frr_response = execute.execute(query)
|
||||
|
||||
logger.debug(f"Raw output:\n{frr_response}")
|
||||
|
||||
return Response(frr_response[0], frr_response[1])
|
||||
msg = "Validation of API key failed. Hash: %s" % api_key_hash
|
||||
logger.error(msg)
|
||||
return jsonify({"message": "Error: Unauthorized"}), 401
|
||||
logger.error(f"Validation of API key failed. Hash:\n{api_key_hash}")
|
||||
return Response("Error: API Key Invalid", 401)
|
||||
|
||||
|
||||
# Simple Waitress WSGI implementation
|
||||
if __name__ == "__main__":
|
||||
logger.debug("Starting hyperglass-frr API via Waitress...")
|
||||
serve(app, host=api["listen_addr"], port=api["port"])
|
||||
|
12
manage.py
12
manage.py
@ -5,17 +5,21 @@ import string
|
||||
from logzero import logger
|
||||
from passlib.hash import pbkdf2_sha256
|
||||
|
||||
from hyperglass_frr import hyperglass_frr
|
||||
|
||||
|
||||
@click.group()
|
||||
def main():
|
||||
pass
|
||||
|
||||
|
||||
@main.command()
|
||||
def testserver():
|
||||
@main.command("dev-server", help="Start Flask development server")
|
||||
@click.option("-h", "--host", type=str, default="0.0.0.0", help="Listening IP")
|
||||
@click.option("-p", "--port", type=int, default=5000, help="TCP Port")
|
||||
def dev_server(host, port):
|
||||
try:
|
||||
from hyperglass_frr import hyperglass_frr
|
||||
from hyperglass_frr import configuration
|
||||
|
||||
debug_state = configuration.debug_state()
|
||||
hyperglass_frr.app.run(host="0.0.0.0", debug=True, port=8080)
|
||||
logger.error("Started test server.")
|
||||
except:
|
||||
|
Reference in New Issue
Block a user