1 # -*- coding: utf-8 -*-
4 ## This file is part of CDS Indico.
5 ## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 CERN.
7 ## CDS Indico is free software; you can redistribute it and/or
8 ## modify it under the terms of the GNU General Public License as
9 ## published by the Free Software Foundation; either version 2 of the
10 ## License, or (at your option) any later version.
12 ## CDS Indico is distributed in the hope that it will be useful, but
13 ## WITHOUT ANY WARRANTY; without even the implied warranty of
14 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 ## General Public License for more details.
17 ## You should have received a copy of the GNU General Public License
18 ## along with CDS Indico; if not, write to the Free Software Foundation, Inc.,
19 ## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
22 This file contains functions used by both 'python setup.py install' and after-easy_install
35 if sys
.platform
== 'linux2':
41 globals()['INDICO_INSTALL'] = False
43 LOCALDATABASEDIR
= '/opt/indico/db'
45 # Egg directory + etc/indico.conf
46 PWD_INDICO_CONF
= os
.path
.abspath(os
.path
.join(
47 os
.path
.split(os
.path
.dirname(MaKaC
.__file
__))[0],'etc', 'indico.conf'
50 def setIndicoInstallMode(newmode
):
52 Sets indico install mode.
53 This function and getIndicoInstallMode are used by some __init__.py inside
54 MaKaC to load or not some modules. At installation time those modules are not
55 available. That's why we need to skip them. They are imported there only as
59 INDICO_INSTALL
= newmode
62 def getIndicoInstallMode():
67 def createDirs(directories
):
68 '''Creates directories that are not automatically created by setup.install or easy_install'''
69 for d
in ['log', 'tmp', 'cache', 'archive']:
70 if not os
.path
.exists(directories
[d
]):
71 os
.makedirs(directories
[d
])
73 def upgrade_indico_conf(existing_conf
, new_conf
, mixinValues
={}):
75 Copies new_conf values to existing_conf file preserving existing_conf's values
77 If mixinValues is given its items will be preserved above existing_conf's values and new_conf's values
80 # We retrieve values from newest indico.conf
82 new_values
= locals().copy()
83 # We retrieve values from existing indico.conf
84 execfile(existing_conf
)
85 existing_values
= locals().copy()
87 new_values
.update(existing_values
)
88 new_values
.update(mixinValues
)
91 # We have to preserve options not currently present in the bundled indico.conf because they
92 # may belong to a plugin. This functionality can lead to forget adding new options to
93 # Configuration.py so be careful my friend.
95 # We remove vars defined here that aren't options
96 for k
in ('new_values', 'new_conf', 'existing_conf', 'mixinValues'):
99 result_values
= new_values
101 # We update a current copy of indico.conf with the new values
102 new_contents
= open(new_conf
).read()
105 if new_values
[k
].__class
__ == str:
106 regexp
= re
.compile('^(%s[ ]*=[ ]*[\'"]{1})([^\'"]*)([\'"]{1})' % k
, re
.MULTILINE
)
107 if regexp
.search(new_contents
):
108 new_contents
= re
.sub(regexp
, "\\g<1>%s\\3" % result_values
[k
], new_contents
)
110 new_contents
= "%s\n%s = '%s'" % (new_contents
, k
, result_values
[k
])
111 elif new_values
[k
].__class
__ == int:
112 regexp
= re
.compile('^(%s[ ]*=[ ]*)([0-9]+)' % k
, re
.MULTILINE
)
113 if regexp
.search(new_contents
):
114 new_contents
= re
.sub(regexp
, "\\g<1>%s" % result_values
[k
], new_contents
)
116 new_contents
= "%s\n%s = %s" % (new_contents
, k
, str(result_values
[k
]))
118 elif new_values
[k
].__class
__ == bool:
119 regexp
= re
.compile('^(%s[ ]*=[ ]*)(True|False)' % k
, re
.MULTILINE
)
120 if regexp
.search(new_contents
):
121 new_contents
= re
.sub(regexp
, "\\g<1>%s" % result_values
[k
], new_contents
)
123 new_contents
= "%s\n%s = %s" % (new_contents
, k
, str(result_values
[k
]))
125 elif new_values
[k
].__class
__ == tuple:
126 regexp
= re
.compile('^(%s[ ]*=[ ]*)[\(]{1}([^\)]+)[\)]{1}' % k
, re
.MULTILINE
)
127 if regexp
.search(new_contents
):
128 new_contents
= re
.sub(regexp
, "\\g<1>%s" % str(result_values
[k
]), new_contents
)
130 new_contents
= "%s\n%s = %s" % (new_contents
, k
, str(result_values
[k
]))
132 elif new_values
[k
].__class
__ == dict:
133 regexp
= re
.compile('^(%s[ ]*=[ ]*)[\{](.+)[\}$]' % k
, re
.MULTILINE
)
134 if regexp
.search(new_contents
):
135 new_contents
= re
.sub(regexp
, "\\g<1>%s" % str(result_values
[k
]), new_contents
)
137 new_contents
= "%s\n%s = %s" % (new_contents
, k
, str(result_values
[k
]))
139 elif new_values
[k
].__class
__ == list:
140 regexp
= re
.compile('^(%s[ ]*=[ ]*)[\[]{1}([^\]]+)[\]]{1}' % k
, re
.MULTILINE
)
141 if regexp
.search(new_contents
):
142 new_contents
= re
.sub(regexp
, "\\g<1>%s" % str(result_values
[k
]), new_contents
)
144 new_contents
= "%s\n%s = %s" % (new_contents
, k
, str(result_values
[k
]))
146 raise Exception('Invalid config value "%s = %s"' % (k
, new_values
[k
]))
148 # We write unknown options to the end of the file, they may not be just outdated options but plugins'
150 open(existing_conf
, 'w').write(new_contents
)
153 def modifyOnDiskIndicoConfOption(indico_conf
, optionName
, optionValue
):
154 upgrade_indico_conf(indico_conf
, indico_conf
, {optionName
: optionValue
})
157 def updateIndicoConfPathInsideMaKaCConfig(indico_conf_path
, makacconfigpy_path
):
158 '''Modifies the location of indico.conf referenced inside makacconfigpy_path to
159 point to indico_conf_path'''
160 fdata
= open(makacconfigpy_path
).read()
161 fdata
= re
.sub('indico_conf[ ]*=[ ]*[\'"]{1}([^\'"]*)[\'"]{1}', "indico_conf = \"%s\"" % indico_conf_path
, fdata
)
162 open(makacconfigpy_path
, 'w').write(fdata
)
164 def _updateMaKaCEggCache(file, path
):
165 fdata
= open(file).read()
166 fdata
= re
.sub('\/opt\/indico\/tmp', path
, fdata
)
167 open(file, 'w').write(fdata
)
170 def compileAllLanguages(cmd
):
171 '''Generates .mo files from .po files'''
174 pkg_resources
.require('babel')
175 except pkg_resources
.DistributionNotFound
:
177 Babel not found! Babel is needed for internationalization if you're building Indico from source. Please install it and re-run this program.
178 i.e. try 'easy_install babel'"""
181 # call commands directly
182 cc
= cmd
.distribution
.get_command_obj('compile_catalog')
184 gjs
= cmd
.distribution
.get_command_obj('compile_catalog_js')
188 def copyTreeSilently(source
, target
):
189 '''Copies source tree to target tree overwriting existing files'''
190 source
= os
.path
.normpath(source
)
191 target
= os
.path
.normpath(target
)
192 for root
, dirs
, files
in os
.walk(source
, topdown
=False):
194 fullpath
= os
.path
.normpath(os
.path
.join(root
, name
))
195 dstfile
= os
.path
.normpath(fullpath
.replace(source
, target
))
196 targetdir
= os
.path
.dirname(dstfile
)
197 if not os
.path
.exists(targetdir
):
198 os
.makedirs(targetdir
)
200 shutil
.copy(fullpath
, dstfile
)
206 '''Packs and minifies javascript files'''
209 pkg_resources
.require('JSTools')
210 except pkg_resources
.DistributionNotFound
:
212 JSTools not found! JSTools is needed for JavaScript compression, if you're building Indico from source. Please install it.
213 i.e. try 'easy_install jstools'"""
216 jsbuildPath
= 'jsbuild'
218 os
.system('%s -o ../../indico/htdocs/js/jquery/pack jquery.cfg' % jsbuildPath
)
219 os
.system('%s -o ../../indico/htdocs/js/indico/pack indico.cfg' % jsbuildPath
)
220 os
.system('%s -o ../../indico/htdocs/js/presentation/pack presentation.cfg' % jsbuildPath
)
221 os
.system('%s -o ../../indico/MaKaC/plugins/InstantMessaging/htdocs/js instantmessaging.cfg ' % jsbuildPath
)
222 os
.system('%s -o ../../indico/ext/livesync/htdocs/js livesync.cfg' % jsbuildPath
)
226 def _checkModPythonIsInstalled():
230 def _guessApacheUidGid():
232 finalUser
, finalGroup
= None, None
234 for username
in ['apache', 'www-data', 'www']:
236 finalUser
= pwd
.getpwnam(username
)
241 for groupname
in ['apache', 'www-data', 'www']:
243 finalGroup
= grp
.getgrnam(groupname
)
248 return finalUser
, finalGroup
250 def _findApacheUserGroup(uid
, gid
):
251 # if we are on linux we need to give proper permissions to the results directory
252 if sys
.platform
== "linux2":
255 # Check to see if uid/gid were provided through commandline
258 accessuser
= pwd
.getpwuid(int(uid
)).pw_name
259 accessgroup
= grp
.getgrgid(int(gid
)).gr_name
262 print 'uid/gid pair (%s/%s) provided through command line is false' % (uid
, gid
)
264 print "uid/gid not provided. Trying to guess them... ",
265 uid
, gid
= _guessApacheUidGid()
267 print "found %s(%s) %s(%s)" % (uid
.pw_name
, uid
.pw_uid
,
268 gid
.gr_name
, gid
.gr_gid
)
269 accessuser
= uid
.pw_name
270 accessgroup
= gid
.gr_name
274 valid_credentials
= False
275 while not valid_credentials
:
276 accessuser
= raw_input("\nPlease enter the user that will be running the Apache server: ")
277 accessgroup
= raw_input("\nPlease enter the group that will be running the Apache server: ")
279 pwd
.getpwnam(accessuser
)
280 grp
.getgrnam(accessgroup
)
281 valid_credentials
= True
283 print "\nERROR: Invalid user/group pair (%s/%s)" % (accessuser
, accessgroup
)
285 return accessuser
, accessgroup
287 return "apache", "apache"
290 def _checkDirPermissions(directories
, dbInstalledBySetupPy
=False, accessuser
=None, accessgroup
=None):
291 '''Makes sure that directories which need write access from Apache have
292 the correct permissions
294 - dbInstalledBySetupPy if True, means that the dbdir has been created by the setup
295 process and needs to be checked.
297 - uid and gid: if they are valid user_ids and group_ids they will be used to chown
298 the directories instead of the indico.conf ones.
301 print "\nWe need to 'sudo' in order to set the permissions of some directories..."
303 dirs2check
= list(directories
[x
] for x
in ['htdocs', 'log', 'tmp', 'cache', 'archive'] if directories
.has_key(x
))
304 if dbInstalledBySetupPy
:
305 dirs2check
.append(dbInstalledBySetupPy
)
307 for dir in dirs2check
:
308 print commands
.getoutput("if test $(which sudo); then CMD=\"sudo\"; fi; $CMD chown -R %s:%s %s" % (accessuser
, accessgroup
, dir))
311 def _existingConfiguredEgg():
312 '''Returns true if an existing EGG has been detected.'''
314 # remove '.' and './indico'
315 path
= copy
.copy(sys
.path
)
318 env
= pkg_resources
.Environment(search_path
=path
)
319 env
.scan(search_path
=path
)
321 # search for all indico dists
322 indico_dists
= env
['indico']
324 for dist
in indico_dists
:
325 eggPath
= dist
.location
327 print "* EGG found at %s..." % eggPath
,
328 fdata
= open(os
.path
.join(eggPath
,'MaKaC','common','MaKaCConfig.py'), 'r').read()
330 m
= re
.search('^\s*indico_conf\s*=\s*[\'"]{1}([^\'"]*)[\'"]{1}', fdata
, re
.MULTILINE
)
331 if m
and m
.group(1) != '':
332 print '%s' % m
.group(1)
338 def _extractDirsFromConf(conf
):
340 values
= locals().copy()
342 return {'bin': values
['BinDir'],
343 'doc': values
['DocumentationDir'],
344 'etc': values
['ConfigurationDir'],
345 'htdocs': values
['HtdocsDir'],
346 'tmp': values
['UploadedFilesTempDir'],
347 'log': values
['LogDir'],
348 'cache': values
['XMLCacheDir'],
349 'archive': values
['ArchiveDir'],
350 'db': LOCALDATABASEDIR
}
352 def _replacePrefixInConf(filePath
, prefix
):
353 fdata
= open(filePath
).read()
354 fdata
= re
.sub('\/opt\/indico', prefix
, fdata
)
355 open(filePath
, 'w').write(fdata
)
357 def _updateDbConfigFiles(dbDir
, logDir
, cfgDir
, tmpDir
, uid
):
358 filePath
= os
.path
.join(cfgDir
, 'zodb.conf')
359 fdata
= open(filePath
).read()
360 fdata
= re
.sub('\/opt\/indico\/db', dbDir
, fdata
)
361 fdata
= re
.sub('\/opt\/indico\/log', logDir
, fdata
)
362 open(filePath
, 'w').write(fdata
)
364 filePath
= os
.path
.join(cfgDir
, 'zdctl.conf')
365 fdata
= open(filePath
).read()
366 fdata
= re
.sub('\/opt\/indico\/db', dbDir
, fdata
)
367 fdata
= re
.sub('\/opt\/indico\/etc', cfgDir
, fdata
)
368 fdata
= re
.sub('\/opt\/indico\/tmp', tmpDir
, fdata
)
369 fdata
= re
.sub('(\s+user\s+)apache', '\g<1>%s' % uid
, fdata
)
370 open(filePath
, 'w').write(fdata
)
373 def indico_pre_install(defaultPrefix
, force_upgrade
=False, existingConfig
=None):
375 defaultPrefix is the default prefix dir where Indico will be installed
380 # Configuration specified in the command-line
382 existing
= existingConfig
383 # upgrade is mandatory
386 # Config not specified
387 # automatically find an EGG in the site-packages path
388 existing
= _existingConfiguredEgg()
390 # if an EGG was found but upgrade is not forced
391 if existing
and not force_upgrade
:
396 while opt
not in ('', 'e', 'E', 'u'):
398 An existing Indico configuration has been detected at:
402 At this point you can:
404 [u]pgrade the existing installation
406 [E]xit this installation process
408 What do you want to do [u/E]? ''' % existing
)
409 if opt
in ('', 'e', 'E'):
410 print "\nExiting installation..\n"
415 print "\nInvalid answer. Exiting installation..\n"
417 # if and EGG was found and upgrade is forced
418 elif existing
and force_upgrade
:
422 print 'Upgrading the existing Indico installation..'
423 return _extractDirsFromConf(existing
)
425 # then, in case no previous installation exists
426 return fresh_install(defaultPrefix
)
429 def fresh_install(defaultPrefix
):
432 print "No previous installation of Indico was found."
433 print "Please specify a directory prefix:"
435 # ask for a directory prefix
436 prefixDir
= raw_input('[%s]: ' % defaultPrefix
).strip()
439 prefixDir
= defaultPrefix
441 configDir
= os
.path
.join(prefixDir
, 'etc')
443 # create the directory that will contain the configuration files
444 if not os
.path
.exists(configDir
):
445 os
.makedirs(configDir
)
447 indicoconfpath
= os
.path
.join(configDir
, 'indico.conf')
450 You now need to configure Indico, by editing indico.conf or letting us do it for you.
451 At this point you can:
453 [c]opy the default values in etc/indico.conf.sample to a new etc/indico.conf
454 and continue the installation
456 [A]bort the installation in order to inspect etc/indico.conf.sample
457 and / or to make your own etc/indico.conf
459 What do you want to do [c/a]? ''')
460 if opt
in ('c', 'C'):
461 shutil
.copy(PWD_INDICO_CONF
+ '.sample', indicoconfpath
)
462 _replacePrefixInConf(indicoconfpath
, prefixDir
)
463 elif opt
in ('', 'a', 'A'):
464 print "\nExiting installation..\n"
467 print "\nInvalid answer. Exiting installation..\n"
470 activemakacconfig
= os
.path
.join(os
.path
.dirname(os
.path
.abspath(MaKaC
.__file
__)), 'common', 'MaKaCConfig.py')
471 updateIndicoConfPathInsideMaKaCConfig(indicoconfpath
, activemakacconfig
)
473 return dict((dirName
, os
.path
.join(prefixDir
, dirName
))
474 for dirName
in ['bin','doc','etc','htdocs','tmp','log','cache','db','archive'])
477 def indico_post_install(targetDirs
, sourceDirs
, makacconfig_base_dir
, package_dir
, force_no_db
= False, uid
=None, gid
=None, dbDir
=LOCALDATABASEDIR
):
478 from MaKaC
.common
.Configuration
import Config
480 if 'db' in targetDirs
:
481 # we don't want that the db directory be created
482 dbDir
= targetDirs
['db']
485 print "Creating directories for resources... ",
486 # Create the directories where the resources will be installed
487 createDirs(targetDirs
)
490 # target configuration file (may exist or not)
491 newConf
= os
.path
.join(targetDirs
['etc'],'indico.conf')
492 # source configuration file (package)
493 sourceConf
= os
.path
.join(sourceDirs
['etc'], 'indico.conf.sample')
495 # if there is a source config
496 if os
.path
.exists(sourceConf
):
497 if not os
.path
.exists(newConf
):
498 # just copy if there is no config yet
499 shutil
.copy(sourceConf
, newConf
)
501 print "Upgrading indico.conf...",
502 # upgrade the existing one
503 upgrade_indico_conf(newConf
, sourceConf
)
506 # change MaKaCConfig.py to include the config
507 updateIndicoConfPathInsideMaKaCConfig(newConf
,
508 os
.path
.join(makacconfig_base_dir
, 'MaKaCConfig.py'))
510 # copy the db config files
511 for f
in [xx
for xx
in ('%s/zdctl.conf' % targetDirs
['etc'],
512 '%s/zodb.conf' % targetDirs
['etc'],
513 '%s/logging.conf' %targetDirs
['etc']) if not os
.path
.exists(xx
)]:
514 shutil
.copy('%s.sample' % f
, f
)
516 # Shall we create a DB?
517 dbInstalledBySetupPy
= False
521 print 'Skipping database detection'
523 if os
.path
.exists(dbDir
):
525 print 'Successfully found a database directory at %s' % dbDir
528 while opt
not in ('Y', 'y', 'n', ''):
529 opt
= raw_input('''\nWe cannot find a configured database at %s.
531 Do you want to create a new database now [Y/n]? ''' % dbDir
)
532 if opt
in ('Y', 'y', ''):
533 dbInstalledBySetupPy
= True
536 dbpath
= raw_input('''\nWhere do you want to install the database [%s]? ''' % dbDir
)
537 if dbpath
.strip() == '':
545 print 'Unable to create database at %s, please make sure that you have permissions to create that directory' % dbpath
550 #we delete an existing vars.js.tpl.tmp
551 tmp_dir
= targetDirs
['tmp']
553 varsJsTplTmpPath
= os
.path
.join(tmp_dir
, 'vars.js.tpl.tmp')
554 if os
.path
.exists(varsJsTplTmpPath
):
555 print 'Old vars.js.tpl.tmp found at: %s. Removing' % varsJsTplTmpPath
556 os
.remove(varsJsTplTmpPath
)
558 if dbInstalledBySetupPy
:
563 # find the apache user/group
564 user
, group
= _findApacheUserGroup(uid
, gid
)
567 modifyOnDiskIndicoConfOption('%s/indico.conf' % targetDirs
['etc'], 'ApacheUser', user
)
568 modifyOnDiskIndicoConfOption('%s/indico.conf' % targetDirs
['etc'], 'ApacheGroup', group
)
570 # set the directory for the egg cache
571 _updateMaKaCEggCache(os
.path
.join(package_dir
, 'MaKaC', '__init__.py'), targetDirs
['tmp'])
573 if not force_no_db
and dbpath
:
574 # change the db config files (paths + apache user/group)
575 _updateDbConfigFiles(dbpath
, targetDirs
['log'], targetDirs
['etc'], targetDirs
['tmp'], user
)
578 _checkDirPermissions(targetDirs
, dbInstalledBySetupPy
=dbParam
, accessuser
=user
, accessgroup
=group
)
579 # check that mod_python is installed
580 _checkModPythonIsInstalled()
585 Indico has been installed correctly.
587 indico.conf: %s/indico.conf
594 For information on how to configure Apache HTTPD, take a look at:
596 http://indico-software.org/wiki/Admin/Installation#a3.ConfiguringApache
599 Please do not forget to start the 'taskDaemon' in order to use alarms, creation
600 of off-line websites, reminders, etc. You can find it in './bin/taskDaemon.py'
603 """ % (targetDirs
['etc'], targetDirs
['bin'], targetDirs
['doc'], targetDirs
['etc'], targetDirs
['htdocs'], _databaseText(targetDirs
['etc']))
605 def _databaseText(cfgPrefix
):
606 return """If you are running ZODB on this host:
607 - Review %s/zodb.conf and %s/zdctl.conf to make sure everything is ok.
608 - To start the database run: zdaemon -C %s/zdctl.conf start
609 """ % (cfgPrefix
, cfgPrefix
, cfgPrefix
)