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

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

ndg-security-install.py: fixed a subtle bug. pkg_resources import must be made AFTER new packages have been installed otherwise it will report OLD version rather the new version number of the distribution.

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