blob: 28275ba35b5392c2f93ff7bfb046bd04bae79524 [file] [log] [blame]
Matthew Treinish8e937d72012-12-14 11:11:41 -05001# vim: tabstop=4 shiftwidth=4 softtabstop=4
2
3# Copyright 2010 United States Government as represented by the
4# Administrator of the National Aeronautics and Space Administration.
5# All Rights Reserved.
6#
7# Copyright 2010 OpenStack, LLC
8#
9# Licensed under the Apache License, Version 2.0 (the "License"); you may
10# not use this file except in compliance with the License. You may obtain
11# a copy of the License at
12#
13# http://www.apache.org/licenses/LICENSE-2.0
14#
15# Unless required by applicable law or agreed to in writing, software
16# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
17# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
18# License for the specific language governing permissions and limitations
19# under the License.
20
21"""Installation script for Tempest's development virtualenv."""
22
23import optparse
24import os
25import subprocess
26import sys
27
28
29ROOT = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
30VENV = os.path.join(ROOT, '.venv')
31PIP_REQUIRES = os.path.join(ROOT, 'tools', 'pip-requires')
32TEST_REQUIRES = os.path.join(ROOT, 'tools', 'test-requires')
33PY_VERSION = "python%s.%s" % (sys.version_info[0], sys.version_info[1])
34
35
36def die(message, *args):
37 print >> sys.stderr, message % args
38 sys.exit(1)
39
40
41def check_python_version():
42 if sys.version_info < (2, 6):
43 die("Need Python Version >= 2.6")
44
45
46def run_command_with_code(cmd, redirect_output=True, check_exit_code=True):
47 """Runs a command in an out-of-process shell.
48
49 Returns the output of that command. Working directory is ROOT.
50 """
51 if redirect_output:
52 stdout = subprocess.PIPE
53 else:
54 stdout = None
55
56 proc = subprocess.Popen(cmd, cwd=ROOT, stdout=stdout)
57 output = proc.communicate()[0]
58 if check_exit_code and proc.returncode != 0:
59 die('Command "%s" failed.\n%s', ' '.join(cmd), output)
60 return (output, proc.returncode)
61
62
63def run_command(cmd, redirect_output=True, check_exit_code=True):
64 return run_command_with_code(cmd, redirect_output, check_exit_code)[0]
65
66
67class Distro(object):
68
69 def check_cmd(self, cmd):
70 return bool(run_command(['which', cmd], check_exit_code=False).strip())
71
72 def install_virtualenv(self):
73 if self.check_cmd('virtualenv'):
74 return
75
76 if self.check_cmd('easy_install'):
77 print 'Installing virtualenv via easy_install...',
78 if run_command(['easy_install', 'virtualenv']):
79 print 'Succeeded'
80 return
81 else:
82 print 'Failed'
83
84 die('ERROR: virtualenv not found.\n\nTempest development'
85 ' requires virtualenv, please install it using your'
86 ' favorite package management tool')
87
88 def post_process(self):
89 """Any distribution-specific post-processing gets done here.
90
91 In particular, this is useful for applying patches to code inside
92 the venv.
93 """
94 pass
95
96
97class Fedora(Distro):
Ryota Hashimoto67adbd92013-01-15 04:04:24 -070098 """This covers Fedora-based distributions.
Matthew Treinish8e937d72012-12-14 11:11:41 -050099
Ryota Hashimoto67adbd92013-01-15 04:04:24 -0700100 Includes: Fedora, RHEL, Scientific Linux"""
Matthew Treinish8e937d72012-12-14 11:11:41 -0500101
102 def check_pkg(self, pkg):
103 return run_command_with_code(['rpm', '-q', pkg],
104 check_exit_code=False)[1] == 0
105
106 def yum_install(self, pkg, **kwargs):
107 print "Attempting to install '%s' via yum" % pkg
108 run_command(['sudo', 'yum', 'install', '-y', pkg], **kwargs)
109
110 def apply_patch(self, originalfile, patchfile):
111 run_command(['patch', originalfile, patchfile])
112
113 def install_virtualenv(self):
114 if self.check_cmd('virtualenv'):
115 return
116
117 if not self.check_pkg('python-virtualenv'):
118 self.yum_install('python-virtualenv', check_exit_code=False)
119
120 super(Fedora, self).install_virtualenv()
121
122 def post_process(self):
123 """Workaround for a bug in eventlet.
124
125 This currently affects RHEL6.1, but the fix can safely be
126 applied to all RHEL and Fedora distributions.
127
128 This can be removed when the fix is applied upstream.
129
130 Nova: https://bugs.launchpad.net/nova/+bug/884915
131 Upstream: https://bitbucket.org/which_linden/eventlet/issue/89
132 """
133
134 # Install "patch" program if it's not there
135 if not self.check_pkg('patch'):
136 self.yum_install('patch')
137
138 # Apply the eventlet patch
139 self.apply_patch(os.path.join(VENV, 'lib', PY_VERSION, 'site-packages',
140 'eventlet/green/subprocess.py'),
141 'contrib/redhat-eventlet.patch')
142
143
Ryota Hashimoto67adbd92013-01-15 04:04:24 -0700144class CentOS(Fedora):
145 """This covers CentOS."""
146
147 def post_process(self):
148 if not self.check_pkg('openssl-devel'):
149 self.yum.install('openssl-devel', check_exit_code=False)
150
151
Matthew Treinish8e937d72012-12-14 11:11:41 -0500152def get_distro():
Pavel Sedlák67309bb2013-01-17 18:40:03 +0100153 if os.path.exists('/etc/redhat-release'):
154 with open('/etc/redhat-release') as rh_release:
155 if 'CentOS' in rh_release.read():
156 return CentOS()
157 return Fedora()
158
159 if os.path.exists('/etc/fedora-release'):
160 return Fedora()
161
162 return Distro()
Matthew Treinish8e937d72012-12-14 11:11:41 -0500163
164
165def check_dependencies():
166 get_distro().install_virtualenv()
167
168
169def create_virtualenv(venv=VENV, no_site_packages=True):
170 """Creates the virtual environment and installs PIP.
171
172 Creates the virtual environment and installs PIP only into the
173 virtual environment.
174 """
175 print 'Creating venv...',
176 if no_site_packages:
177 run_command(['virtualenv', '-q', '--no-site-packages', VENV])
178 else:
179 run_command(['virtualenv', '-q', VENV])
180 print 'done.'
181 print 'Installing pip in virtualenv...',
182 if not run_command(['tools/with_venv.sh', 'easy_install',
183 'pip>1.0']).strip():
184 die("Failed to install pip.")
185 print 'done.'
186
187
188def pip_install(*args):
189 run_command(['tools/with_venv.sh',
190 'pip', 'install', '--upgrade'] + list(args),
191 redirect_output=False)
192
193
194def install_dependencies(venv=VENV):
195 print 'Installing dependencies with pip (this can take a while)...'
196
197 # First things first, make sure our venv has the latest pip and distribute.
198 # NOTE: we keep pip at version 1.1 since the most recent version causes
199 # the .venv creation to fail. See:
200 # https://bugs.launchpad.net/nova/+bug/1047120
201 pip_install('pip==1.1')
202 pip_install('distribute')
203
204 # Install greenlet by hand - just listing it in the requires file does not
205 # get it in stalled in the right order
206 pip_install('greenlet')
207
208 pip_install('-r', PIP_REQUIRES)
209 pip_install('-r', TEST_REQUIRES)
210
211 # Install tempest into the virtual_env. No more path munging!
212 run_command([os.path.join(venv, 'bin/python'), 'setup.py', 'develop'])
213
214
215def post_process():
216 get_distro().post_process()
217
218
219def print_help():
220 help = """
221 Tempest development environment setup is complete.
222
223 Tempest development uses virtualenv to track and manage Python dependencies
224 while in development and testing.
225
226 To activate the Tempest virtualenv for the extent of your current shell
227 session you can run:
228
229 $ source .venv/bin/activate
230
231 Or, if you prefer, you can run commands in the virtualenv on a case by case
232 basis by running:
233
234 $ tools/with_venv.sh <your command>
235
236 Also, make test will automatically use the virtualenv.
237 """
238 print help
239
240
241def parse_args():
242 """Parses command-line arguments."""
243 parser = optparse.OptionParser()
244 parser.add_option("-n", "--no-site-packages", dest="no_site_packages",
245 default=False, action="store_true",
246 help="Do not inherit packages from global Python"
247 " install")
248 return parser.parse_args()
249
250
251def main(argv):
252 (options, args) = parse_args()
253 check_python_version()
254 check_dependencies()
255 create_virtualenv(no_site_packages=options.no_site_packages)
256 install_dependencies()
257 post_process()
258 print_help()
259
260if __name__ == '__main__':
261 main(sys.argv)