blob: 42ed32c70dbf5a0c1bba5c101514a7dacaf8f8c1 [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):
98 """This covers all Fedora-based distributions.
99
100 Includes: Fedora, RHEL, CentOS, Scientific Linux"""
101
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
144def get_distro():
145 if (os.path.exists('/etc/fedora-release') or
146 os.path.exists('/etc/redhat-release')):
147 return Fedora()
148 else:
149 return Distro()
150
151
152def check_dependencies():
153 get_distro().install_virtualenv()
154
155
156def create_virtualenv(venv=VENV, no_site_packages=True):
157 """Creates the virtual environment and installs PIP.
158
159 Creates the virtual environment and installs PIP only into the
160 virtual environment.
161 """
162 print 'Creating venv...',
163 if no_site_packages:
164 run_command(['virtualenv', '-q', '--no-site-packages', VENV])
165 else:
166 run_command(['virtualenv', '-q', VENV])
167 print 'done.'
168 print 'Installing pip in virtualenv...',
169 if not run_command(['tools/with_venv.sh', 'easy_install',
170 'pip>1.0']).strip():
171 die("Failed to install pip.")
172 print 'done.'
173
174
175def pip_install(*args):
176 run_command(['tools/with_venv.sh',
177 'pip', 'install', '--upgrade'] + list(args),
178 redirect_output=False)
179
180
181def install_dependencies(venv=VENV):
182 print 'Installing dependencies with pip (this can take a while)...'
183
184 # First things first, make sure our venv has the latest pip and distribute.
185 # NOTE: we keep pip at version 1.1 since the most recent version causes
186 # the .venv creation to fail. See:
187 # https://bugs.launchpad.net/nova/+bug/1047120
188 pip_install('pip==1.1')
189 pip_install('distribute')
190
191 # Install greenlet by hand - just listing it in the requires file does not
192 # get it in stalled in the right order
193 pip_install('greenlet')
194
195 pip_install('-r', PIP_REQUIRES)
196 pip_install('-r', TEST_REQUIRES)
197
198 # Install tempest into the virtual_env. No more path munging!
199 run_command([os.path.join(venv, 'bin/python'), 'setup.py', 'develop'])
200
201
202def post_process():
203 get_distro().post_process()
204
205
206def print_help():
207 help = """
208 Tempest development environment setup is complete.
209
210 Tempest development uses virtualenv to track and manage Python dependencies
211 while in development and testing.
212
213 To activate the Tempest virtualenv for the extent of your current shell
214 session you can run:
215
216 $ source .venv/bin/activate
217
218 Or, if you prefer, you can run commands in the virtualenv on a case by case
219 basis by running:
220
221 $ tools/with_venv.sh <your command>
222
223 Also, make test will automatically use the virtualenv.
224 """
225 print help
226
227
228def parse_args():
229 """Parses command-line arguments."""
230 parser = optparse.OptionParser()
231 parser.add_option("-n", "--no-site-packages", dest="no_site_packages",
232 default=False, action="store_true",
233 help="Do not inherit packages from global Python"
234 " install")
235 return parser.parse_args()
236
237
238def main(argv):
239 (options, args) = parse_args()
240 check_python_version()
241 check_dependencies()
242 create_virtualenv(no_site_packages=options.no_site_packages)
243 install_dependencies()
244 post_process()
245 print_help()
246
247if __name__ == '__main__':
248 main(sys.argv)