| 1 | # Copyright David Abrahams 2007. Distributed under the Boost |
|---|
| 2 | # Software License, Version 1.0. (See accompanying |
|---|
| 3 | # file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
|---|
| 4 | |
|---|
| 5 | import sys |
|---|
| 6 | import os |
|---|
| 7 | from trac.config import Configuration |
|---|
| 8 | |
|---|
| 9 | parent_dir = '/usr/local/share/tracs' |
|---|
| 10 | old_trac_dir = parent_dir |
|---|
| 11 | new_trac_dir = '/usr/local/share/trac' |
|---|
| 12 | repospath = '/usr/local/svnroot/main' |
|---|
| 13 | |
|---|
| 14 | dbname = 'trac' |
|---|
| 15 | master_user = 'trac' |
|---|
| 16 | |
|---|
| 17 | def system(cmd): |
|---|
| 18 | sys.stderr.flush() |
|---|
| 19 | if noact: |
|---|
| 20 | print '--------------------------not executing:---------------------------' |
|---|
| 21 | print cmd |
|---|
| 22 | return |
|---|
| 23 | elif verbose: |
|---|
| 24 | print '--------------------------executing:---------------------------' |
|---|
| 25 | print cmd |
|---|
| 26 | sys.stdout.flush() |
|---|
| 27 | |
|---|
| 28 | status = os.system(cmd) |
|---|
| 29 | if status: |
|---|
| 30 | raise SystemError, "Exit status %s\n%r" % (status, cmd) |
|---|
| 31 | return |
|---|
| 32 | |
|---|
| 33 | ### Read arguments and options ### |
|---|
| 34 | class Args(set): |
|---|
| 35 | def __contains__(self, x): |
|---|
| 36 | super_in = super(Args,self).__contains__ |
|---|
| 37 | if not x.startswith('-') and super_in('ALL'): |
|---|
| 38 | return True |
|---|
| 39 | else: |
|---|
| 40 | return super_in(x) |
|---|
| 41 | |
|---|
| 42 | args = Args(sys.argv[1:]) |
|---|
| 43 | verbose = '-v' in args |
|---|
| 44 | noact = '-n' in args |
|---|
| 45 | passwd = [ x for x in args if x.startswith('-p') ][0][2:] |
|---|
| 46 | if not passwd: |
|---|
| 47 | raise TypeError, 'password required in the form -pPASSWORD' |
|---|
| 48 | |
|---|
| 49 | ### Find Tracs to Operate On ### |
|---|
| 50 | children = set(os.listdir(old_trac_dir)) |
|---|
| 51 | tracspec = set([ o[:-1] for o in args if o.endswith(':') ]) |
|---|
| 52 | if tracspec: |
|---|
| 53 | children &= tracspec |
|---|
| 54 | tracs = [ d for d in [ os.path.join(parent_dir,f) for f in children ] |
|---|
| 55 | if os.path.isdir(d) ] |
|---|
| 56 | |
|---|
| 57 | ### Create Directory Structure ### |
|---|
| 58 | backup_dir = os.path.join(new_trac_dir, 'projects') |
|---|
| 59 | try: |
|---|
| 60 | os.makedirs(backup_dir) |
|---|
| 61 | except OSError: pass |
|---|
| 62 | |
|---|
| 63 | for d in 'conf', 'templates', 'plugins': |
|---|
| 64 | try: |
|---|
| 65 | os.makedirs(os.path.join(new_trac_dir,'global',d)) |
|---|
| 66 | except OSError: pass |
|---|
| 67 | |
|---|
| 68 | ### Upgrade and Backup ### |
|---|
| 69 | newtracs = [ os.path.join(backup_dir, os.path.split(t)[1]) for t in tracs ] |
|---|
| 70 | for t,n in zip(tracs, newtracs): |
|---|
| 71 | if 'upgrade' in args: |
|---|
| 72 | system('trac-admin "%s" upgrade' % t) |
|---|
| 73 | system('trac-admin "%s" wiki upgrade' % t) |
|---|
| 74 | |
|---|
| 75 | if 'backup' in args: |
|---|
| 76 | system('trac-admin "%s" hotcopy "%s"' % (t, n)) |
|---|
| 77 | |
|---|
| 78 | ### Adjust Paths to Point at Backups ### |
|---|
| 79 | tracs = newtracs |
|---|
| 80 | parent_dir = backup_dir |
|---|
| 81 | |
|---|
| 82 | ### Create PostGreSQL Database ### |
|---|
| 83 | if 'createdb' in args: |
|---|
| 84 | |
|---|
| 85 | # could be smarter about quoting passwd |
|---|
| 86 | system( |
|---|
| 87 | '''psql -d postgres -v "tracpass='%s'" -f create_multitrac_db.sql''' |
|---|
| 88 | % passwd |
|---|
| 89 | ); |
|---|
| 90 | |
|---|
| 91 | global_conf = os.path.join(new_trac_dir,'global','conf') |
|---|
| 92 | project_trac_ini = os.path.join(global_conf, 'project-trac.ini') |
|---|
| 93 | |
|---|
| 94 | config_specifics = { |
|---|
| 95 | 'project': ['descr', 'name', 'url'], |
|---|
| 96 | 'trac': ['base_url', 'database'], |
|---|
| 97 | } |
|---|
| 98 | |
|---|
| 99 | ### Create Master Trac Instance ### |
|---|
| 100 | if 'createmaster' in args: |
|---|
| 101 | master_env = os.path.join(parent_dir, 'master') |
|---|
| 102 | system('''trac-admin %(master_env)s initenv master''' |
|---|
| 103 | ''' "postgres://trac_instance_master:%(passwd)s''' |
|---|
| 104 | '''@localhost/%(dbname)s''' |
|---|
| 105 | '''?schema=trac_instance_master"''' |
|---|
| 106 | ''' svn "%(repospath)s"''' |
|---|
| 107 | % locals()) |
|---|
| 108 | |
|---|
| 109 | base_trac_ini = os.path.join(global_conf, 'base-trac.ini') |
|---|
| 110 | master_trac_ini = os.path.join(master_env,'conf','trac.ini') |
|---|
| 111 | |
|---|
| 112 | # Move the master project's trac.ini into the base file |
|---|
| 113 | if not noact: |
|---|
| 114 | if verbose: |
|---|
| 115 | print 'renaming %s => %s' % (master_trac_ini, base_trac_ini) |
|---|
| 116 | os.rename(master_trac_ini, base_trac_ini) |
|---|
| 117 | |
|---|
| 118 | # Make a new master config file, and move any project specifics from the |
|---|
| 119 | # base file back into it. |
|---|
| 120 | base_config = Configuration(base_trac_ini) |
|---|
| 121 | master_config = Configuration(master_trac_ini) |
|---|
| 122 | for section,names in config_specifics.items(): |
|---|
| 123 | for name in names: |
|---|
| 124 | master_config.set(section,name, base_config.get(section,name)) |
|---|
| 125 | base_config.remove(section, name) |
|---|
| 126 | |
|---|
| 127 | # inherit everything else from the base config file |
|---|
| 128 | master_config.set('inherit', 'file', base_trac_ini) |
|---|
| 129 | |
|---|
| 130 | # set up tracforge and account manager components |
|---|
| 131 | master_components = master_config['components'] |
|---|
| 132 | master_components.set('acct_mgr.*', 'enabled') |
|---|
| 133 | master_components.set('multiple_project_query_filter', 'enabled') |
|---|
| 134 | master_components.set('tracforge.*', 'enabled') |
|---|
| 135 | master_components.set('tracforge.linker.*', 'disabled') |
|---|
| 136 | master_components.set('tracforge.linker.auth.tracforgecookiemunger', 'enabled') |
|---|
| 137 | master_components.set('tracforge.subscriptions', 'disabled') |
|---|
| 138 | if not noact: |
|---|
| 139 | master_config.save() |
|---|
| 140 | |
|---|
| 141 | # We're not using tracforge subscriptions |
|---|
| 142 | base_config.set('components', 'tracforge.subscriptions', 'disabled') |
|---|
| 143 | |
|---|
| 144 | # Prepare global plugins and templates directories |
|---|
| 145 | base_config.set('inherit', 'templates_dir', os.path.join(new_trac_dir,'global','templates')) |
|---|
| 146 | base_config.set('inherit', 'plugins_dir', os.path.join(new_trac_dir,'global','plugins')) |
|---|
| 147 | if not noact: |
|---|
| 148 | base_config.save() |
|---|
| 149 | |
|---|
| 150 | # set up a config file for individual projects |
|---|
| 151 | project_config = Configuration(project_trac_ini) |
|---|
| 152 | project_config.set('inherit', 'file', base_trac_ini) |
|---|
| 153 | project_components = project_config['components'] |
|---|
| 154 | project_components.set('acct_mgr.*', 'disabled') |
|---|
| 155 | project_components.set('web.auth.loginmodule', 'disabled') |
|---|
| 156 | project_components.set('tracforge.admin.perm.*', 'enabled') |
|---|
| 157 | project_components.set('tracforge.linker.*', 'enabled') |
|---|
| 158 | project_components.set('tracforge.perms.*', 'enabled') |
|---|
| 159 | if not noact: |
|---|
| 160 | project_config.save() |
|---|
| 161 | |
|---|
| 162 | ### Create PostGreSQL users and translate the databases ### |
|---|
| 163 | for t in tracs: |
|---|
| 164 | project = os.path.split(t)[1] |
|---|
| 165 | |
|---|
| 166 | username = 'trac_instance_' + project |
|---|
| 167 | |
|---|
| 168 | if 'createuser' in args: |
|---|
| 169 | system('''psql -d %(dbname)s -U %(master_user)s<<EOF |
|---|
| 170 | CREATE ROLE %(username)s PASSWORD '%(passwd)s' |
|---|
| 171 | IN ROLE trac_instance LOGIN; |
|---|
| 172 | |
|---|
| 173 | -- Give the master trac instance all the same |
|---|
| 174 | -- permissions that this one has. |
|---|
| 175 | GRANT %(username)s TO trac_instance_master; |
|---|
| 176 | EOF |
|---|
| 177 | ''' % locals() |
|---|
| 178 | ) |
|---|
| 179 | |
|---|
| 180 | dbstring = ('postgres://' |
|---|
| 181 | '%(username)s:%(passwd)s' |
|---|
| 182 | '@localhost/%(dbname)s' |
|---|
| 183 | '?schema=%(username)s' |
|---|
| 184 | % locals()) |
|---|
| 185 | |
|---|
| 186 | if 'translate' in args: |
|---|
| 187 | system('python ./sqlite2pg -t "%(parent_dir)s"' |
|---|
| 188 | ' -p "%(dbstring)s"' |
|---|
| 189 | ' %(project)s' |
|---|
| 190 | % locals() |
|---|
| 191 | ); |
|---|
| 192 | |
|---|
| 193 | if 'newini' in args: |
|---|
| 194 | conf = os.path.join(parent_dir, project, 'conf') |
|---|
| 195 | new_ini = os.path.join(conf, 'trac.ini') |
|---|
| 196 | old_ini = os.path.join(conf, 'trac.bak.ini') |
|---|
| 197 | if not noact: |
|---|
| 198 | os.rename(new_ini, old_ini) |
|---|
| 199 | |
|---|
| 200 | old_config = Configuration(old_ini) |
|---|
| 201 | new_config = Configuration(new_ini) |
|---|
| 202 | |
|---|
| 203 | new_config.set('inherit','file',project_trac_ini) |
|---|
| 204 | for section,names in config_specifics.items(): |
|---|
| 205 | for name in names: |
|---|
| 206 | new_config.set(section, name, old_config.get(section,name)) |
|---|
| 207 | |
|---|
| 208 | if not noact: |
|---|
| 209 | new_config.save() |
|---|
| 210 | |
|---|
| 211 | if 'upgrade' in args: |
|---|
| 212 | system('trac-admin "%s" upgrade --no-backup' % t) |
|---|
| 213 | |
|---|
| 214 | print 'done.' |
|---|
| 215 | |
|---|