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

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

Updated contact e-mail address

  • 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__ = "Philip.Kershaw@stfc.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        try:
242            configParser.write(open(distutilsCfgFilePath, 'w'))
243        except IOError:
244            # distutils dir may not be installed - try local dir as back-up
245            # option
246            distutilsCfgFilePath = os.path.join(os.environ['HOME'],
247                                                '.pydistutils.cfg')
248            configParser.write(open(distutilsCfgFilePath, 'w'))
249   
250   
251    def installTwisted(self):
252        '''Download and install twisted manually as it is not egg compatible
253        '''
254       
255        if self.opt.noTwisted:
256            return
257       
258        log.info("Installing Twisted: %s ..." % self.opt.twistedURI)
259       
260        # Install Twisted sumo
261        try:
262            twistedTarBz = os.path.basename(self.opt.twistedURI)   
263            urllib.urlretrieve(self.opt.twistedURI, twistedTarBz)
264           
265        except IOError, (errMsg, e):
266            raise SecurityInstallError, \
267                'Error retrieving Twisted from "%s": %s' % \
268                                                (self.opt.twistedTarURI, e[1])
269        except Exception, e:
270            raise SecurityInstallError, \
271                'Error retrieving Twisted from "%s": %s' % \
272                                                (self.opt.twistedTarURI, e)
273
274        import tarfile
275       
276        twistedTar = tarfile.open(twistedTarBz, 'r:bz2')
277        for tarInfo in twistedTar:
278            twistedTar.extract(tarInfo)
279       
280        try:
281            twistedDir=os.path.splitext(os.path.splitext(twistedTarBz)[0])[0]
282        except Exception:
283            raise SecurityInstallError, \
284            'Error getting Twisted dir path from tar.bz file name: "%s"' % \
285                twistedTarBz
286       
287        os.chdir(twistedDir)
288        try: 
289            retCode = call([os.path.join(sys.prefix, 'bin', 'python'), 
290                            'setup.py', 
291                            'install'])
292        except OSError, e:
293            raise SecurityInstallError, \
294                        "Error calling setup install for Twisted: " + str(e)
295       
296        if retCode != 0:
297            raise SecurityInstallError, "Twisted setup install returned %d" %\
298                                        retCode
299       
300        os.chdir('..')
301
302
303    def createConfigDir(self):
304        """Copy configuration files for services from the server egg into
305        a config area.  The default is /etc/ndg/security/conf"""
306       
307        # Skip if not set
308        if not self.opt.configDir: 
309            return
310       
311        log.info('Copying configuration directory to "%s"'%self.opt.configDir)
312       
313        # Otherwise create - fix to rwx for owner and group only
314        confDirPath = os.path.dirname(self.opt.configDir)
315        try:
316            os.makedirs(confDirPath, mode=0770)
317        except OSError, e:
318            if e.errno != 17:
319                # errno=17 -> file already exists - it's OK if directory is
320                # already present
321                raise SecurityInstallError, \
322                   "Creating configuration directory: %s" % e
323       
324        # Locate conf directory in active egg
325        #
326        # pkg_resources import MUST be go here otherwise in an update to
327        # existing eggs, the latest version will be reported as the one
328        # you're replacing instead of the new one
329        import pkg_resources
330
331        # Get distribution version info
332        serverDistro = pkg_resources.get_distribution('ndg-security-server')
333        eggConfigDir = os.path.join(serverDistro.location, 'ndg', 'security',
334                                    'server', 'conf')
335
336        configDirVers = "%s.%s" % (self.opt.configDir, serverDistro.version)
337        # Copy over conf directory from egg
338        try:
339            shutil.copytree(eggConfigDir, configDirVers)
340        except OSError, e:
341            if e.errno != 17:
342                raise SecurityInstallError, \
343                    "Copying configuration directory: %s" % e
344
345        # Create a symbolic link to the conf.<version> dir - first check the
346        # link doesn't already exist
347        if os.path.islink(self.opt.configDir):
348            os.unlink(self.opt.configDir)
349           
350        try:
351            os.symlink(configDirVers, self.opt.configDir)
352        except OSError, e:
353            raise SecurityInstallError, \
354                "Making a symbolic link %s -> %s: %s" % (self.opt.configDir, 
355                                                         configDirVers, 
356                                                         e)
357if __name__ == "__main__":
358    SecurityInstall()()
359     
Note: See TracBrowser for help on using the repository browser.