1
0
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:
checktheroads
2019-06-11 15:02:53 -07:00
parent 28b12be8b1
commit 6ea90f428a
5 changed files with 157 additions and 117 deletions

View File

@ -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]

View File

@ -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

View File

@ -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)

View File

@ -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"])

View File

@ -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: