root/cleverbox/trunk/cleverbox/model/project.py

Revision 357, 14.3 KB (checked in by trivoallan, 3 years ago)

Webserver can now write in Trac's "plugins" directory by default.

Line 
1# This file is part of the "Cleverbox" program.
2#
3# Cleverbox is free software: you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by
5# the Free Software Foundation, either version 3 of the License, or
6# (at your option) any later version.
7#
8# Cleverbox is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11# GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License
14# along with Cleverbox.  If not, see <http://www.gnu.org/licenses/>.
15#
16# Copyright 2008, Tristan Rivoallan
17
18"""
19This module regroups all logic used to manipulate project data in a given environment.
20"""
21
22import logging, os, re, ConfigParser, traceback
23from cleverbox.utils import filesystem, styles
24from cleverbox import model
25
26def exists(environment, client_name, project_name, project_status = None):
27    """
28    Returns true if requested project exists in environment.
29    """
30    return project_name in get(environment, client_name, project_status)
31
32def get(environment, client_name = None, status = None):
33    """
34    Returns list of projects for supplied environment.
35    If "client_name" parameter has a value, returned list is limited to client's projects.
36    If "status" parameter has a value (either "enabled" or "disabled"), returned list is limited to project with this status.
37    """
38
39    list_projects = []
40
41    # -- List of requested projects
42    list_enabled_projects = os.listdir(environment.get_path('projects-enabled'))
43    list_all_projects = os.listdir(environment.get_path('projects-available'))
44
45    list_projects = []
46    if (status == None):
47        list_projects = list_all_projects
48    if (status == 'enabled'):
49        list_projects = list_enabled_projects
50    if (status == 'disabled'):
51        for p in list_all_projects:
52            if p not in list_enabled_projects:
53                list_projects.append(p)
54
55    # Restrict to requested client
56    pattern = re.compile('(\w+)-(.*)')
57    if (client_name):
58        client_projects = []
59        for project_name in list_projects:
60            matches = pattern.findall(project_name)
61            if (client_name == matches[0][0]):
62                client_projects.append(matches[0][1])
63        list_projects = client_projects
64
65    return list_projects
66
67def disable(environment, client_name, project_name):
68    """
69    Disables project in environment.
70    """
71
72    if not exists(environment, client_name, project_name):
73        raise Exception, 'Client "%s" has no project "%s".' % (client_name, project_name)
74
75    try:
76        os.unlink(os.path.join(environment.get_path('projects-enabled'), '%s-%s' % (client_name, project_name)))
77        print styles.style.SUCCESSNORESET('')
78        logging.info("  Project '%s' has been disabled. Apache needs to be reloaded." % project_name)
79        print styles.style.RESET('')
80    except (IOError, os.error), exception:
81            raise Exception, 'Project "%s" is already disabled' % project_name
82
83def enable(environment, client_name, project_name):
84    """
85    Enables project in environment.
86    """
87
88    if not exists(environment, client_name, project_name):
89        raise Exception, 'Client "%s" has no project "%s"' % (client_name, project_name)
90
91    target = os.path.join(environment.get_path('projects-available'),
92                          '%s-%s' % (client_name, project_name))
93    linkname = os.path.join(environment.path,
94                            'projects-enabled',
95                            '%s-%s' % (client_name, project_name))
96    os.symlink(target, linkname)
97
98    print styles.style.SUCCESSNORESET('')
99    logging.info("  Project '%s' has been enabled. Apache needs to be reloaded." % project_name)
100    print styles.style.RESET('')
101
102def add(environment, client_name, project_name, parameters = {}):
103    """
104    Creates a new project for client in environment.
105    """
106
107    # Merging for consistency
108    parameters['client'] = client_name
109    parameters['short_name'] = project_name
110
111    # Check that all required parameters are here
112    required = ('client', 'short_name', 'full_name', 'profile', 'enable')
113    model.check_required_parameters(required, parameters)
114
115    # Make sure client exists
116    from cleverbox.model import client
117    if not client.exists(environment, client_name):
118        raise Exception, "Client '%s' does not exist." % client_name
119
120    # Make sure project does not already exists
121    if exists(environment, client_name, project_name):
122        raise Exception, 'Client "%s" already has a project named "%s"' % (client_name, project_name)
123
124    # Information collection
125    collected_infos = parameters
126
127    try:
128        # -- Apache configuration file
129        _write_apache_conf(environment, parameters)
130
131        # -- Create required dirs
132        _create_dirs(environment, parameters)
133
134        # -- SVN repository creation
135        _create_svn_repos(environment, parameters)
136
137        # -- Trac environment initialisation
138        _trac_initenv(environment, parameters)
139
140        # -- Revoke all permissions in trac
141        _trac_revoke_all_perms(environment, parameters)
142
143        # -- Trac initial permissions
144        _trac_setperms(environment, parameters)
145
146        # -- Modifies Trac default conf
147        _trac_defaultconf(environment, parameters)
148
149        # -- Fix perms
150        _fix_perms(environment, parameters)
151
152        if str(parameters['enable']).find('y') == 0:
153            enable(environment, client_name, project_name)
154
155    except Exception, e:
156        print "An error occured : "
157        print e
158        traceback.print_exc()
159        print "TODO : rollback changes"
160
161
162def _create_dirs(environment, infos):
163    os.makedirs(os.path.join(environment.config.get('general', 'clients_root'), infos['client'], 'htdocs', infos['short_name']), 0775)
164
165    print "  Created project's directory layout\n"
166
167def _write_apache_conf(environment, infos):
168    """
169    TODO : clients may not be stored in 'clients_root', be careful with that.
170    """
171    conf_template = open(os.path.join(environment.path, 'profiles', infos['profile'], 'project.apache.conf'))
172    conf_data = conf_template.read() % {'client_name'      : infos['client'],
173                                        'project_name'     : infos['short_name'],
174                                        'clients_root'     : environment.config.get('general', 'clients_root'),
175                                        'authbackend_pass' : environment.config.get('general', 'authbackend_pass'),
176                                        'domain_name'      : environment.config.get('general', 'domain')}
177
178    apache_conf_filepath = os.path.join( environment.path, 'projects-available', '%s-%s' % (infos['client'], infos['short_name']) )
179    f = file(apache_conf_filepath, 'w+')
180    f.write(conf_data)
181    f.close()
182
183    print "  Apache configuration written to %s\n" % apache_conf_filepath
184
185def _create_svn_repos(environment, infos):
186    repos_path = os.path.join( environment.config.get('general', 'clients_root'), infos['client'], 'var/svn', infos['short_name'] )
187    create_cmd = 'svnadmin create %s' % repos_path
188    (stdin, stdout, stderr) = os.popen3( create_cmd )
189
190    err = stderr.read()
191    if err:
192        raise Exception(err)
193
194    print "  Subversion repository created in %s\n" % repos_path
195
196def _trac_initenv(environment, infos):
197
198    trac_env_path = os.path.join( environment.config.get('general', 'clients_root'),
199                                 infos['client'],
200                                 'var/trac',
201                                 infos['short_name'] )
202
203    svn_path = os.path.join( environment.config.get('general', 'clients_root'),
204                            infos['client'],
205                            'var/svn',
206                            infos['short_name'] )
207
208    cmd_data = { 'env_path'         : trac_env_path,
209                 'title'            : '%s - %s - Trac' % (infos['client'], infos['short_name']),
210                 'db_dsn'           : 'sqlite:db/trac.db',
211                 'svn_path'         : svn_path,
212                 'tracadmin_path' : environment.config.get('trac', 'tracadmin_path')}
213
214    trac_cmd = '%(tracadmin_path)s %(env_path)s initenv "%(title)s" %(db_dsn)s svn %(svn_path)s' % cmd_data
215   
216    (stdin, stdout, stderr) = os.popen3( trac_cmd )
217
218    err = stderr.read()
219    if err:
220        raise Exception(err)
221
222    print "  Trac project initialized in %s\n" % trac_env_path
223
224def _trac_setperms(environment, infos):
225    trac_env_path = os.path.join( environment.config.get('general', 'clients_root'),
226                                 infos['client'],
227                                 'var/trac',
228                                 infos['short_name'] )
229
230    trac_perms_cmd = '%(tracadmin_path)s %(env_path)s permission %(subcommand)s %(subject)s %(perms)s'
231
232    # Grant default permissions
233    perms_config = ConfigParser.SafeConfigParser()
234    perms_config.read(os.path.join(environment.path, 'profiles', infos['profile'], 'permissions.ini'))
235
236    # Grant permissions as defined in permissions.ini
237    for profile in perms_config.options('trac'):
238        os.system( trac_perms_cmd % {'env_path'       : trac_env_path,
239                                     'subcommand'     : 'add',
240                                     'subject'        : profile,
241                                     'perms'          : perms_config.get('trac', profile),
242                                     'tracadmin_path' : environment.config.get('trac', 'tracadmin_path')} )
243
244    admin_login = infos['tracadmin']
245
246    os.system( trac_perms_cmd % {'env_path'       : trac_env_path,
247                                 'subcommand'     : 'add',
248                                 'subject'        : admin_login,
249                                 'perms'          : 'TRAC_ADMIN',
250                                 'tracadmin_path' : environment.config.get('trac', 'tracadmin_path')} )
251
252    print "  Trac initial permissions set (admin rights given to '%s')\n" % admin_login
253
254def _trac_revoke_all_perms(environment, infos):
255    import trac.env
256
257    trac_env_path = os.path.join( environment.config.get('general', 'clients_root'),
258                                 infos['client'],
259                                 'var/trac',
260                                 infos['short_name'] )
261
262    # Revoke all permissions
263    trac_env = trac.env.open_environment(trac_env_path)
264    trac_db = trac_env.get_db_cnx()
265    db_cursor = trac_db.cursor()
266    db_cursor.execute('DELETE FROM permission')
267    trac_db.commit()
268
269    print "  Revoked Trac default permissions\n"
270
271def _fix_perms(environment, infos):
272
273    clients_root = environment.config.get('general', 'clients_root')
274    client_name = infos['client']
275    project_name = infos['short_name']
276
277    permissions = {
278        os.path.join(environment.get_path(), 'projects-available', client_name + '-' + project_name)   : (0640, 'root', 'root'),
279        os.path.join(clients_root, client_name, 'htdocs', project_name)                                : (0770, 'root', 'www-data'),
280        os.path.join(clients_root, client_name, 'var', 'svn', project_name)                            : (0770, 'root', 'www-data'),
281        os.path.join(clients_root, client_name, 'var', 'svn', project_name, 'hooks')                   : (0770, 'root', 'www-data'),
282        os.path.join(clients_root, client_name, 'var', 'trac', project_name)                           : (0770, 'root', 'www-data'),
283        os.path.join(clients_root, client_name, 'var', 'trac', project_name, 'db')                     : (0770, 'root', 'www-data'),
284        os.path.join(clients_root, client_name, 'var', 'trac', project_name, 'db', 'trac.db')          : (0770, 'root', 'www-data'),
285        os.path.join(clients_root, client_name, 'var', 'trac', project_name, 'attachments')            : (0770, 'root', 'www-data'),
286        os.path.join(clients_root, client_name, 'var', 'trac', project_name, 'plugins')                : (0770, 'root', 'www-data'),
287        os.path.join(clients_root, client_name, 'var', 'trac', project_name, 'conf', 'trac.ini')       : (0660, 'root', 'www-data'),
288    }
289
290    filesystem.set_permissions(permissions)
291
292    # Since filesystem.set_permissions does not handle recursion, svn/db dir is modified is done manually
293    filesystem.chowntree(
294        os.path.join(clients_root, client_name, 'var', 'svn', project_name, 'db'),
295        filesystem.get_uid_from_name('root'),
296        filesystem.get_gid_from_name('www-data')
297    )
298    os.path.walk(os.path.join(clients_root, client_name, 'var', 'svn', project_name, 'db'), filesystem.chmodtree, 0770)
299
300    print "  Perms fixed\n"
301
302def _trac_defaultconf(environment, infos):
303    """
304    Overrides project's default trac.ini with values provided in configuration profile.
305    """
306
307    # New defaults
308    tracdefaults_config = ConfigParser.SafeConfigParser()
309    tracdefaults_config.read(os.path.join(environment.path, 'profiles', infos['profile'], 'trac-defaults.ini'))
310
311    # Trac base config file
312    project_config_path = os.path.join(environment.config.get('general', 'clients_root'), infos['client'], 'var', 'trac', infos['short_name'], 'conf', 'trac.ini')
313    tracproject_config = ConfigParser.SafeConfigParser()
314    tracproject_config.read(project_config_path)
315
316    # Overriding
317    for section in tracdefaults_config.sections():
318        if not tracproject_config.has_section(section):
319            tracproject_config.add_section(section)
320        for option in tracdefaults_config.options(section):
321            # Perform variable substituion in each default option
322            # It is probably not very good in term of performances, but we'll wait until someone complains to fix it
323            default_option = tracdefaults_config.get(section, option)
324            tracproject_config.set(section, option, default_option)
325
326    # Add cleverbox section
327    # The variables in this section can be used for string substitution across the file
328    cleverbox_options = {'client_name'      : infos['client'],
329                         'project_name'     : infos['short_name'],
330                         'clients_root'     : environment.config.get('general', 'clients_root'),
331                         'authbackend_pass' : environment.config.get('general', 'authbackend_pass'),
332                         'domain_name'      : environment.config.get('general', 'domain')}
333    tracproject_config.add_section('cleverbox')
334    for (option, value) in cleverbox_options.items():
335        tracproject_config.set('cleverbox', option, value)
336
337    # Write file to disk
338    fp = open(project_config_path, 'w+')
339    tracproject_config.write(fp)
Note: See TracBrowser for help on using the browser.