blob: fd9076f0eb6bd312b14d32528ea698198de4ff36 [file] [log] [blame]
Matthew Treinish51dfee72013-01-28 15:50:29 -05001# vim: tabstop=4 shiftwidth=4 softtabstop=4
2
3# Copyright 2013 OpenStack, LLC
4# Copyright 2013 IBM Corp.
5#
6# Licensed under the Apache License, Version 2.0 (the "License"); you may
7# not use this file except in compliance with the License. You may obtain
8# a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15# License for the specific language governing permissions and limitations
16# under the License.
17
18"""Provides methods needed by installation script for OpenStack development
19virtual environments.
20
21Synced in from openstack-common
22"""
23
Matthew Treinishf8240852013-02-25 18:01:39 -050024import argparse
Matthew Treinish51dfee72013-01-28 15:50:29 -050025import os
26import subprocess
27import sys
28
Matthew Treinish7682cde2013-02-06 16:34:40 -050029
Matthew Treinish51dfee72013-01-28 15:50:29 -050030class InstallVenv(object):
31
32 def __init__(self, root, venv, pip_requires, test_requires, py_version,
33 project):
34 self.root = root
35 self.venv = venv
36 self.pip_requires = pip_requires
37 self.test_requires = test_requires
38 self.py_version = py_version
39 self.project = project
40
41 def die(self, message, *args):
42 print >> sys.stderr, message % args
43 sys.exit(1)
44
45 def check_python_version(self):
46 if sys.version_info < (2, 6):
47 self.die("Need Python Version >= 2.6")
48
49 def run_command_with_code(self, cmd, redirect_output=True,
50 check_exit_code=True):
51 """Runs a command in an out-of-process shell.
52
Joe Gordon2b0591d2013-02-14 23:18:39 +000053 Returns the output of that command. Working directory is self.root.
Matthew Treinish51dfee72013-01-28 15:50:29 -050054 """
55 if redirect_output:
56 stdout = subprocess.PIPE
57 else:
58 stdout = None
59
60 proc = subprocess.Popen(cmd, cwd=self.root, stdout=stdout)
61 output = proc.communicate()[0]
62 if check_exit_code and proc.returncode != 0:
63 self.die('Command "%s" failed.\n%s', ' '.join(cmd), output)
64 return (output, proc.returncode)
65
66 def run_command(self, cmd, redirect_output=True, check_exit_code=True):
67 return self.run_command_with_code(cmd, redirect_output,
68 check_exit_code)[0]
69
70 def get_distro(self):
71 if (os.path.exists('/etc/fedora-release') or
72 os.path.exists('/etc/redhat-release')):
73 return Fedora(self.root, self.venv, self.pip_requires,
74 self.test_requires, self.py_version, self.project)
75 else:
76 return Distro(self.root, self.venv, self.pip_requires,
77 self.test_requires, self.py_version, self.project)
78
79 def check_dependencies(self):
80 self.get_distro().install_virtualenv()
81
82 def create_virtualenv(self, no_site_packages=True):
83 """Creates the virtual environment and installs PIP.
84
85 Creates the virtual environment and installs PIP only into the
86 virtual environment.
87 """
88 if not os.path.isdir(self.venv):
89 print 'Creating venv...',
90 if no_site_packages:
91 self.run_command(['virtualenv', '-q', '--no-site-packages',
92 self.venv])
93 else:
94 self.run_command(['virtualenv', '-q', self.venv])
95 print 'done.'
Joe Gordon2b0591d2013-02-14 23:18:39 +000096 print 'Installing pip in venv...',
Matthew Treinish51dfee72013-01-28 15:50:29 -050097 if not self.run_command(['tools/with_venv.sh', 'easy_install',
98 'pip>1.0']).strip():
99 self.die("Failed to install pip.")
100 print 'done.'
101 else:
102 print "venv already exists..."
103 pass
104
105 def pip_install(self, *args):
106 self.run_command(['tools/with_venv.sh',
107 'pip', 'install', '--upgrade'] + list(args),
108 redirect_output=False)
109
110 def install_dependencies(self):
111 print 'Installing dependencies with pip (this can take a while)...'
112
113 # First things first, make sure our venv has the latest pip and
114 # distribute.
115 # NOTE: we keep pip at version 1.1 since the most recent version causes
116 # the .venv creation to fail. See:
117 # https://bugs.launchpad.net/nova/+bug/1047120
118 self.pip_install('pip==1.1')
119 self.pip_install('distribute')
120
121 # Install greenlet by hand - just listing it in the requires file does
122 # not
123 # get it installed in the right order
124 self.pip_install('greenlet')
125
126 self.pip_install('-r', self.pip_requires)
127 self.pip_install('-r', self.test_requires)
128
129 def post_process(self):
130 self.get_distro().post_process()
131
132 def parse_args(self, argv):
133 """Parses command-line arguments."""
Matthew Treinishf8240852013-02-25 18:01:39 -0500134 parser = argparse.ArgumentParser()
135 parser.add_argument('-n', '--no-site-packages',
136 action='store_true',
137 help="Do not inherit packages from global Python "
138 "install")
139 return parser.parse_args(argv[1:])
Matthew Treinish51dfee72013-01-28 15:50:29 -0500140
141
142class Distro(InstallVenv):
143
144 def check_cmd(self, cmd):
145 return bool(self.run_command(['which', cmd],
146 check_exit_code=False).strip())
147
148 def install_virtualenv(self):
149 if self.check_cmd('virtualenv'):
150 return
151
152 if self.check_cmd('easy_install'):
153 print 'Installing virtualenv via easy_install...',
154 if self.run_command(['easy_install', 'virtualenv']):
155 print 'Succeeded'
156 return
157 else:
158 print 'Failed'
159
160 self.die('ERROR: virtualenv not found.\n\n%s development'
161 ' requires virtualenv, please install it using your'
162 ' favorite package management tool' % self.project)
163
164 def post_process(self):
165 """Any distribution-specific post-processing gets done here.
166
167 In particular, this is useful for applying patches to code inside
168 the venv.
169 """
170 pass
171
172
173class Fedora(Distro):
174 """This covers all Fedora-based distributions.
175
176 Includes: Fedora, RHEL, CentOS, Scientific Linux
177 """
178
179 def check_pkg(self, pkg):
180 return self.run_command_with_code(['rpm', '-q', pkg],
181 check_exit_code=False)[1] == 0
182
183 def yum_install(self, pkg, **kwargs):
184 print "Attempting to install '%s' via yum" % pkg
185 self.run_command(['sudo', 'yum', 'install', '-y', pkg], **kwargs)
186
187 def apply_patch(self, originalfile, patchfile):
188 self.run_command(['patch', originalfile, patchfile])
189
190 def install_virtualenv(self):
191 if self.check_cmd('virtualenv'):
192 return
193
194 if not self.check_pkg('python-virtualenv'):
195 self.yum_install('python-virtualenv', check_exit_code=False)
196
197 super(Fedora, self).install_virtualenv()
198
199 def post_process(self):
200 """Workaround for a bug in eventlet.
201
202 This currently affects RHEL6.1, but the fix can safely be
203 applied to all RHEL and Fedora distributions.
204
205 This can be removed when the fix is applied upstream.
206
207 Nova: https://bugs.launchpad.net/nova/+bug/884915
208 Upstream: https://bitbucket.org/which_linden/eventlet/issue/89
209 """
210
211 # Install "patch" program if it's not there
212 if not self.check_pkg('patch'):
213 self.yum_install('patch')
214
215 # Apply the eventlet patch
216 self.apply_patch(os.path.join(self.venv, 'lib', self.py_version,
217 'site-packages',
218 'eventlet/green/subprocess.py'),
219 'contrib/redhat-eventlet.patch')