source: TI12-security/trunk/python/ndg-security-install.py @ 3057

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/python/ndg-security-install.py@3057
Revision 3057, 13.2 KB checked in by pjkersha, 13 years ago (diff)

python/ndg-security-install.py: if -U and -a are set to upgrade all, ensure all ndg_security dependencies (ndg_security_server, ndg_security_common and ndg_security_client) are included explicitly otherwise they get left out.

  • Property svn:executable set to *
RevLine 
[2286]1#!/usr/bin/env python
2"""Install NDG Server package with M2Crypto build settings and to include
3Twisted
4
5NERC Data Grid Project
[2909]6"""
7__author__ = "P J Kershaw"
8__date__ = "15/03/07"
9__copyright__ = "(C) 2007 STFC & NERC"
10__license__ = \
11"""This software may be distributed under the terms of the Q Public
12License, version 1.0 or later."""
13__contact__ = "P.J.Kershaw@rl.ac.uk"
14__revision__ = '$Id$'
[2286]15
16
17import os, sys
[2948]18import pkg_resources # Locate ndg.security.server.conf dir
19import shutil # For creating config dir
[2286]20import urllib
21import optparse
22from ConfigParser import SafeConfigParser
[2298]23from subprocess import call
[2391]24from setuptools.command.easy_install import main
[3047]25
26import logging
27log = logging.getLogger(__name__)
28     
[2397]29class SecurityInstallError(Exception):
30    """Errors related to security installation"""
31   
[2391]32class SecurityInstall(object):
33    '''Wrapper class for NDG security installation
[2298]34   
[2391]35    A wrapper is required over and above easy_install as additional setup
36    steps are required to enable for example custom build settings for
37    M2Crypto
[2298]38   
[2391]39    @cvar dependencyLink: default location for dependencies
40    @type dependencyLink: string
[2298]41   
[2391]42    @cvar defaultTwistedURI: default location for Twisted download
[2855]43    @type param: string
44   
45    @cvar configDir: default location for configuration directory "conf"
46    @type configDir: string'''
47   
[2368]48    dependencyLink = "http://ndg.nerc.ac.uk/dist/" 
[2391]49    defaultTwistedURI = \
50'http://tmrc.mit.edu/mirror/twisted/Twisted/2.2/TwistedSumo-2006-02-12.tar.bz2'
[2948]51    configDir = os.path.join("/etc", "ndg", "security", "conf")
[2298]52   
[2391]53    def __call__(self):
54        self.main()
55       
56    def main(self):
57        '''Parse command line args and execute the installation'''
58       
59        parser = optparse.OptionParser()
60       
61        parser.add_option("-a",
62                          "--install-all",
63                          dest="installAll",
64                          action="store_true",
65                          default=False,
66                          help="Install client AND server packages.")
67       
68        parser.add_option("-c",
69                          "--install-client",
70                          dest="installClient",
71                          action="store_true",
72                          default=False,
73                          help="Install client package only.")
74       
75        parser.add_option("-s",
76                          "--install-server",
77                          dest="installServer",
78                          action="store_true",
79                          default=False,
80                          help="Install server package only.")
81       
[2685]82        parser.add_option("-u",
83                          "--install-unittests",
84                          dest="installUnitTests",
85                          action="store_true",
86                          default=False,
87                          help="Install unit test package only.")
88       
[2391]89        parser.add_option("-o",
90                          "--openssl-path",
91                          dest="opensslPath",
92                          default='/usr/local/ssl',
93                          help="Path to openssl for M2Crypto to link with")
94       
95        parser.add_option("-n",
96                          "--no-twisted",
97                          dest="noTwisted",
98                          action="store_true",
99                          default=False,
100                          help=\
101"""Skip Twisted install.  This option applies to the \"all\" and \"server\"
102package options only.  Twisted is not needed for the client.""")
103       
104        parser.add_option("-t",
105                          "--twisted-uri",
106                          dest="twistedURI",
107                          default=self.__class__.defaultTwistedURI,
108                          help=\
109"""Provide an alternative location for Twisted download.  A .tar.bz type file
110is expected.  The default is \"%s\"""" % self.__class__.defaultTwistedURI)
111       
112        parser.add_option("-f",
113                          "--find-links",
114                          dest="dependencyLinks",
115                          default=self.__class__.dependencyLink,
116                          help=\
117                      'Set URLs to locate packages.  The default is "%s"' % \
118                      self.__class__.dependencyLink)
[2723]119       
120        parser.add_option("-U",
121                          "--upgrade",
122                          dest="upgrade",
123                          action="store_true",
124                          default=False,
125                          help=\
126          'force upgrade (search PyPI/dependency links for latest version)')
[2298]127   
[2948]128        configOpts = ("-C", "--config-dir")
129        parser.add_option(dest="configDir",
[2855]130                          default=self.__class__.configDir,
131                          help=\
[2948]132"""Specify a location for configuration files (server package only).  The
133default is \"%s\"""" % self.__class__.configDir,
134                          *configOpts)
[2298]135   
[2855]136        self.opt, args = parser.parse_args()
[2298]137   
[2391]138        # Sanity check
139        nInstallArgs = sum((self.opt.installClient, 
[2685]140                            self.opt.installServer,
141                            self.opt.installUnitTests, 
[2391]142                            self.opt.installAll))
143        if not nInstallArgs:
144            parser.error("At least one install option must be set")
145           
146        elif nInstallArgs > 1:
[2948]147            parser.error("Only one install option may be set")
148 
[2391]149        # Set M2Crypto build settings in a distutils config file
150        self.initM2CryptoDependencies() 
[2286]151   
[2391]152        # Installation based on flags set
[2723]153        if self.opt.upgrade:
154            args = ['-U']
155        else:
156            args = []
157     
158        # Add links for dependencies 
159        args += ['-f', self.opt.dependencyLinks]
[3047]160
[2391]161        if self.opt.installClient:
[3047]162            log.info("Installing ndg-security-client ...")
[2723]163            args += ["ndg_security_client"]
164            main(args)
[2391]165           
166        elif self.opt.installServer:
[3047]167            log.info("Installing ndg-security-server ...")
[2723]168            args += ["ndg_security_server"]
169            main(args)
[2391]170            self.installTwisted()
171           
[2948]172            # Config dir is part of server package only
173            self.createConfigDir()
174           
[2685]175        elif self.opt.installUnitTests:
[3047]176            log.info("Installing ndg-security-test ...")
[2723]177            args += ["ndg_security_test"]
178            main(args)
[2685]179            self.installTwisted()
180           
[2391]181        elif self.opt.installAll:
[3047]182            log.info("Installing all ...")
[3057]183            args += ["ndg_security", "ndg_security_test"]
184            if self.opt.upgrade:
185                # If upgrade is set dependencies for ndg_security aren't
186                # updated - they need to be added explicitly
187                args += ["ndg_security_common", 
188                         "ndg_security_server",
189                         "ndg_security_client"] 
190            main(args)
[2391]191            self.installTwisted()
192           
[3047]193            # Config dir is part of server package
194            self.createConfigDir()
[2391]195           
[3047]196           
[2391]197    def initM2CryptoDependencies(self):       
198        '''Set-up link path for openssl for M2Crypto build by creating a
199        distutils config file containing the include file and library file
200        paths'''
[3047]201        log.info("Initialising M2Crypto set-up ...")
[2368]202       
[2391]203        opensslInclPath = os.path.join(self.opt.opensslPath, 'include')
204        opensslLibPath = os.path.join(self.opt.opensslPath, 'lib')
[2298]205       
[2391]206        distutilsCfgFilePath = os.path.join(sys.prefix,
207                                            'lib',
208                                            'python%s' % sys.version[:3],
209                                            'distutils',
210                                            'distutils.cfg')
211        configParser = SafeConfigParser()
[2298]212       
[2391]213        if configParser.read(distutilsCfgFilePath):
214            # File already exists
215            if not configParser.has_section('build_ext'):
216                configParser.add_section('build_ext')
[2286]217           
[2391]218            if configParser.has_option('build_ext', 'include_dirs'):
219                existingInclDirs=configParser.get('build_ext', 'include_dirs')
220               
221                if opensslInclPath not in existingInclDirs.split():
222                    includeDirs = "%s %s" % (opensslInclPath,existingInclDirs)
223                    configParser.set('build_ext', 'include_dirs', includeDirs)
224            else:
225                configParser.set('build_ext', 'include_dirs', opensslInclPath)
226           
227            if configParser.has_option('build_ext', 'library_dirs'):
228                existingLibDirs = configParser.get('build_ext','library_dirs')
229               
230                if opensslLibPath not in existingLibDirs.split():
231                    libraryDirs = "%s %s" % (opensslLibPath, existingLibDirs)
232                    configParser.set('build_ext', 'library_dirs', libraryDirs)
233            else:
234                configParser.set('build_ext', 'library_dirs', opensslLibPath)
235                                 
[2286]236        else:
[2391]237            # No config file present - make one
238            configParser.add_section('build_ext')
[2286]239            configParser.set('build_ext', 'include_dirs', opensslInclPath)
[2391]240            configParser.set('build_ext', 'library_dirs', opensslLibPath)
[2286]241           
[2391]242        configParser.write(open(distutilsCfgFilePath, 'w'))
[2286]243   
244   
[2391]245    def installTwisted(self):
246        '''Download and install twisted manually as it is not egg compatible
247        '''
[2286]248       
[2391]249        if self.opt.noTwisted:
250            return
[3047]251       
252        log.info("Installing Twisted: %s ..." % self.opt.twistedURI)
253       
[2391]254        # Install Twisted sumo
255        try:
256            twistedTarBz = os.path.basename(self.opt.twistedURI)   
257            urllib.urlretrieve(self.opt.twistedURI, twistedTarBz)
258           
259        except IOError, (errMsg, e):
[2827]260            raise SecurityInstallError, \
261                'Error retrieving Twisted from "%s": %s' % \
262                                                (self.opt.twistedTarURI, e[1])
[2391]263        except Exception, e:
[2827]264            raise SecurityInstallError, \
265                'Error retrieving Twisted from "%s": %s' % \
266                                                (self.opt.twistedTarURI, e)
[2391]267
268        import tarfile
269       
[2402]270        twistedTar = tarfile.open(twistedTarBz, 'r:bz2')
[2391]271        for tarInfo in twistedTar:
272            twistedTar.extract(tarInfo)
273       
274        try:
275            twistedDir=os.path.splitext(os.path.splitext(twistedTarBz)[0])[0]
276        except Exception:
[2397]277            raise SecurityInstallError, \
[2391]278            'Error getting Twisted dir path from tar.bz file name: "%s"' % \
279                twistedTarBz
[2298]280       
[2391]281        os.chdir(twistedDir)
282        try: 
[2855]283            retCode = call([os.path.join(sys.prefix, 'bin', 'python'), 
284                            'setup.py', 
285                            'install'])
[2391]286        except OSError, e:
[2397]287            raise SecurityInstallError, \
288                        "Error calling setup install for Twisted: " + str(e)
[2391]289       
290        if retCode != 0:
[2397]291            raise SecurityInstallError, "Twisted setup install returned %d" %\
292                                        retCode
[2391]293       
294        os.chdir('..')
295
296
[2855]297    def createConfigDir(self):
298        """Copy configuration files for services from the server egg into
299        a config area.  The default is /etc/ndg/security/conf"""
300       
301        # Skip if not set
302        if not self.opt.configDir: 
303            return
304       
[3047]305        log.info('Copying configuration directory to "%s"'%self.opt.configDir)
306       
[2855]307        # Otherwise create - fix to rwx for owner and group only
[2948]308        confDirPath = os.path.dirname(self.opt.configDir)
309        try:
310            os.makedirs(confDirPath, mode=0770)
311        except OSError, e:
[3047]312            if e.errno != 17:
313                # errno=17 -> file already exists - it's OK if directory is
314                # already present
315                raise SecurityInstallError, \
316                   "Creating configuration directory: %s" % e
[2855]317       
[2948]318        # Locate conf directory in active egg
319        eggConfigDir = pkg_resources.resource_filename('ndg.security.server',
320                                                       'conf')
[3047]321        # Get distribution version info
322        serverDistro = pkg_resources.get_distribution('ndg-security-server')
[2948]323       
[3047]324        configDirVers = "%s.%s" % (self.opt.configDir, serverDistro.version)
[2855]325        # Copy over conf directory from egg
[2948]326        try:
[3047]327            shutil.copytree(eggConfigDir, configDirVers)
[2948]328        except OSError, e:
[3047]329            if e.errno != 17:
330                raise SecurityInstallError, \
331                    "Copying configuration directory: %s" % e
332
[3048]333        # Create a symbolic link to the conf.<version> dir - first check the
334        # link doesn't already exist
335        if os.path.islink(self.opt.configDir):
336            os.unlink(self.opt.configDir)
337           
[3047]338        try:
339            os.symlink(configDirVers, self.opt.configDir)
340        except OSError, e:
[2948]341            raise SecurityInstallError, \
[3047]342                "Making a symbolic link %s -> %s: %s" % (self.opt.configDir, 
343                                                         configDirVers, 
344                                                         e)
[2391]345if __name__ == "__main__":
346    SecurityInstall()()
347     
Note: See TracBrowser for help on using the repository browser.