diff --git a/stack/monitor2/buildout.cfg b/stack/monitor2/buildout.cfg index 2aa0c887a7ccd8137278ac652929534afbab4dcc..c2ed97e02db877fae944f81a0bbd9e615a451187 100644 --- a/stack/monitor2/buildout.cfg +++ b/stack/monitor2/buildout.cfg @@ -106,7 +106,7 @@ recipe = slapos.recipe.template:jinja2 filename = template-monitor.cfg template = ${:_profile_base_location_}/instance-monitor.cfg.jinja2.in rendered = ${buildout:directory}/template-monitor.cfg -md5sum = c3438660ae33d0049fceaf45ddb781bf +md5sum = e6244ecdae7fa8f5b5b6b7f94d30be8b context = key apache_location apache:location key gzip_location gzip:location @@ -133,6 +133,7 @@ context = raw python_executable ${buildout:executable} raw promise_executor_py ${run-promise-py:location}/${run-promise-py:filename} raw template_wrapper ${template-wrapper:output} + raw status2rss_executable_path ${status2rss-executable:location}/${status2rss-executable:filename} [monitor-httpd-conf] <= monitor-download-base @@ -219,14 +220,10 @@ filename = monitor-password.py.cgi filename = monitor-password-interface.html md5sum = 04b664dfb47bfd3d01502768311aa239 -[rss-bin] -recipe = hexagonit.recipe.download -url = ${:_profile_base_location_}/${:filename} -download-only = true -md5sum = 6c84a826778cb059754623f39b33651b -destination = ${buildout:parts-directory}/monitor-template-rss-bin +[status2rss-executable] +<= monitor-download-base filename = status2rss.py -mode = 0644 +md5sum = 65315ded80cd72f54f6e12d06ce813c4 [dcron-service] recipe = slapos.recipe.template diff --git a/stack/monitor2/instance-monitor.cfg.jinja2.in b/stack/monitor2/instance-monitor.cfg.jinja2.in index c1b7b56d8dcb23ad8bc6c1af12490a69f2ea1c30..6f906c56b5aa610b2624769001e37c0dde8e8850 100644 --- a/stack/monitor2/instance-monitor.cfg.jinja2.in +++ b/stack/monitor2/instance-monitor.cfg.jinja2.in @@ -69,6 +69,7 @@ promise-wrapper = ${directory:var}/monitor-promise-wrapper monitor-var = ${directory:var}/monitor monitor-password-var = ${monitor-directory:monitor-var}/password monitor-password-interface = ${monitor-directory:monitor-password-var}/password/interface +monitor-status2rss-var = ${monitor-directory:monitor-var}/status2rss [logrotate-directory] recipe = slapos.cookbook:mkdirectory @@ -182,6 +183,18 @@ context = key content :command command = kill -USR1 $(cat ${monitor-httpd-conf-parameter:pid-file}) +[monitor-status2rss-wrapper] +recipe = slapos.cookbook:wrapper +command-line = {{ python_executable }} {{ status2rss_executable_path }} '${monitor-instance-parameter:monitor-title}' '${monitor-httpd-conf-parameter:url}feed' ${monitor-directory:public} ${monitor-directory:monitor-status2rss-var}/previous_status ${monitor-directory:web-dir}/feed +wrapper-path = ${directory:bin}/monitor-status2rss.py + +[monitor-status2rss-cron-entry] +recipe = slapos.cookbook:cron.d +cron-entries = ${cron:cron-entries} +name = monitor-status2rss +frequency = * * * * * +command = ${monitor-status2rss-wrapper:wrapper-path} + [monitor-web-default-promise-interface] recipe = slapos.recipe.template:jinja2 template = {{ monitor_web_default_promise_interface }} @@ -342,4 +355,5 @@ parts = monitor-password-promise-conf monitor-password-cgi monitor-password-promise-interface + monitor-status2rss-cron-entry publish diff --git a/stack/monitor2/status2rss.py b/stack/monitor2/status2rss.py index 26ed8f1875b6ac7a7dc8f0794b615b231db8e86b..dd016215a9f6e3b76dde06861c9e9352cac157ce 100644 --- a/stack/monitor2/status2rss.py +++ b/stack/monitor2/status2rss.py @@ -1,59 +1,107 @@ -import datetime -import PyRSS2Gen import sys -import sqlite3 -import time +import os +import json +import datetime import base64 +import hashlib -# Based on http://thehelpfulhacker.net/2011/03/27/a-rss-feed-for-your-crontabs/ - -# ### Defaults -TITLE = sys.argv[1] -LINK = sys.argv[2] -db_path = sys.argv[3] -DESCRIPTION = TITLE -SUCCESS = "SUCCESS" -FAILURE = "FAILURE" - -items = [] -status = "" - -current_timestamp = int(time.time()) -# We only build the RSS for the last ten days -period = 3600 * 24 * 10 -db = sqlite3.connect(db_path) -rows = db.execute("select timestamp, status from status where timestamp>? order by timestamp", (current_timestamp - period,)) -for row in rows: - line_timestamp, line_status = row - line_status = line_status.encode() +def main(): + _, title, link, public_folder, previous_status_path, output_path = sys.argv + final_status = "OK"; + # getting status + for filename in os.listdir(public_folder): + if filename.endswith(".status.json"): + filepath = os.path.join(public_folder, filename) + status = None + try: + status = json.load(open(filepath, "r")) + except ValueError: + continue + try: + if status["status"] != "OK": + final_status = "BAD" + break + except KeyError: + final_status = "BAD" + break + # checking previous status + try: + status = open(previous_status_path, "r").readline(4) + if status == final_status: + return 0 + except IOError: + pass + # update status + open(previous_status_path, "w").write(final_status) + # generating RSS + utcnow = datetime.datetime.utcnow() + open(output_path, "w").write( + newRssString( + title, + title, + link, + utcnow, + utcnow, + "60", + [ + newRssItemString( + "Status is %s" % final_status, + "Status is %s" % final_status, + link, + newGuid("%s, %s" % (utcnow, final_status)), + utcnow, + ) + ], + ) + ) - if line_status == status: - continue - status = line_status +def escapeHtml(string): + return string.replace("&", "&").replace("<", "<").replace(">", ">").replace("\"", """) - event_time = datetime.datetime.fromtimestamp(line_timestamp).strftime('%Y-%m-%d %H:%M:%S') +def newGuid(string): + sha256 = hashlib.sha256() + sha256.update(string) + return sha256.hexdigest() - individual_rows = db.execute("select status, element, output from individual_status where timestamp=?", (line_timestamp,)) - description = '\n'.join(['%s: %s %s' % row for row in individual_rows]) +def newRssItemString(title, description, link, guid, pub_date, guid_is_perma_link=True): + return """<item> + <title>%(title)s</title> + <description>%(description)s</description> + <link>%(link)s</link> + <guid isPermaLink="%(guid_is_perma_link)s">%(guid)s</guid> + <pubDate>%(pub_date)s</pubDate> +</item>""" % { + "title": escapeHtml(title), + "description": escapeHtml(description), + "link": escapeHtml(link), + "guid": escapeHtml(guid), + "pub_date": escapeHtml(pub_date.strftime("%a, %d %b %Y %H:%M:%S +0000")), + "guid_is_perma_link": escapeHtml(repr(guid_is_perma_link).lower()), + } - rss_item = PyRSS2Gen.RSSItem( - title = status, - description = "%s: %s\n%s" % (event_time, status, description), - link = LINK, - pubDate = event_time, - guid = PyRSS2Gen.Guid(base64.b64encode("%s, %s" % (event_time, status))) - ) - items.append(rss_item) - -### Build the rss feed -items.reverse() -rss_feed = PyRSS2Gen.RSS2 ( - title = TITLE, - link = LINK, - description = DESCRIPTION, - lastBuildDate = datetime.datetime.utcnow(), - items = items - ) +def newRssString(title, description, link, last_build_date, pub_date, ttl, rss_item_string_list): + return """<?xml version="1.0" encoding="UTF-8" ?> +<rss version="2.0"> +<channel> + <title>%(title)s</title> + <description>%(description)s</description> + <link>%(link)s</link> + <lastBuildDate>%(last_build_date)s</lastBuildDate> + <pubDate>%(pub_date)s</pubDate> + <ttl>%(ttl)s</ttl> +%(items)s +</channel> +</rss> +""" % { + "title": escapeHtml(title), + "description": escapeHtml(description), + "link": escapeHtml(link), + "last_build_date": escapeHtml(last_build_date.strftime("%a, %d %b %Y %H:%M:%S +0000")), + "pub_date": escapeHtml(pub_date.strftime("%a, %d %b %Y %H:%M:%S +0000")), + "ttl": escapeHtml(str(ttl)), + "items": "\n\n".join([" " + item.replace("\n", "\n ") for item in rss_item_string_list]), + } -print rss_feed.to_xml() +if __name__ == "__main__": + exit(main())