diff --git a/Dockerfile b/Dockerfile index 8e58b2a..20d8ef6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,19 @@ FROM python:3.10 +# Get supervisor and cron +# Do not get picky on exact cron & supervisor versions +# hadolint ignore=DL3008 +RUN apt-get update \ + && apt-get install --no-install-recommends -y supervisor cron \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* \ + && mkdir -p /var/log/supervisor \ + && rm /etc/cron.daily/* + +COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf + +COPY cron-update-endpoints /etc/cron.d/endpoints + COPY requirements.txt /fedcloud-dashboard/requirements.txt RUN pip install --no-cache-dir --upgrade -r /fedcloud-dashboard/requirements.txt @@ -10,4 +24,4 @@ WORKDIR /fedcloud-dashboard EXPOSE 8000 -CMD ["gunicorn", "--bind", "0.0.0.0:8000", "dashboard.main:app"] +CMD ["/usr/bin/supervisord"] diff --git a/cron-update-endpoints b/cron-update-endpoints new file mode 100644 index 0000000..4899260 --- /dev/null +++ b/cron-update-endpoints @@ -0,0 +1 @@ +*/15 * * * * root /usr/local/bin/python /fedcloud-dashboard/dashboard/dashy_endpoints.py > /app/public/conf.yml diff --git a/dashboard/dashy_endpoints.py b/dashboard/dashy_endpoints.py new file mode 100644 index 0000000..07bc891 --- /dev/null +++ b/dashboard/dashy_endpoints.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python + +""" +This code has been copied from https://github.com/tdviet/fedcloudclient +""" + +from urllib import parse + +import defusedxml.ElementTree as ElementTree +import requests +import yaml + +DEFAULT_ICON = ( + "https://github.com/openstack/openstackdocstheme/blob/master" + "/openstackdocstheme/theme/openstackdocs/static/favicon.ico?raw=true" +) +GOCDB_PUBLICURL = "https://goc.egi.eu/gocdbpi/public/" +DOCS_URL = "https://docs.egi.eu/users/compute/cloud-compute/openstack/" +TIMEOUT = 10 + + +def get_sites(): + """ + Get list of sites (using GOCDB instead of site configuration) + :return: list of site IDs + """ + q = {"method": "get_site_list", "certification_status": "Certified"} + url = "?".join([GOCDB_PUBLICURL, parse.urlencode(q)]) + r = requests.get(url) + sites = {} + if r.status_code == 200: + root = ElementTree.fromstring(r.text) + for s in root: + name = s.attrib.get("NAME") + country = s.attrib.get("COUNTRY") + country_code = s.attrib.get("COUNTRY_CODE") + sites[name] = {"country": country, "country_code": country_code} + else: + print("Something went wrong...") + print(r.status_code) + print(r.text) + return sites + + +def find_endpoints(service_type, production=True, monitored=True): + """ + Searching GOCDB for endpoints according to service types and status + :param service_type: + :param production: + :param monitored: + :param site: list of sites, None for searching all sites + :return: list of endpoints + """ + q = {"method": "get_service_endpoint", "service_type": service_type} + if monitored: + q["monitored"] = "Y" + sites = get_sites() + url = "?".join([GOCDB_PUBLICURL, parse.urlencode(q)]) + r = requests.get(url) + endpoints = [] + if r.status_code == 200: + root = ElementTree.fromstring(r.text) + for sp in root: + if production: + prod = sp.find("IN_PRODUCTION").text.upper() + if prod != "Y": + continue + os_url = sp.find("URL").text + ep_site = sp.find("SITENAME").text + if ep_site not in sites: + continue + # os_url = urlparse.urlparse(sp.find('URL').text) + # sites[sp.find('SITENAME').text] = urlparse.urlunparse( + # (os_url[0], os_url[1], os_url[2], '', '', '')) + endpoints.append( + [ + ep_site, + service_type, + os_url, + sites[ep_site]["country"], + sites[ep_site]["country_code"], + ] + ) + else: + print("Something went wrong...") + print(r.status_code) + print(r.text) + return endpoints + + +def main(): + dashy_conf = { + "pageInfo": { + "title": "EGI Cloud Compute", + "description": "FedCloud providers dashboards", + "navLinks": [ + { + "title": "Documentation", + "path": DOCS_URL, + } + ], + "logo": "https://egi-api.nois3.net/app/uploads/2021/11/egi-logo.svg", + }, + "appConfig": { + "theme": "material", + "layout": "horizontal", + "iconSize": "medium", + "language": "en", + "disableConfiguration": True, + }, + "sections": [ + {"name": "OpenStack Dashboards", "icon": "fas fa-clouds", "items": []} + ], + "displayData": { + "sortBy": "alphabetical", + "rows": 1, + "cols": 1, + "collapsed": False, + "hideForGuests": False, + }, + } + endpoints = find_endpoints("org.openstack.horizon") + items = dashy_conf["sections"][0]["items"] + for s in endpoints: + items.append( + { + "title": s[0], + "description": "%s (%s)" % (s[3], s[4]), + "icon": DEFAULT_ICON, + "url": s[2], + "target": "newtab", + } + ) + print(yaml.dump(dashy_conf)) + + +if __name__ == "__main__": + main() diff --git a/docker-compose.yaml b/docker-compose.yaml index ded8d96..cc6caa4 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -29,9 +29,19 @@ services: - "/var/run/docker.sock:/var/run/docker.sock:ro" dashboard: - build: . + image: "lissy93/dashy:latest" labels: - "traefik.enable=true" - "traefik.http.routers.dashboard.rule=Host(`${DASHBOARD_HOSTNAME}`)" - "traefik.http.routers.dashboard.entrypoints=websecure" - "traefik.http.routers.dashboard.tls.certresolver=myresolver" + volumes: + - "config:/app/public" + + endpoint_updater: + build: . + volumes: + - "config:/app/public" + +volumes: + config: diff --git a/supervisord.conf b/supervisord.conf new file mode 100644 index 0000000..453fe9f --- /dev/null +++ b/supervisord.conf @@ -0,0 +1,9 @@ +[supervisord] +nodaemon=true + +[program:gunicorn] +directory=/fedcloud-dashboard +command=gunicorn --bind 0.0.0.0:8000 dashboard.main:app + +[program:cron] +command=cron -f