proxy_manager.py 3.35 KB
Newer Older
1 2 3 4 5 6 7 8
from os import system
from urllib.parse import urlparse
import re
from datetime import datetime
import logging

from jinja2 import Template

9
from common.configuration import zoeconf
10 11
from common.state import AlchemySession, Proxy

12 13
log = logging.getLogger(__name__)

14 15 16 17 18 19 20 21 22 23
LOOP_INTERVAL = 1  # seconds
ACCESS_TIME_REFRESH_INTERVAL = 60  # seconds

ENTRY_TEMPLATE = """
# Zoe proxy entry for service {{ service_name }}
<Location /proxy/{{ proxy_id }}>
    ProxyHtmlEnable On
    ProxyHTMLExtended On
    ProxyPass {{ proxy_url }} retry=1
    ProxyPassReverse {{ proxy_url }}
24
    {% if service_name != "Spark Notebook interface" %}
25 26 27 28 29 30 31 32
    ProxyHTMLURLMap ^/(.*)$ /proxy/{{ proxy_id }}/$1 RL
    ProxyHTMLURLMap ^logPage(.*)$ /proxy/{{ proxy_id }}/logPage$1 RL
    ProxyHTMLURLMap ^app(.*)$ /proxy/{{ proxy_id }}/app$1 RL
    {% for node in nodes %}
    ProxyHTMLURLMap ^http://{{ node[0] }}(.*)$ /proxy/{{node[1]}}$1 RL
    {% endfor %}
    {% endif %}
</Location>
33
{% if service_name == "Spark Notebook interface" %}
34 35 36 37 38 39 40 41 42
<Location /proxy/{{ proxy_id }}/ws/>
    ProxyPass ws://{{ netloc }}/proxy/{{ proxy_id }}/ws/
</Location>
{% endif %}
"""


class ProxyManager:
    def __init__(self):
43 44
        self.apache_conf_filepath = zoeconf.apache_proxy_config_file
        self.apache_access_log = zoeconf.apache_log_file
45 46 47 48 49 50 51 52 53 54

    def _get_proxy_entries(self):
        state = AlchemySession()
        return state.query(Proxy).all()

    def _generate_file(self, proxy_entries):
        output = ""
        jinja_template = Template(ENTRY_TEMPLATE)
        node_list = []
        for p in proxy_entries:
55 56
            netloc = urlparse(p.internal_url)[1]
            node_list.append((netloc, p.id))
57
        for p in proxy_entries:
58
            netloc = urlparse(p.internal_url)[1]
59
            jinja_dict = {
60 61 62
                "proxy_id": p.id,
                "proxy_url": p.internal_url,
                "service_name": p.service_name,
63 64 65 66 67 68 69 70
                "netloc": netloc,
                "nodes": node_list
            }
            apache_entry = jinja_template.render(jinja_dict)
            output += apache_entry + "\n"
        return output

    def _commit_and_reload(self, generated_file):
71
        open(self.apache_conf_filepath, "w").write(generated_file)
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
        system("sudo service apache2 reload")
        log.info("Apache reloaded")

    def update_proxy(self):
        entries = self._get_proxy_entries()
        output = self._generate_file(entries)
        self._commit_and_reload(output)

    def update_proxy_access_timestamps(self):
        regex = re.compile('[0-9.]+ - - \[(.*)\] "GET /proxy/([0-9a-z\-]+)/')
        logf = open(self.apache_access_log, 'r')
        last_accesses = {}
        for line in logf:
            match = re.match(regex, line)
            if match is not None:
                proxy_id = match.group(2)
                timestamp = datetime.strptime(match.group(1), "%d/%b/%Y:%H:%M:%S %z")
                last_accesses[proxy_id] = timestamp

        state = AlchemySession()
92
        for proxy in state.query(Proxy).all():
93 94 95 96
            if proxy.id in last_accesses:
                log.debug("Updating access timestamp for proxy ID {}".format(proxy.id))
                proxy = state.query(Proxy).filter_by(id=proxy.id).one()
                proxy.last_access = last_accesses[proxy.id]
97
                proxy.container.cluster.execution.termination_notice = False
98 99 100
                state.commit()

pm = ProxyManager()