source: trunk/plugins/oforgeplugin/oforge/api.py @ 843

Revision 843, 34.5 KB checked in by rlixandru, 2 years ago (diff)

fixed borked api.py

Line 
1from os import path
2import datetime
3import os
4import os.path
5import logging
6import pkg_resources
7import psycopg2 
8import re
9import traceback
10import sys
11
12from trac.core import *
13from trac.config import Configuration
14from trac.admin.console import TracAdmin
15from trac.env import Environment, EnvironmentSetup
16from trac.db import get_column_names, Column, Index, DatabaseManager
17from trac.perm import PermissionSystem
18
19from oforgetools.projects.models import Project, Ticket
20
21class OForgeProjectCreationError(Exception):
22    def __init__(self, value):
23        self.value = value
24
25    def __str__(self):
26        return repr(self.value)
27
28class OForgeException(Exception):
29    def __init__(self, value):
30        self.value = value
31
32    def __str__(self):
33        return repr(self.value)
34
35class OForge(object):
36    '''
37    Perform multi-project operations.
38    '''
39    MASTER_CONFIG = ['/etc/oforge.ini', '/etc/oforge/oforge.ini']
40    '''MASTER_CONFIG lets oforge find the base trac site config file'''
41    def __init__(self, configpath=None):
42        """
43        The master ini is the trac.ini file that all projects
44        inherit from.  If the config path is not provided, we
45        will check for an oforge ini file in etc.  This can
46        provide the location of the master ini.
47        """
48        if not configpath:
49            for oforge_config in self.MASTER_CONFIG:
50                if os.path.exists(oforge_config):
51                    master_config = Configuration(oforge_config)
52                    configpath = master_config.get('oforge', 'configfile')
53        self.configpath = configpath
54        self.config = Configuration(configpath)
55        self.logger = logging.getLogger('OForge')
56
57    def postgres_db_props(self):
58        """
59        Get all project dashboard db related connection options.
60        """
61        props = {}
62        props['database'] = \
63                self.config.get('oforge', 'master_warehouse.db', None)
64        props['user'] = \
65                self.config.get('oforge', 'master_warehouse.user', None)
66        props['password'] = \
67                self.config.get('oforge', 'master_warehouse.password', None)
68        props['host'] = \
69                self.config.get('oforge', 'master_warehouse.host', None)
70        props['port'] = \
71                self.config.get('oforge', 'master_warehouse.port', None)
72        props['schema'] = \
73                self.config.get('oforge', 'master_warehouse.schema', None)
74        return props
75
76    def warehouse_db_cnx(self):
77        """
78        Context for the project dashboard postgres schema.
79        """
80        p = self.postgres_db_props()
81
82        dsn = []
83        if p['user']:
84            dsn.append('user=%s'%(p['user']))
85        if p['database']:
86            dsn.append('dbname=%s'%(p['database']))
87        if p['password']:
88            dsn.append('password=%s'%(p['password']))
89        if p['host']:
90            dsn.append('host=%s'%(p['host']))
91        if p['port']:
92            dsn.append('port=%s'%(p['port']))
93
94        cnx = psycopg2.connect(' '.join(dsn))
95        cnx.set_client_encoding('UNICODE')
96        return cnx
97
98    def getenv(self, name):
99        """
100        Get an environment by name.
101        """
102        base = self.config.get('oforge', 'trac_base')
103        thepath = path.join(base, name)
104        env = Environment(thepath)
105        return env
106
107    def getconfig(self, name):
108        """
109        Return the config object of an environment by name.
110        """
111        base = self.config.get('oforge', 'trac_base')
112        thepath = path.join(base, name, 'conf/trac.ini')
113        config = Configuration(thepath)
114        return  config
115
116    def get_project(self, env):
117        """
118        Return a project object from the django project dashboard api.
119        """
120        return Project.objects.get(id=env.config.getint('oforge', 'proj_id'))
121
122    def _create_postgres_user_and_schema(self, name, password):
123        if not re.match('^\w+$', name):
124            raise OForgeProjectCreationError('Project schema name may only contain alphanumeric chars')
125        # Create the new schema/user
126        master_db_cnx = self.warehouse_db_cnx()
127        # Grab a context for the old sqlite db to copy data from
128        try:
129            # Create schema and user for the project
130            master_cursor = master_db_cnx.cursor()
131            master_cursor.execute('CREATE SCHEMA %s'%(name))
132            master_cursor.execute("CREATE USER %s WITH PASSWORD '%s'"%(name, password))
133            master_cursor.execute('GRANT ALL PRIVILEGES ON SCHEMA %s TO %s'%(name, \
134                    name))
135            # Set the default schema of the user
136            master_cursor.execute('ALTER USER %s SET search_path TO %s'%\
137                    (name, name))
138            # Create the project dashboard entry for the project
139            master_db_cnx.commit()
140        except Exception, e:
141            master_db_cnx.rollback()
142            raise e
143
144    def _get_postgres_url(self, user_schema_name, password):
145        props = self.postgres_db_props()
146        dburl = 'postgres://%s:%s@%s/%s'%(user_schema_name, password, \
147                props['host'], props['database'])
148        return dburl
149
150    def _canonize_schema_name(self, name):
151        return ("workspace_%s"%(re.sub('(\W+)', '', name))).lower()
152
153    def reload_warehouse_tickets(self, name):
154        env = self.getenv(name)
155        proj_id = env.config.get('oforge', 'proj_id')
156        if proj_id:
157            project = Project(id=proj_id)
158            if not project:
159                raise OForgeException('project %d not found'%(proj_id))
160            Ticket.objects.filter(project=project).delete
161            db_cnx = env.get_db_cnx()
162            cursor = db_cnx.cursor()
163            cursor.execute("""SELECT id, type, time, changetime, component,
164                                severity, priority, owner, reporter, cc,
165                                version, milestone, resolution, summary,
166                                description, keywords, status
167                              FROM ticket""")
168            for row in cursor.fetchall():
169                t = Ticket(
170                        project=project,
171                        ticket_id=row[0],
172                        type=row[1],
173                        time=int(row[2]),
174                        changetime=int(row[3]),
175                        component=row[4],
176                        severity=row[5],
177                        priority=row[6],
178                        owner=row[7],
179                        reporter=row[8],
180                        cc=row[9],
181                        version=row[10],
182                        milestone=row[11],
183                        resolution=row[12],
184                        summary=row[13],
185                        description=row[14],
186                        keywords=row[15],
187                        status=row[16])
188                t.save()
189
190    def warehouse_project(self, name):
191        env = self.getenv(name)
192        proj_id = env.config.get('oforge', 'proj_id')
193        if proj_id:
194            fields = ('type', 'time', 'changetime', 'component', 'severity', 
195                'priority', 'owner', 'reporter', 'cc', 'version', 'milestone', 
196                'resolution', 'summary', 'description', 'keywords', 'status')
197            names = ','.join(fields)
198            values = ','.join(map(lambda f: "new.%s"%(f), fields))
199            setters = ','.join(map(lambda f: "%s = new.%s"%(f,f), fields))
200            db_cnx = env.get_db_cnx()
201            db_conn_str = env.config.get('trac', 'database')
202            conn_regex = re.compile('^postgres://([^:]+):')
203            db_user = conn_regex.match(db_conn_str).group(1)
204            workspace_prefix = self._canonize_schema_name(name)
205            project = Project.objects.filter(id=proj_id)
206            wh_db_cnx = self.warehouse_db_cnx()
207            try:
208                wh_cursor = wh_db_cnx.cursor()
209                cursor = db_cnx.cursor()
210                schema = env.config.get('oforge', 'postgres_schema', 
211                        workspace_prefix)
212                wh_schema = self.config.get('oforge', 'master_warehouse.schema', 'public')
213                wh_cursor.execute("""GRANT USAGE ON
214                        schema %s TO %s"""%(wh_schema, db_user))
215                wh_cursor.execute("""GRANT INSERT,DELETE,UPDATE ON
216                        projects_ticket TO %s"""%(db_user))
217                wh_cursor.execute("""GRANT UPDATE,SELECT ON
218                        projects_ticket_id_seq TO %s"""%(db_user))
219                if not re.match('(.*, ?)?%s($|,)'%(wh_schema), schema):
220                    # Add warehouse schema to the path if it's not added already
221                    wh_cursor.execute("""ALTER USER %s SET search_path TO %s, %s 
222                            """%(db_user, schema, wh_schema))
223                    cursor.execute("SET search_path TO %s, %s"%(schema, wh_schema))
224                wh_db_cnx.commit()
225                cursor.execute("""
226                    CREATE OR REPLACE RULE %s_update_ticket
227                    AS ON UPDATE TO ticket
228                    DO ALSO UPDATE projects_ticket
229                      SET %s 
230                      WHERE ticket_id = old.id AND
231                        project_id = %s"""%(workspace_prefix, setters, 
232                            proj_id))
233                cursor.execute("""
234                    CREATE OR REPLACE RULE %s_insert_ticket
235                    AS ON INSERT TO ticket
236                    DO ALSO
237                      INSERT INTO projects_ticket (project_id, ticket_id, %s)
238                          VALUES(%s, currval('ticket_id_seq'::text), %s)
239                    """%(workspace_prefix, names, proj_id, values))
240                cursor.execute("""
241                    CREATE OR REPLACE RULE %s_delete_ticket
242                    AS ON DELETE TO ticket
243                    DO ALSO DELETE FROM projects_ticket
244                      WHERE ticket_id = old.id AND
245                        project_id = %s"""%(workspace_prefix, proj_id))
246                cursor.execute("SELECT * FROM ticket")
247                db_cnx.commit()
248            except Exception, e:
249                db_cnx.rollback()
250                wh_db_cnx.rollback()
251                raise
252            self.reload_warehouse_tickets(name)
253
254    def converttopostgres(self, name):
255        """
256        Convert a project from sqlite to postgresql.
257        """
258        # Setup vars
259        config = self.getconfig(name)
260        new_user_schema_name = self._canonize_schema_name(name)
261        new_password = config.get('oforge','initial_db_password', 'secret')
262        project_desc = config.get('project','desc', name)
263        category = config.get('project', 'category')
264        project_display_name = config.get('project','name', name)
265        # Create the new schema/user
266        self._create_postgres_user_and_schema(new_user_schema_name, new_password)
267
268        src_db_cnx = self.getenv(name).get_db_cnx()
269        olddburl = config.get('trac', 'database')
270        config.set('trac', 'database', 
271                self._get_postgres_url(new_user_schema_name, new_password))
272        config.set('oforge', 'postgres_schema', new_user_schema_name)
273        config.save()
274        new_env = self.getenv(name)
275        target_db_cnx = new_env.get_db_cnx()
276        try:
277            # Create tables in new schema
278            DatabaseManager(new_env).init_db()
279            # This is needed to get the default data into the DB
280            # Which is required to run the upgrade method that follows
281            EnvironmentSetup(new_env).environment_created()
282            new_env.upgrade(backup=False)
283            # Get list of tables excluding ones that shouldn't be copeied
284            target_db_cursor = target_db_cnx.cursor()
285            target_db_cursor.execute("""
286                SELECT table_name
287                FROM information_schema.tables
288                WHERE table_schema = %s AND table_name != 'session'
289                    AND table_name != 'auth_cookie'""", (new_user_schema_name,))
290            # Copy tables
291            for table in map(lambda r: r[0], target_db_cursor.fetchall()):
292                self.logger.debug("copying table %s"%(table))
293                self._copy_table(src_db_cnx, target_db_cnx, table)
294            # Commit all changes
295            target_db_cnx.commit()
296        except Exception, e:
297            config.set('trac','database', olddburl)
298            config.set('oforge','proj_id', None)
299            target_db_cnx.rollback()
300            master_db_cnx = self.warehouse_db_cnx()
301            master_cursor = master_db_cnx.cursor()
302            master_cursor.execute('DROP SCHEMA %s CASCADE'%(new_user_schema_name))
303            master_cursor.execute('DROP USER %s'%(new_user_schema_name))
304            master_db_cnx.commit()
305            raise 
306
307    def add_dashboard_project(self, name, tags=None):
308        """
309        Connect a project to the project dashboard.
310        """
311        env = self.getenv(name)
312        if env.config.get('oforge', 'proj_id', None):
313            raise OForgeException("Project already registered")
314        display_name = env.config.get('project', 'name')
315        type = env.config.get('project', 'category')
316        p = Project(name=name, display_name=display_name,type=type,
317                state='Open',start_date=datetime.datetime.now())
318        p.save()
319        p.tags = tags
320        p.save()
321        env.config.set('oforge', 'proj_id', p.id)
322        env.config.save()
323        return p.id
324
325    def remove_dashboard_project(self, name):
326        """
327        Disconnect a project from the project dashboard.
328        """
329        env = self.getenv(name)
330        proj_id = env.config.getint('oforge', 'proj_id', -1)
331        if proj_id != -1:
332            Project.objects.filter(id=proj_id).delete()
333            env.config.set('oforge', 'proj_id', None)
334            env.config.save()
335        else:
336            raise OForgeProjectException("Project not registered")
337
338    def createproject(self, name, **kwargs):
339        '''
340        Create an OForge project.  Takes the following dict args:
341
342         * createsvn:           Flag to stop svn repo creation
343                                default: True
344
345         * svntemplate:         Template svn structure
346                                default: none
347
348         * type:                Project type
349                                default: Client
350
351         * defaultworkspace:    Workspace to clone
352                                default: none
353
354         * admins:              Users to give admin priviledges to
355                                Comma seperated
356                                default: noone
357
358         * postgres:            Use postgres DB instead of sqlite
359                                default: True
360
361         * display_name:        Display name of project
362                                default: same as name parameter
363
364         * description:         Description of the project
365
366         * tags:                Tags for project
367
368         * copy_ini_sections:   Sections of default workspace (if any)
369                                to copy to the new environment
370                                default: ticket-workflow and ticket-custom
371        '''
372        self.logger.debug('Parsing args')
373
374        svnlocation = None
375        gitlocation = None
376        traclocation = None
377        engine = "git"
378        createsvn = kwargs.get('createsvn', False)
379        svntemplate = kwargs.get('svntemplate', None)
380        type = kwargs.get('type', 'Client')
381        defaultworkspace = kwargs.get('defaultworkspace', None)
382        admins = kwargs.get('admins', None)
383        postgres = kwargs.get('postgres', True)
384        descr = kwargs.get('description', None)
385        display_name = kwargs.get('display_name', name)
386        tags = kwargs.get('tags', None)
387        copy_ini_sections = kwargs.get('copy_ini_sections', ('ticket-custom', 
388            'ticket-workflow'))
389        tracadmin = TracAdmin()
390
391        # error handling flags
392        project_created = None
393        schema_created = None
394        user_created = None
395        env_created = None
396        svn_created = None
397        proj_id = None
398        try:
399        #added git
400            if createsvn:
401                if engine=="svn":
402                    svnlocation = path.join(self.config.get('oforge', 'svn_base'),name)
403                    if not path.exists(svnlocation) and createsvn:
404                        os.makedirs(svnlocation)
405                        svn_created = True
406                if engine=="git":
407                    gitlocation = path.join(self.config.get('oforge', 'git_base'),name)
408                    if not path.exists(gitlocation) and createsvn:
409                        os.makedirs(gitlocation)
410                        svn_created = True
411           
412            traclocation = path.join(self.config.get('oforge', 'trac_base'), 
413                    name)
414            dburl = None
415            if postgres:
416                new_user_schema_name = self._canonize_schema_name(name)
417                new_password = self.config.get('oforge','initial_db_password', 'secret')
418                self._create_postgres_user_and_schema(new_user_schema_name, new_password)
419                schema_created = user_created = True
420                dburl = self._get_postgres_url(new_user_schema_name, new_password)
421            self.logger.debug('Creating svn and trac dirs')
422            if not path.exists(traclocation):
423                os.makedirs(traclocation)
424            self.logger.debug('Sanity check')
425            # sanity check
426            tracadmin.env_set(traclocation)
427            if tracadmin.env_check():
428                raise OForgeProjectCreationError, \
429                        'The project %s already exists.'%(name)            if os.listdir(traclocation):
430                raise OForgeProjectCreationError, \
431                    'There are files in the project directory %s.'%(traclocation)
432            if createsvn:
433                if engine=="svn":
434                    self.logger.debug('Creating svn repo')
435                    self.createsvn(svnlocation, svntemplate)
436                if engine=="git":
437                    self.logger.debug('Creating git repo')
438                    self.creategit(gitlocation, svntemplate) #todo: whatis svntemplate
439            self.logger.debug('Creating trac environment')
440            if engine=="svn":
441                env = self.createenvironment(
442                    name=name,
443                    display_name=display_name, 
444                    descr=descr, 
445                    traclocation=traclocation, 
446                    category=type, 
447                    repopath=svnlocation,
448                    repo_type='',
449                    database=dburl
450                    )
451            if engine=="git":
452                env = self.createenvironment(
453                    name=name,
454                    display_name=display_name, 
455                    descr=descr, 
456                    traclocation=traclocation, 
457                    category=type, 
458                    repopath=gitlocation+"/.git",
459                    repo_type="git",
460                    database=dburl
461                    )   
462            env_created = True
463
464            if defaultworkspace: 
465                self.logger.debug(
466                        "Using source project %s"%(defaultworkspace))
467                sourceenv = self.getenv(defaultworkspace)
468                if copy_ini_sections and len(copy_ini_sections):
469                    src_conf = sourceenv.config
470                    target_conf = env.config
471                    self._copy_ini_sections(src_conf, target_conf, copy_ini_sections)
472                src_dbstr = sourceenv.config.get('trac', 'database')
473                src_is_postgres = \
474                        re.match('^postgres', src_dbstr)
475                try: 
476                    self.logger.debug('Cloning default workspace')
477                    self.cloneproject(sourceenv, env, src_is_postgres=src_is_postgres)
478                finally:
479                    sourceenv.shutdown()
480            env.upgrade(backup=False)
481            if admins:
482                permsys = PermissionSystem(self.getenv(name))
483                for admin in re.split('\s*,\s*', admins):
484                    permsys.grant_permission(admin, 'TRAC_ADMIN')
485            proj_id = self.add_dashboard_project(name, tags=tags)
486            project_created = True
487            self.logger.debug('Environment creation complete')
488        except Exception, e:
489            # rollback
490            self.logger.error(traceback.format_exc())
491            self.logger.debug('A failure occured when creating' + \
492                    ' the project.  Deleting directories')
493            if env_created:
494                try:
495                    if traclocation:
496                        rm_rf(traclocation)
497                except Exception, e:
498                    pass
499
500            if svn_created:
501                try:
502                    if svnlocation:
503                        rm_rf(svnlocation)
504                except Exception, e:
505                    pass
506
507            if project_created and proj_id:
508                try:
509                    Project.objects.filter(id=proj_id).delete()
510                except Exception, e:
511                    pass
512
513            if schema_created:
514                self.logger.error("deleting schema..")
515                try:
516                    master_db_cnx = self.warehouse_db_cnx()
517                    master_cursor = master_db_cnx.cursor()
518                    master_cursor.execute('DROP SCHEMA %s CASCADE'%(new_user_schema_name))
519                    master_db_cnx.commit()
520                except:
521                    self.logger.error("deleting schema failed!")
522                    pass
523
524            if user_created:
525                try:
526                    self.logger.error("deleting db user..")
527                    master_db_cnx = self.warehouse_db_cnx()
528                    master_cursor = master_db_cnx.cursor()
529                    master_cursor.execute('DROP USER %s'%(new_user))
530                    master_db_cnx.commit()
531                except:
532                    self.logger.error("deleting db user failed!")
533                    pass
534
535            raise e
536
537    def project_categories(self):
538        cats = self.config.get('oforge', 'categories', '')
539        return re.split('\s*,\s*', cats)
540       
541    def createsvn(self, svnlocation, defaultsvn=None):
542        self.logger.debug("Creating repo at %s"%(svnlocation))
543        os.system('svnadmin create --fs-type=fsfs %s'%(svnlocation))
544        if defaultsvn:
545            self.logger.debug("Importing %s"%(defaultsvn))
546            os.system(('svn import -q %s --message ' +\
547                    '"Initial repository layout" file://%s')%
548                    (defaultsvn, svnlocation))
549
550    def creategit(self, gitlocation, defaultsvn=None):
551        self.logger.debug("Creating repo at %s"%(gitlocation))
552        os.system('git init %s'%(gitlocation))
553        os.chdir(gitlocation)
554        os.system('/usr/lib/git-core/git-update-server-info')
555        #todo: Igonring template for now
556        """if defaultsvn:
557            self.logger.debug("Importing %s"%(defaultsvn))
558            os.system(('svn import -q %s --message ' +\
559                    '"Initial repository layout" file://%s')%
560                    (defaultsvn, gitlocation))"""
561
562    def exists(self, name):
563        base = self.config.get('oforge', 'trac_base')
564        thepath = path.join(base, name)
565        return os.path.exists(thepath)
566
567    def createenvironment(self, name, display_name, descr, traclocation, category, repopath, repo_type, database):
568                if repo_type=="git":
569                        tracoptions = filter(None, [
570                        ('inherit', 'file', self.config.filename),
571                        ('project', 'category', category),
572                        ('project', 'name', display_name),
573                        ('project', 'descr', descr),
574                        ('trac', 'repository_dir', repopath),
575                        ('trac', 'repository_type', 'git'),     
576                        ('header_logo', 'link', '/trac/%s'%(name)),
577                        database and ('trac', 'database', database) or None])
578                else:
579                        tracoptions = filter(None, [
580                        ('inherit', 'file', self.config.filename),
581                        ('project', 'category', category),
582                        ('project', 'name', display_name),
583                        ('project', 'descr', descr),
584                        ('trac', 'repository_dir', repopath),
585                        ('header_logo', 'link', '/trac/%s'%(name)),
586                        database and ('trac', 'database', database) or None])
587                env = None
588                env = Environment(traclocation, create=True, options=tracoptions)
589                env.upgrade(backup=False)
590                return env
591
592    def cloneproject(self, sourceenv, targetenv, src_is_postgres=False):
593        '''
594        Extract data from one project and insert it into the other
595        '''
596        tables = ['wiki', 'report', 'permission', 'milestone', 'component', 'enum', 'version']
597        if targetenv.is_component_enabled('tractags.*') and \
598                sourceenv.is_component_enabled('tractags.*'):
599            tables.append('tags')
600
601        if targetenv.is_component_enabled('tracresourcetools.*') and \
602                sourceenv.is_component_enabled('tracresourcetools.*'):
603            tables.append('resource_custom')
604
605        source_db = sourceenv.get_db_cnx()
606        target_db = targetenv.get_db_cnx()
607
608        try:
609            for table in tables:
610                self._copy_table(source_db, target_db, table, 
611                        src_is_postgres=src_is_postgres)
612        except Exception, e:
613            target_db.rollback()
614            sys.stderr.write(traceback.format_exc())
615            raise e
616
617        target_db.commit()
618
619    def _copy_ini_sections(self, src_conf, target_conf, sections, save=True):
620        for section in sectio      category = config.get('project', 'category')
621        project_display_name = config.get('project','name', name)
622        # Create the new schema/user
623        self._create_postgres_user_and_schema(new_user_schema_name, new_password)
624
625        src_db_cnx = self.getenv(name).get_db_cnx()
626        olddburl = config.get('trac', 'database')
627        config.set('trac', 'database', 
628                self._get_postgres_url(new_user_schema_name, new_password))
629        config.set('oforge', 'postgres_schema', new_user_schema_name)
630        config.save()
631        new_env = self.getenv(name)
632        target_db_cnx = new_env.get_db_cnx()
633        try:
634            # Create tables in new schema
635            DatabaseManager(new_env).init_db()
636            # This is needed to get the default data into the DB
637            # Which is required to run the upgrade method that follows
638            EnvironmentSetup(new_env).environment_created()
639            new_env.upgrade(backup=False)
640            # Get list of tables excluding ones that shouldn't be copeied
641            target_db_cursor = target_db_cnx.cursor()
642            target_db_cursor.execute("""
643                SELECT table_name
644                FROM information_schema.tables
645                WHERE table_schema = %s AND table_name != 'session'
646                    AND table_name != 'auth_cookie'""", (new_user_schema_name,))
647            # Copy tables
648            for table in map(lambda r: r[0], target_db_cursor.fetchall()):
649                self.logger.debug("copying table %s"%(table))
650                self._copy_table(src_db_cnx, target_db_cnx, table)
651            # Commit all changes
652            target_db_cnx.commit()
653        except Exception, e:
654            config.set('trac','database', olddburl)
655            config.set('oforge','proj_id', None)
656            target_db_cnx.rollback()
657            master_db_cnx = self.warehouse_db_cnx()
658            master_cursor = master_db_cnx.cursor()
659            master_cursor.execute('DROP SCHEMA %s CASCADE'%(new_user_schema_name))
660            master_cursor.execute('DROP USER %s'%(new_user_schema_name))
661            master_db_cnx.commit()
662            raise 
663
664    def add_dashboard_project(self, name, tags=None):
665        """
666        Connect a project to the project dashboard.
667        """
668        env = self.getenv(name)
669        if env.config.get('oforge', 'proj_id', None):
670            raise OForgeException("Project already registered")
671        display_name = env.config.get('project', 'name')
672        type = env.config.get('project', 'category')
673        p = Project(name=name, display_name=display_name,type=type,
674                state='Open',start_date=datetime.datetime.now())
675        p.save()
676        p.tags = tags
677        p.save()
678        env.config.set('oforge', 'proj_id', p.id)
679        env.config.save()
680        return p.id
681
682    def remove_dashboard_project(self, name):
683        """
684        Disconnect a project from the project dashboard.
685        """
686        env = self.getenv(name)
687        proj_id = env.config.getint('oforge', 'proj_id', -1)
688        if proj_id != -1:
689            Project.objects.filter(id=proj_id).delete()
690            env.config.set('oforge', 'proj_id', None)
691            env.config.save()
692        else:
693            raise OForgeProjectException("Project not registered")
694
695    def createproject(self, name, **kwargs):
696        '''
697        Create an OForge project.  Takes the following dict args:
698
699         * createsvn:           Flag to stop svn repo creation
700                                default: True
701
702         * svntemplate:         Template svn structure
703                                default: none
704
705         * type:                Project type
706                                default: Client
707
708         * defaultworkspace:    Workspace to clone
709                                default: none
710
711         * admins:              Users to give admin priviledges to
712                                Comma seperated
713                                default: noone
714
715         * postgres:            Use postgres DB instead of sqlite
716                                default: True
717
718         * display_name:        Display name of project
719                                default: same as name parameter
720
721         * description:         Description of the project
722
723         * tags:                Tags for project
724
725         * copy_ini_sections:   Sections of default workspace (if any)
726                                to copy to the new environment
727                                default: ticket-workflow and ticket-custom
728        '''
729        self.logger.debug('Parsing args')
730
731        svnlocation = None
732        gitlocation = None
733        traclocation = None
734        engine = "git"
735        createsvn = kwargs.get('createsvn', False)
736        svntemplate = kwargs.get('svntemplate', None)
737        type = kwargs.get('type', 'Client')
738        defaultworkspace = kwargs.get('defaultworkspace', None)
739        admins = kwargs.get('admins', None)
740        postgres = kwargs.get('postgres', True)
741        descr = kwargs.get('description', None)
742        display_name = kwargs.get('display_name', name)
743        tags = kwargs.get('tags', None)
744        copy_ini_sections = kwargs.get('copy_ini_sections', ('ticket-custom', 
745            'ticket-workflow'))
746        tracadmin = TracAdmin()
747
748        # error handling flags
749        project_created = None
750        schema_created = None
751        user_created = None
752        env_created = None
753        svn_created = None
754        proj_id = None
755        try:
756        #added git
757            if createsvn:
758                if engine=="svn":
759                    svnlocation = path.join(self.config.get('oforge', 'svn_base'),name)
760                    if not path.exists(svnlocation) and createsvn:
761                        os.makedirs(svnlocation)
762                        svn_created = True
763                if engine=="git":
764                    gitlocation = path.join(self.config.get('oforge', 'git_base'),name)
765                    if not path.exists(gitlocation) and createsvn:
766                        os.makedirs(gitlocation)
767                        svn_created = True
768           
769            traclocation = path.join(self.config.get('oforge', 'trac_base'), 
770                    name)
771            dburl = None
772            if postgres:
773                new_user_schema_name = self._canonize_schema_name(name)
774                new_password = self.config.get('oforge','initial_db_password', 'secret')
775                self._create_postgres_user_and_schema(new_user_schema_name, new_password)
776                schema_created = user_created = True
777                dburl = self._get_postgres_url(new_user_schema_name, new_password)
778            self.logger.debug('Creating svn and trac dirs')
779            if not path.exists(traclocation):
780                os.makedirs(traclocation)
781            self.logger.debug('Sanity check')
782            # sanity check
783            tracadmin.env_set(traclocation)
784            if tracadmin.env_check():
785                raise OForgeProjectCreationError, \
786                ns:
787            for option in target_conf[section].options():
788                target_conf.remove(section, option[0])
789            for option in src_conf[section].options():
790                target_conf.set(section, *option)
791        if save:
792            target_conf.save()
793
794    def _copy_table(self, src_db, dest_db, table, do_close_tran=False, src_is_postgres=False):
795        '''
796        Copy a table from one DB to another
797        '''
798        src_cursor = src_db.cursor()
799        dest_cursor = dest_db.cursor()
800        test_query = """SELECT name FROM sqlite_master
801                        WHERE name = '%s'"""%(table)
802        if src_is_postgres:
803            src_cursor.execute("SHOW search_path")
804            schema = src_cursor.fetchone()[0]
805            test_query = """SELECT table_name FROM information_schema.tables
806                            WHERE table_name = '%s' AND
807                                table_schema = '%s'"""%(table, schema)
808        # TODO: this needs to work with postgres too
809        src_cursor.execute(test_query)
810        if src_cursor.fetchone():
811            src_cursor.execute('SELECT * FROM %s'%(table))
812            dest_cursor.execute('SELECT * FROM %s LIMIT 1'%(table))
813
814            src_table_columns = get_column_names(src_cursor)
815            dest_table_columns = get_column_names(dest_cursor)
816            if src_table_columns != dest_table_columns:
817                raise Exception("Different table structure. \n src: %s " + \
818                        "\n dest: %s"%(','.join(src_table_columns), \
819                        ','.join(dest_table_columns)))
820           
821            src_data = src_cursor.fetchall()
822            try:
823                if len(src_data):
824                     # clean up destination
825                     dest_cursor.execute('DELETE FROM %s'%(table,))
826                     # load default data
827                     src_tc_str = ','.join(src_table_columns)
828                     dest_tc_str = ','.join(['%s' for c in src_table_columns])
829                     dest_cursor.executemany(
830                             'INSERT INTO %s (%s) values (%s)'%(table, 
831                                 src_tc_str, dest_tc_str), src_data)
832                     # commit
833                     if do_close_tran:
834                         src_db.commit()
835            except Exception, e:
836                logging.getLogger('OForge').error(traceback.format_exc())
837                if do_close_tran:
838                    src_db.rollback()
839                raise e
840def rm_rf(d):
841    for path in (os.path.join(d,f) for f in os.listdir(d)):
842        if os.path.isdir(path):
843            rm_rf(path)
844        else:
845            os.unlink(path)
846    os.rmdir(d)
Note: See TracBrowser for help on using the repository browser.