Commit 3444015e authored by Nick Yefremov's avatar Nick Yefremov

Updated redirector files, separated classes, updated setup

parent 2410da06
#!/usr/bin/env python
# -*- coding: utf-8 -*-
NAME = "redirector"
REDIRECTOR_DIR = "/var/lib/redirector" REDIRECTOR_DIR = "/var/lib/redirector"
MAPS_DIR = REDIRECTOR_DIR + "/maps" MAPS_DIR = REDIRECTOR_DIR + "/maps"
CONFIG_DIR = REDIRECTOR_DIR + "/location-includes" CONFIG_DIR = REDIRECTOR_DIR + "/location-includes"
\ No newline at end of file
...@@ -2,9 +2,9 @@ ...@@ -2,9 +2,9 @@
class MapGenerator: class MapGenerator:
def __init__(self): def __init__(self):
self.start_lines = [ self.start_lines = [
"map $uri $%s-redirect {\n", "map $uri $%s-redirect {",
"\nmap $uri $%s-option-redirect {\n", "\nmap $uri $%s-option-redirect {",
"\nmap $uri $%s-option {\n" "\nmap $uri $%s-option {"
] ]
self.endline = "\n}" self.endline = "\n}"
...@@ -13,17 +13,18 @@ class MapGenerator: ...@@ -13,17 +13,18 @@ class MapGenerator:
for i, map_type in enumerate(self.start_lines): for i, map_type in enumerate(self.start_lines):
data += map_type % project_name data += map_type % project_name
for arg1, arg2 in redirects_sorted[i]: for arg1, arg2 in redirects_sorted[i]:
data += "\t%s\t%s;" % (arg1, arg2) data += "\n\t%s\t%s;" % (arg1, arg2)
data += self.endline data += self.endline
return data return data
class ConfigGenerator: class ConfigGenerator:
def __init__(self): def __init__(self):
self.start_line = "if($%s-redirect) {\n" self.start_line = "if($%s-redirect) {\n"
self.rewrite_line = "\trewrite ^/%s/(.^)$ %s redirect;\n}" self.rewrite_line = "\trewrite ^/%s/(.^)$ $%s redirect;\n}"
def generate_conf(self, project_name): def generate_conf(self, project_name):
return self.start_line % project_name + self.rewrite_line % project_name return (self.start_line % project_name) + (self.rewrite_line % (project_name, project_name))
import logging
class Logger:
def __init__(self, file_=None):
if file_:
logging.basicConfig(filename=file_)
def log(self, message, level="critical"):
if self.file_:
if level == "critical":
logging.critical(message)
elif level == "debug":
logging.debug(message)
elif level == "info":
logging.info(message)
else:
print(level.upper() + ":\n" + message)
import sys
import yaml import yaml
from pprint import pprint as pp
import re import re
# Constants: class MapLineParser:
class RedirectorParser:
def __init__(self, logger=None): def __init__(self, logger=None):
self.url_regexp = re.compile(r'(\/[(\w)-:]+)+\/?(\.\w+)?') self.url_regexp = re.compile(r'(\/[(\w)-:]+)+\/?(\.\w+)?')
self.url_absolute = re.compile(r"^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$") self.url_absolute = re.compile(
r"^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$")
self.logger = logger self.logger = logger
def _parse_line(self, line, prefix, i): def parse_line(self, line, prefix, i):
if not line.startswith("#"): line = line.strip()
line = line.split()
return_code, options = 0, [] return_code, options = 0, []
if len(line) == 0:
if line.startswith("#") or len(line) == 0:
return -1, None, None return -1, None, None
elif len(line) < 2:
raise self.ParseLineError("Error on %s:{line};\nNot enough arguments to parse!".format(line=i), None) line = line.split()
if len(line) < 2:
raise self.ParseLineError("Error on %s:{line};\n"
"Not enough arguments to parse!".format(line=i), None)
elif len(line) > 2: elif len(line) > 2:
return_code = 1 return_code = 1
options = [option[1:-1] for option in line[2:]] options = [option[1:-1] for option in line[2:]]
# not url - regexp
if not self.url_regexp.fullmatch(line[0]): if not self.url_regexp.fullmatch(line[0]): # not url - regexp
try: try:
re.compile(line[0]) re.compile(line[0])
re.compile(line[1]) re.compile(line[1])
...@@ -33,23 +33,54 @@ class RedirectorParser: ...@@ -33,23 +33,54 @@ class RedirectorParser:
raise self.RegexpTestError("Can\'t compile regular expressions {expression1} {expression2} in " raise self.RegexpTestError("Can\'t compile regular expressions {expression1} {expression2} in "
"%s:{line_num}".format(expression1=line[0], expression2=line[1], "%s:{line_num}".format(expression1=line[0], expression2=line[1],
line_num=i), re.error) line_num=i), re.error)
# if new URI relative to the root
elif line[0].startswith("//"): elif line[0].startswith("//"): # if new URI relative to the root
line[0] = line[0][1:] # cutting out extra '/' at the beginning line[0] = line[0][1:] # cutting out extra '/' at the beginning
elif line[1].startswith("//"): elif line[1].startswith("//"):
line[1] = line[1][1:] line[1] = line[1][1:]
elif self.url_absolute.fullmatch(line[1]): elif self.url_absolute.fullmatch(line[1]):
pass pass
# default url
else: else: # default url
line[0] = prefix + line[0] line[0] = prefix + line[0]
line[1] = prefix + line[1] line[1] = prefix + line[1]
return return_code, line[0], line[1], options return return_code, line[0], line[1], options
else:
return -1, None, None class ParseLineError(Exception):
"""Raised when line contains not enough arguments
Attributes:
message -- expanation where in .yaml config file aprser didn't find enough arguments
"""
def __init__(self, message, errors):
super().__init__(message)
self.message = message
self.errors = errors
class RegexpTestError(Exception):
"""Raised when parser fails to compile url containing regular expression
Attributes:
message -- explanation what regexp failed to compile and where
"""
def __init__(self, message, errors):
super().__init__(message)
self.message = message
self.errors = errors
class ConfigReader:
def __init__(self, logger=None):
self.line_parser = MapLineParser(logger=logger)
if logger:
self.logger = logger
@staticmethod @staticmethod
def _parse_yaml(file_dir): def parse_yaml(file_dir):
return_list = [] return_list = []
with open(file_dir, 'r') as stream: with open(file_dir, 'r') as stream:
data = yaml.safe_load(stream) data = yaml.safe_load(stream)
...@@ -59,34 +90,23 @@ class RedirectorParser: ...@@ -59,34 +90,23 @@ class RedirectorParser:
return_list.append((map_path, project_prefix)) return_list.append((map_path, project_prefix))
return return_list return return_list
def log(self, message, level="critical"):
if self.logger:
if level == "critical":
self.logger.critical(message)
elif level == "debug":
self.logger.debug(message)
elif level == "info":
self.logger.info(message)
else:
print(level.upper() + ":\n" + message)
def parse_map(self, map_file, yaml_file): def parse_map(self, map_file, yaml_file):
res = [[], [], []] res = [[], [], []]
res_prefix = None res_prefix = None
try: try:
for map_path, prefix in self._parse_yaml(yaml_file): for map_path, prefix in self.parse_yaml(yaml_file):
if map_path == map_file: if map_path == map_file:
# ???: Is the last directory of map_path a project's name? res_prefix = prefix.split("/")[-1] # ???: Is the last directory of map_path a project's name?
res_prefix = prefix.split("/")[-1]
with open(map_file, "r") as file: with open(map_file, "r") as file:
for i, line in enumerate(file): for i, line in enumerate(file):
request_url, redirect_url, options = None, None, [] request_url, redirect_url, options = None, None, []
try: try:
return_code, request_url, redirect_url, *options = self._parse_line(line, prefix, i) return_code, request_url, redirect_url, *options = \
except self.RegexpTestError as e: self.line_parser.parse_line(line, prefix, i)
self.log(e.message % map_file) except MapLineParser.RegexpTestError as e:
except self.ParseLineError as e: self.logger.log(e.message % map_file)
self.log(e.message % map_file) except MapLineParser.ParseLineError as e:
self.logger.log(e.message % map_file)
if not request_url or not redirect_url or return_code == -1: if not request_url or not redirect_url or return_code == -1:
continue continue
...@@ -98,29 +118,5 @@ class RedirectorParser: ...@@ -98,29 +118,5 @@ class RedirectorParser:
res[2].append((request_url, options)) res[2].append((request_url, options))
return res, res_prefix return res, res_prefix
except yaml.YAMLError as e: except yaml.YAMLError as e:
self.log("Error occured while reading %s" % yaml_file + str(e)) self.logger.log("Error occurred while reading %s" % yaml_file + str(e))
class ParseLineError(Exception):
"""Raised when line contains not enough arguments
Attributes:
message -- expanation where in .yaml config file aprser didn't find enough arguments
"""
def __init__(self, message, errors):
super().__init__(message)
self.message = message
self.errors = errors
class RegexpTestError(Exception):
"""Raised when parser fails to compile url containing regular expression
Attributes:
message -- explanation what regexp failed to compile and where
"""
def __init__(self, message, errors):
super().__init__(message)
self.message = message
self.errors = errors
import sys import sys
import generators from core import generators
import parser from core import logger
import const from core import parser
from core import const
class Redirector: class Redirector:
def __init__(self, logger=None): def __init__(self, logger=None):
self.parser = parser.RedirectorParser(logger=logger) self.parser = parser.ConfigReader(logger=logger)
self.map_generator = generators.MapGenerator() self.map_generator = generators.MapGenerator()
self.config_generator = generators.ConfigGenerator() self.config_generator = generators.ConfigGenerator()
def log(self, message, level="critical"): def generate(self, yaml_file, map_file):
if self.logger: import pdb
if level == "critical": pdb.set_trace()
self.logger.critical(message)
elif level == "debug":
self.logger.debug(message)
elif level == "info":
self.logger.info(message)
else:
print(level.upper() + ":\n" + message)
def generate(self, map_file, yaml_file):
project_name = "Error" project_name = "Error"
try: try:
data, project_name = self.parser.parse_map(map_file, yaml_file) data, project_name = self.parser.parse_map(map_file, yaml_file)
...@@ -35,9 +28,9 @@ class Redirector: ...@@ -35,9 +28,9 @@ class Redirector:
with open(const.CONFIG_DIR + "/%s.conf" % project_name, "w") as conf_file: with open(const.CONFIG_DIR + "/%s.conf" % project_name, "w") as conf_file:
conf_file.write(self.config_generator.generate_conf(project_name)) conf_file.write(self.config_generator.generate_conf(project_name))
except Exception as e: except Exception as e:
print(e)
raise self.RedirectorGenerationError("Can\'t generate new .conf or new .map file \ raise self.RedirectorGenerationError("Can\'t generate new .conf or new .map file \
from %s .map file for %s project" % (map_file, project_name). e) from %s .map file for %s project" % (map_file, project_name), e)
class RedirectorParserError(Exception): class RedirectorParserError(Exception):
def __init__(self, message, errors): def __init__(self, message, errors):
......
...@@ -5,10 +5,10 @@ setup( ...@@ -5,10 +5,10 @@ setup(
name=const.NAME, name=const.NAME,
version="0.1", version="0.1",
packages=find_packages(), packages=find_packages(),
entry_points=""" entry_points={
[redirector] 'console_scripts': [
redirector = core.redirector:main 'redirector = core.redirector:main',
[redirector-watch] 'redirector-watch = core.redirector:watch'
redirector-watch = core.redirector:watch ]
""" }
) )
\ 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