root/management/bs-admin/trunk/cache_and_sync.py

Revision 467, 21.1 kB (checked in by cedenoj, 1 month ago)

fixes #4956 : adding projectsync cronjobs to svn. This is what the live site files look like.

  • Property svn:executable set to *
Line 
1 #!/usr/bin/python
2
3 # You'll need these set in your environment before running this script
4 # export DJANGO_SETTINGS_MODULE=webmanagement.settings
5 # export PYTHONPATH="/data/"
6 #
7 #
8 #
9
10 #generic python imports
11 from os import listdir
12 from ConfigParser import ConfigParser
13 import time,os
14 from time import strftime
15 from datetime import datetime
16 from difflib import *
17 from subprocess import *
18 from shutil import *
19 import re
20 from sets import Set
21
22 #trac imports
23 from trac.perm import PermissionSystem
24 from trac.env import open_environment
25
26 #django imports
27 from django.contrib.auth.models import User
28 from django.core.exceptions import ObjectDoesNotExist
29 from django.core.mail import send_mail
30 from webmanagement.project_request.models import Project, License
31
32 #psycopg imports
33 import psycopg2
34
35 #Directories
36 TRAC_PARENT_PATH = '/var/lib/trac/sites/'
37 SVN_PARENT_PATH = '/var/lib/svn/'
38 MANAGEMENT_DIR = '/data/management/'
39 SVN_INIT_PATH = '/data/master-svn/initial-data/' # should these go in /usr/share?
40 TRAC_INIT_PATH = '/data/master-trac/initial-data/' # ^^
41 APACHE_CONFIG_PATH = '/etc/apache2/vhosts.d/policies/'
42 SVN_AUTHZ_FILE = '/var/lib/svn/admin/conf/bs_authz'
43
44 def create_nonexistant_approved_projects():
45     projects = Project.objects.all()
46     for p in projects:
47         if(p.status == 'A' and not os.path.exists(TRAC_PARENT_PATH+p.short_name) and not os.path.exists(SVN_PARENT_PATH+p.short_name) ): #The project has been approved, but doesn't exist
48             #format users with no spaces and a comma between names
49
50             userlist = ','.join([u.username for u in p.members.all()])
51
52             if(userlist.count(str(p.owner)) < 1):
53                 userlist += ","+str(p.owner)
54
55             cmd = [MANAGEMENT_DIR + 'bs-admin.py',
56                 p.short_name,
57                 '--create',
58                 '--trac-name=' + p.name,
59                 '--users=' + userlist,
60                 '--trac-desc=' + str(p.description),
61                 '--trac-footer=This project licensed under the ' + str(p.license.name),
62                 '--owner-email=' + str(p.email),
63                 '--owner-username=' + str(p.owner),
64                 '--owner-name=' + p.owner.first_name + ' '  + p.owner.last_name,
65                 '--visibility=' + p.visibility,
66                 '--license=' + str(p.license.name),
67                 '--licensefile=' + p.license.filename,
68                 '--svn-initial-data=' + SVN_INIT_PATH + p.license.filename +'.svn',
69             ]
70             #execute the project creation command
71             print cmd
72             pipe = Popen(cmd, cwd=MANAGEMENT_DIR, shell=False, stdout=PIPE).stdout
73             data = pipe.read()
74             pipe.close()
75             #Email the owner
76             msg= "Your project, " + p.name + ", has been created. You may visit it at http://beaversource.oregonstate.edu/projects/" + p.short_name + "/ \n\nPlease contact us if you have any problems,\nBeaverSource Admins"
77             send_mail('[Project Created]  ' + p.short_name, msg, 'request@beaversource.oregonstate.edu', [p.email], fail_silently=False)
78
79             print data           
80
81
82 def add_admin_permissions(shortname, admin_list):
83
84     """As part of ticket #3266 we're making it so that all projects have an
85     admin group with view permissions and the admins are members of the group.
86     This will be very helpful when attempting to debug a project we're not a
87     member of"""
88
89     #Get the list of people who already have admin view permissions
90     env = open_environment(TRAC_PARENT_PATH + shortname)
91     time.sleep(2)
92     temp_list = [user for user,permission in PermissionSystem(env).get_all_permissions() if permission=='admin']
93
94     if(temp_list == []):
95        
96         #Create an admin group with all view permissions
97         cmd = ['/usr/bin/trac-admin', TRAC_PARENT_PATH + shortname, 'permission', 'add', 'admin', 'BROWSER_VIEW', 'CHANGESET_VIEW', 'CONFIG_VIEW', 'FILE_VIEW', 'LOG_VIEW', 'MILESTONE_VIEW', 'REPORT_VIEW', 'ROADMAP_VIEW', 'SEARCH_VIEW', 'STRACTISTICS_VIEW', 'TICKET_VIEW', 'TIMELINE_VIEW', 'WIKI_VIEW']
98         pipe = Popen(cmd, cwd=MANAGEMENT_DIR, shell=False, stdout=PIPE).stdout
99         data = pipe.read()
100         pipe.close()
101    
102     delta_list = Set(admin_list) - Set(temp_list)
103     #add all members of the admin project to that group
104     for user in delta_list:
105         cmd = ['/usr/bin/trac-admin', TRAC_PARENT_PATH + shortname, 'permission', 'add', user, 'admin']
106         pipe = Popen(cmd, cwd=MANAGEMENT_DIR, shell=False, stdout=PIPE).stdout
107         data = pipe.read()
108         pipe.close()
109
110
111 def sync_project_data():
112     #get the admin list once for the add_admin_permissions function.
113     env = open_environment(TRAC_PARENT_PATH + 'admin')
114     time.sleep(2)
115     admin_list = [user for user,permission in PermissionSystem(env).get_all_permissions() if permission=='TRAC_ADMIN']
116
117     for TRAC_ENV in listdir(TRAC_PARENT_PATH):
118         member_set = set()
119         config = ConfigParser()
120         config.read(TRAC_PARENT_PATH + TRAC_ENV + '/conf/trac.ini')
121         name = config.get('project', 'name')
122         shortname = config.get('project', 'shortname')
123         descr = config.get('project', 'descr')
124         url = config.get('project', 'url')
125
126         #While I'm here, I may as well remove the mainnav line from the individual project since we define it globally.
127         if(config.has_option('trac', 'mainnav')):
128             if(config.remove_option('trac','mainnav')):
129                 config.write(open(TRAC_PARENT_PATH + TRAC_ENV + '/conf/trac.ini', 'wb'))
130         #We also define the project icon globally.
131         if(config.has_option('project', 'icon')):
132             if(config.remove_option('trac','icon')):
133                 config.write(open(TRAC_PARENT_PATH + TRAC_ENV + '/conf/trac.ini', 'wb'))
134
135         #get a list of the members of the project
136         env = open_environment(TRAC_PARENT_PATH + TRAC_ENV)
137         time.sleep(2)
138         member_list = [user for user,permission in PermissionSystem(env).get_all_permissions() if permission=='TRAC_ADMIN']
139
140         #Make sure all the admins have view permission on this project.  I pass in the admin list so it doesn't need to be repeatedly found.
141         add_admin_permissions(shortname, admin_list)
142
143         #update project details
144         #get project, update its details
145         try:
146             p = Project.objects.get(short_name = shortname)
147         except ObjectDoesNotExist:
148             print "[Exception] Project %s doesn't exist, Please create it." % shortname
149             continue
150         p.name = name
151         p.description = descr
152        
153         #update members list
154         for person in member_list:
155
156             m, created = User.objects.get_or_create(username=person)
157             if(created):
158                 print "[Info] user %s had to be created." % m
159             member_set.add(m)
160
161         p.members = member_set # Set notation implicitly clears previous entries
162         p.save()
163         time.sleep(3)
164
165 def create_authz():
166
167     auth_header = """
168 ### This file is an example authorization file for svnserve.
169 ### Its format is identical to that of mod_authz_svn authorization
170 ### files.
171 ### As shown below each section defines authorizations for the path and
172 ### (optional) repository specified by the section name.
173 ### The authorizations follow. An authorization line can refer to a
174 ### single user, to a group of users defined in a special [groups]
175 ### section, or to anyone using the '*' wildcard.  Each definition can
176 ### grant read ('r') access, read-write ('rw') access, or no access
177 ### ('').
178
179 [groups]
180 # harry_and_sally = harry,sally
181
182 """
183     projects = Project.objects.all()
184     for p in projects:
185         if(p.visibility != 'A' and p.status == 'A'): #non-public projects
186             auth_file = open(SVN_PARENT_PATH + p.short_name + "/conf/authz",'w')
187             auth_file.write(auth_header)
188             auth_file.write("## project " + p.name + " at /data/trac/sites/" + p.short_name  + "\n")
189             auth_file.write("[/]\n")
190             if(p.visibility == 'M'):
191                 for person in p.members.values():
192                     auth_file.write(person['username'] + " = rw\n")
193             else:
194                 auth_file.write("* = r\n")
195             auth_file.write('\n')
196
197             auth_file.close()
198
199
200
201
202
203 def create_apache_config():
204     #create apache config that requires valid user for any non-public project
205     if(os.path.exists(APACHE_CONFIG_PATH + 'bs_projects')):
206         copyfile(APACHE_CONFIG_PATH + 'bs_projects', APACHE_CONFIG_PATH + 'bs_projects.old')
207
208     config_file = open(APACHE_CONFIG_PATH + 'bs_projects','w')
209
210     projects = Project.objects.all()
211     for p in projects:
212         if(p.visibility != 'A' and p.status == 'A'): #Any non-public project
213             #make sure your apache config has "Include /path/to/these/files/" for this to apple
214
215             config_file.write("""
216         <Location /svn/""" + p.short_name + """/>
217                 AuthzSVNAccessFile """ + SVN_PARENT_PATH + p.short_name + """/conf/authz
218                 Require valid-user
219         </Location>
220             """)
221
222     config_file.close()
223
224 #    #diff the new and old config and prompt to reload the server, if needed
225 #    if(os.path.exists(APACHE_CONFIG_PATH + 'bs_projects.old') and os.path.exists(APACHE_CONFIG_PATH + 'bs_projects')):
226 #        d = Differ()
227 #        result = list(d.compare(open(APACHE_CONFIG_PATH + 'bs_projects').readlines(), open(APACHE_CONFIG_PATH + 'bs_projects.old').readlines()))
228 #        if(len(result) != 0):
229 #            print "Project visibility config has changed, please reload server"
230
231
232
233
234
235 def check_license_wiki_files():
236     #check that all available licenses except other have valid svn and wiki files
237     licenses = License.objects.all()
238     for l in licenses:
239         license_file = TRAC_INIT_PATH + l.filename + ".wiki"
240         if(not os.path.exists(license_file)):
241             print "[Exception] %s not found" % license_file
242
243         svn_file = SVN_INIT_PATH + l.filename + ".svn"
244         if(not os.path.exists(svn_file)):
245             print "[Exception] %s not found" % svn_file
246
247
248 def smart_truncate(content, length=35, suffix='...'):
249   if len(content) <= length:
250     return content
251   else:
252     return ' '.join(content[:length+1].split(' ')[0:-1]) + suffix
253
254 def sync_project_profiles():
255  
256   # Create Social DB
257   file = open("/var/www/beaversource.oregonstate.edu/config.php")
258   social_config = file.read()
259   match = re.search('(?<=dbuser = ).*', social_config)
260   elgg_user = match.group(0).strip('";')
261   match = re.search('(?<=dbpass = ).*', social_config)
262   elgg_pass = match.group(0).strip('";')
263   match = re.search('(?<=dbhost = ).*', social_config)
264   elgg_host = match.group(0).strip('";')
265   match = re.search('(?<=dbname = ).*', social_config)
266   elgg_dbname = match.group(0).strip('";')
267
268   try:
269     conn = psycopg2.connect("dbname='%s' user='%s' host='%s' password='%s'" % (elgg_dbname, elgg_user, elgg_host, elgg_pass))
270   except:
271     print "Unable to connect to the db"
272    
273   cursor = conn.cursor()   
274
275   #Trac DB
276   config = ConfigParser()
277   if config.read(MANAGEMENT_DIR + 'beaversource.ini'):
278     tracuser = config.get('trac', 'dbadmin')
279     tracpass = config.get('trac', 'dbpass')
280   else:
281     print "Unable to read config file"
282
283   try:
284     tracconn = psycopg2.connect("dbname='bsc_projects' user='%s' host='dogwood.osuosl.org' password='%s'" % (tracuser, tracpass))
285   except:
286     print "Unable to connect to the trac db"
287
288   trac_cursor = tracconn.cursor()
289  
290
291   # New Profile Sync
292   meta_list = []
293   elgg_list = []
294   diff_list = []
295
296   sql = "SELECT username FROM elgg_users WHERE user_type='project'"
297   cursor.execute(sql)
298   elgg_projects = cursor.fetchall()
299   for each in elgg_projects:
300     elgg_list.append(each[0])
301
302   projects = Project.objects.all()
303   for p in projects:
304     if(p.status == 'A'):
305       meta_list.append(p.short_name)
306
307   for item in meta_list:
308     if not item in elgg_list:
309       diff_list.append(item)
310
311   for each in diff_list:
312     p = Project.objects.get(short_name__exact=each)
313     sql = "SELECT ident FROM elgg_users WHERE username='%s'" % (p.owner.username)
314     cursor.execute(sql)
315     value = cursor.fetchall() # returns a list of tuples
316     if value:
317       ownerid = value[0][0] # we only need the first record in the first tuple
318       sql = "INSERT INTO elgg_users (username, name, active, owner, user_type, moderation) VALUES ('%s', '%s', 'yes', '%s', 'project', 'no')" % (each, p.name, ownerid)
319       cursor.execute(sql)
320       conn.commit()
321
322
323     else:
324       sql = "INSERT INTO elgg_users (username, active, owner, user_type, moderation) VALUES ('%s', 'yes', '-1', 'person', 'no')" % (p.owner.username)
325       cursor.execute(sql)
326       conn.commit()
327      
328       sql = "SELECT ident FROM elgg_users WHERE username='%s'" % (p.owner.username)
329       cursor.execute(sql)
330       value = cursor.fetchall() # returns a list of tuples
331       if value:
332         ownerid = value[0][0] # we only need the first record in the first tuple
333         sql = "INSERT INTO elgg_users (username, name, active, owner, user_type, moderation) VALUES ('%s', '%s', 'yes', '%s', 'project', 'no')" % (each, p.name, ownerid)
334         cursor.execute(sql)
335         conn.commit()
336
337   # end of new profile sync
338
339   # trac data sync
340   projects = Project.objects.all()
341
342   for p in projects:
343     if(p.status == 'A'):
344       trac_name = p.name
345       shortname = p.short_name
346       descr = p.description
347       url = "http://beaversource.oregonstate.edu/projects/%s" % (p.short_name)
348
349       visibility = "PUBLIC"
350
351       trac_member_list = []
352       for each in p.members.all():
353         trac_member_list.append(each.username)
354      
355       elgg_member_list = []
356       diff_member_list = []
357      
358       sql = "SELECT ident FROM elgg_users WHERE username='%s'" % (p.short_name)
359       cursor.execute(sql)
360       value = cursor.fetchall()
361       if value:
362         project_id = value[0][0]
363
364         sql = "SELECT owner FROM elgg_friends WHERE friend='%s'" % (project_id)
365         cursor.execute(sql)
366         value = cursor.fetchall()
367         for each in value:
368           sql = "SELECT username FROM elgg_users WHERE ident='%s'" % (each[0])
369           cursor.execute(sql)
370           value = cursor.fetchall()
371           name = value[0][0]
372           elgg_member_list.append(name)
373
374
375         for each in trac_member_list:
376           if not each in elgg_member_list:
377             diff_member_list.append(each)
378
379         for each in diff_member_list:
380           sql = "SELECT ident FROM elgg_users WHERE username='%s'" % (each)
381           cursor.execute(sql)
382           value = cursor.fetchall()
383           if value:
384             user_id = value[0][0]
385            
386             sql = "INSERT INTO elgg_friends VALUES (DEFAULT, '%s', '%s')" % (user_id, project_id)
387             cursor.execute(sql)
388             conn.commit()
389
390         sql = "SELECT value FROM elgg_profile_data WHERE owner='%s' AND name='license'" % (project_id)
391         cursor.execute(sql)
392         value = cursor.fetchall()
393         if value:
394           sql = "UPDATE elgg_profile_data SET value='%s', access='%s' WHERE owner='%s' AND name='license'" % (p.license, visibility, project_id)
395           cursor.execute(sql)
396           conn.commit()
397         else:
398           sql = "INSERT INTO elgg_profile_data VALUES (DEFAULT, '%s', '%s', 'license', '%s')" % (project_id, visibility, p.license)
399           cursor.execute(sql)
400           conn.commit()
401        
402         sql = "SELECT value FROM elgg_profile_data WHERE owner='%s' AND name='biography'" % (project_id)
403         cursor.execute(sql)
404         value = cursor.fetchall()
405         if value:
406           sql = "UPDATE elgg_profile_data SET value=%s, access=%s WHERE owner=%s AND name='biography'"
407           cursor.execute(sql, (descr, visibility, project_id))
408           conn.commit()
409         else:
410           sql = "INSERT INTO elgg_profile_data VALUES (DEFAULT, %s, %s, 'biography', %s)"
411           cursor.execute(sql, (project_id, visibility, descr))
412           conn.commit()
413        
414         sql = "SELECT value FROM elgg_profile_data WHERE owner='%s' AND name='minibio'" % (project_id)
415         cursor.execute(sql)
416         value = cursor.fetchall()
417         if value:
418           sql = "UPDATE elgg_profile_data SET value=%s, access=%s WHERE owner=%s AND name='minibio'"
419           cursor.execute(sql, (smart_truncate(descr), visibility, project_id))
420           conn.commit()
421         else:
422           sql = "INSERT INTO elgg_profile_data VALUES (DEFAULT, %s, %s, 'minibio', %s)"
423           cursor.execute(sql, (project_id, visibility, smart_truncate(descr)))
424           conn.commit()
425        
426         sql = "SELECT time FROM master_wiki WHERE name='WikiStart' AND author='trac' AND dbuser='bs_%s_user' LIMIT 1" % (p.short_name)
427         trac_cursor.execute(sql)
428         trac_start_time = trac_cursor.fetchone()[0]
429         trac_time = datetime.fromtimestamp(trac_start_time)
430         trac_db_time = trac_time.strftime("%b %d, %Y - %I:%M %p")
431         sql = "SELECT value FROM elgg_profile_data WHERE owner='%s' AND name='start_time'" % (project_id)
432         cursor.execute(sql)
433         value = cursor.fetchone()
434         if value:
435           sql = "UPDATE elgg_profile_data SET value='%s', access='%s' WHERE owner='%s' AND name='start_time'" % (trac_db_time, visibility, project_id)
436           cursor.execute(sql)
437           conn.commit()
438         else:
439           sql = "INSERT INTO elgg_profile_data VALUES (DEFAULT, '%s', '%s', 'start_time', '%s')" % (project_id, visibility, trac_db_time)
440           cursor.execute(sql)
441           conn.commit()
442
443         sql = "SELECT changetime FROM master_ticket WHERE dbuser='bs_%s_user' ORDER BY time DESC LIMIT 1" % (p.short_name)
444         trac_cursor.execute(sql)
445         trac_last_ticket_change_time = trac_cursor.fetchone()
446         if trac_last_ticket_change_time:
447           trac_time = datetime.fromtimestamp(trac_last_ticket_change_time[0])
448           trac_db_time = trac_time.strftime("%b %d, %Y - %I:%M %p")
449         else:
450           trac_db_time = "None"
451         sql = "SELECT value FROM elgg_profile_data WHERE owner='%s' AND name='last_ticket_change'" % (project_id)
452         cursor.execute(sql)
453         value = cursor.fetchone()
454         if value:
455           sql = "UPDATE elgg_profile_data SET value='%s', access='%s' WHERE owner='%s' AND name='last_ticket_change'" % (trac_db_time, visibility, project_id)
456           cursor.execute(sql)
457           conn.commit()
458         else:
459           sql = "INSERT INTO elgg_profile_data VALUES (DEFAULT, '%s', '%s', 'last_ticket_change', '%s')" % (project_id, visibility, trac_db_time)
460           cursor.execute(sql)
461           conn.commit()
462        
463         sql = "SELECT time FROM master_wiki WHERE dbuser='bs_%s_user' ORDER BY time DESC LIMIT 1" % (p.short_name)
464         trac_cursor.execute(sql)
465         trac_last_wiki_change_time = trac_cursor.fetchone()
466         if trac_last_wiki_change_time:
467           trac_time = datetime.fromtimestamp(trac_last_wiki_change_time[0])
468           trac_db_time = trac_time.strftime("%b %d, %Y - %I:%M %p")
469         else:
470           trac_db_time = "None"
471         sql = "SELECT value FROM elgg_profile_data WHERE owner='%s' AND name='last_wiki_change'" % (project_id)
472         cursor.execute(sql)
473         value = cursor.fetchone()
474         if value:
475           sql = "UPDATE elgg_profile_data SET value='%s', access='%s' WHERE owner='%s' AND name='last_wiki_change'" % (trac_db_time, visibility, project_id)
476           cursor.execute(sql)
477           conn.commit()
478         else:
479           sql = "INSERT INTO elgg_profile_data VALUES (DEFAULT, '%s', '%s', 'last_wiki_change', '%s')" % (project_id, visibility, trac_db_time)
480           cursor.execute(sql)
481           conn.commit()
482        
483         sql = "SELECT rev,time FROM master_revision WHERE dbuser='bs_%s_user' ORDER BY time DESC LIMIT 1" % (p.short_name)
484         trac_cursor.execute(sql)
485         trac_last_commit = trac_cursor.fetchone()
486         if trac_last_commit:
487           trac_time = datetime.fromtimestamp(trac_last_commit[1])
488           trac_db_time = "<a href=\"/projects/%s/changeset/%s\">Revision %s</a> (%s)" % (p.short_name, trac_last_commit[0], trac_last_commit[0], trac_time.strftime("%b %d, %Y - %I:%M %p"))
489         else:
490           trac_db_time = "None"
491         sql = "SELECT value FROM elgg_profile_data WHERE owner='%s' AND name='last_commit'" % (project_id)
492         cursor.execute(sql)
493         value = cursor.fetchone()
494         if value:
495           sql = "UPDATE elgg_profile_data SET value='%s', access='%s' WHERE owner='%s' AND name='last_commit'" % (trac_db_time, visibility, project_id)
496           cursor.execute(sql)
497           conn.commit()
498         else:
499           sql = "INSERT INTO elgg_profile_data VALUES (DEFAULT, '%s', '%s', 'last_commit', '%s')" % (project_id, visibility, trac_db_time)
500           cursor.execute(sql)
501           conn.commit()
502
503         sql = "SELECT value FROM elgg_profile_data WHERE owner='%s' AND name='workweb'" % (project_id)
504         cursor.execute(sql)
505         value = cursor.fetchall()
506         if value:
507           sql = "UPDATE elgg_profile_data SET value='%s', access='%s' WHERE owner='%s' AND name='workweb'" % (url, visibility, project_id)
508           cursor.execute(sql)
509           conn.commit()
510         else:
511           sql = "INSERT INTO elgg_profile_data VALUES (DEFAULT, '%s', '%s', 'workweb', '%s')" % (project_id, visibility, url)
512           cursor.execute(sql)
513           conn.commit()
514
515         sql = "UPDATE elgg_users SET name='%s' WHERE ident='%s'" % (trac_name, project_id)
516         cursor.execute(sql)
517         conn.commit()
518
519 #Call all the funcation
520 create_nonexistant_approved_projects()
521 sync_project_data()
522 create_authz()
523 create_apache_config()
524 check_license_wiki_files()
525 #sync_project_profiles()
526
Note: See TracBrowser for help on using the browser.