blob: b81ec9c4ea3d3a421185885d45992c7c3349ffea [file] [log] [blame]
Anita Kunobf3a97b2013-02-28 10:09:46 -05001#!/usr/bin/env python
2# Copyright (c) 2013 Chmouel Boudjnah, eNovance
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
15
16# This script is designed to generate rss feeds for subscription from updates
17# to various gerrit tracked projects. It is intended to be run periodically,
Anita Kuno96a159d2013-03-12 14:14:38 -040018# for example hourly via cron. It takes an optional argument to specify the
19# path to a configuration file.
Anita Kunobf3a97b2013-02-28 10:09:46 -050020# -*- encoding: utf-8 -*-
Sergey Lukjanovdb8e7072013-07-22 12:13:35 +040021
22from __future__ import print_function
23
Anita Kunobf3a97b2013-02-28 10:09:46 -050024__author__ = "Chmouel Boudjnah <chmouel@chmouel.com>"
Monty Taylor061919f2013-06-02 11:35:42 -040025
26import ConfigParser
27import cStringIO
Anita Kunobf3a97b2013-02-28 10:09:46 -050028import datetime
Monty Taylor061919f2013-06-02 11:35:42 -040029import json
Anita Kunobf3a97b2013-02-28 10:09:46 -050030import os
31import sys
32import time
Anita Kunobf3a97b2013-02-28 10:09:46 -050033
34import PyRSS2Gen
Christian Berendt589ade62014-10-11 14:01:45 +020035import six.moves.urllib.request as urlrequest
Anita Kunobf3a97b2013-02-28 10:09:46 -050036
37PROJECTS = ['openstack/nova', 'openstack/keystone', 'opensack/swift']
38JSON_URL = 'https://review.openstack.org/query'
39DEBUG = False
40OUTPUT_MODE = 'multiple'
41
42curdir = os.path.dirname(os.path.realpath(sys.argv[0]))
43
44
45class ConfigurationError(Exception):
46 pass
47
48
49def get_config(config, section, option, default=None):
50 if not config.has_section(section):
51 raise ConfigurationError("Invalid configuration, missing section: %s" %
52 section)
53 if config.has_option(section, option):
54 return config.get(section, option)
55 elif not default is None:
56 return default
57 else:
58 raise ConfigurationError("Invalid configuration, missing "
59 "section/option: %s/%s" % (section, option))
60
61
62def parse_ini(inifile):
63 ret = {}
64 if not os.path.exists(inifile):
65 return
66 config = ConfigParser.RawConfigParser(allow_no_value=True)
67 config.read(inifile)
68
69 if config.has_section('swift'):
70 ret['swift'] = dict(config.items('swift'))
71
72 ret['projects'] = get_config(config, 'general', 'projects', PROJECTS)
73 if type(ret['projects']) is not list:
74 ret['projects'] = [x.strip() for x in ret['projects'].split(',')]
75 ret['json_url'] = get_config(config, 'general', 'json_url', JSON_URL)
76 ret['debug'] = get_config(config, 'general', 'debug', DEBUG)
77 ret['output_mode'] = get_config(config, 'general', 'output_mode',
78 OUTPUT_MODE)
79 return ret
80
Anita Kuno96a159d2013-03-12 14:14:38 -040081try:
82 conffile = sys.argv[1]
83except IndexError:
84 conffile = os.path.join(curdir, '..', 'config', 'openstackwatch.ini')
85CONFIG = parse_ini(conffile)
Anita Kunobf3a97b2013-02-28 10:09:46 -050086
87
88def debug(msg):
89 if DEBUG:
Sergey Lukjanovdb8e7072013-07-22 12:13:35 +040090 print(msg)
Anita Kunobf3a97b2013-02-28 10:09:46 -050091
92
93def get_javascript(project=None):
Anita Kunoc8cc7a52013-04-10 21:32:08 -040094 url = CONFIG['json_url']
Anita Kunobf3a97b2013-02-28 10:09:46 -050095 if project:
96 url += "+project:" + project
Christian Berendt589ade62014-10-11 14:01:45 +020097 fp = urlrequest.urlretrieve(url)
Anita Kunobf3a97b2013-02-28 10:09:46 -050098 ret = open(fp[0]).read()
99 return ret
100
101
102def parse_javascript(javascript):
103 for row in javascript.splitlines():
104 try:
105 json_row = json.loads(row)
106 except(ValueError):
107 continue
108 if not json_row or not 'project' in json_row or \
109 json_row['project'] not in CONFIG['projects']:
110 continue
111 yield json_row
112
113
114def upload_rss(xml, output_object):
Monty Taylor061919f2013-06-02 11:35:42 -0400115 if 'swift' not in CONFIG:
Sergey Lukjanovdb8e7072013-07-22 12:13:35 +0400116 print(xml)
Anita Kunobf3a97b2013-02-28 10:09:46 -0500117 return
118
119 import swiftclient
120 cfg = CONFIG['swift']
121 client = swiftclient.Connection(cfg['auth_url'],
122 cfg['username'],
123 cfg['password'],
124 auth_version=cfg.get('auth_version',
125 '2.0'))
126 try:
127 client.get_container(cfg['container'])
128 except(swiftclient.client.ClientException):
129 client.put_container(cfg['container'])
130 # eventual consistenties
131 time.sleep(1)
132
133 client.put_object(cfg['container'], output_object,
134 cStringIO.StringIO(xml))
135
136
137def generate_rss(javascript, project=""):
138 title = "OpenStack %s watch RSS feed" % (project)
139 rss = PyRSS2Gen.RSS2(
140 title=title,
141 link="http://github.com/chmouel/openstackwatch.rss",
tanlinfa936da2014-02-13 15:41:41 +0800142 description="The latest reviews about OpenStack, straight "
Anita Kunobf3a97b2013-02-28 10:09:46 -0500143 "from Gerrit.",
144 lastBuildDate=datetime.datetime.now()
145 )
146 for row in parse_javascript(javascript):
147 author = row['owner']['name']
148 author += " <%s>" % ('email' in row['owner'] and
149 row['owner']['email']
150 or row['owner']['username'])
151 rss.items.append(
152 PyRSS2Gen.RSSItem(
153 title="%s [%s]: %s" % (os.path.basename(row['project']),
154 row['status'],
155 row['subject']),
156 author=author,
157 link=row['url'],
158 guid=PyRSS2Gen.Guid(row['id']),
159 description=row['subject'],
160 pubDate=datetime.datetime.fromtimestamp(row['lastUpdated']),
161 ))
162 return rss.to_xml()
163
164
165def main():
166 if CONFIG['output_mode'] == "combined":
167 upload_rss(generate_rss(get_javascript()),
168 CONFIG['swift']['combined_output_object'])
169 elif CONFIG['output_mode'] == "multiple":
170 for project in CONFIG['projects']:
171 upload_rss(
172 generate_rss(get_javascript(project), project=project),
173 "%s.xml" % (os.path.basename(project)))
174
175if __name__ == '__main__':
176 main()