blob: 3dbcafe290446e43d8b5873f8b6e94ac370fac7e [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
24import os
25import subprocess
26import sys
27
28from tempest.openstack.common import cfg
29
30
31class InstallVenv(object):
32
33 def __init__(self, root, venv, pip_requires, test_requires, py_version,
34 project):
35 self.root = root
36 self.venv = venv
37 self.pip_requires = pip_requires
38 self.test_requires = test_requires
39 self.py_version = py_version
40 self.project = project
41
42 def die(self, message, *args):
43 print >> sys.stderr, message % args
44 sys.exit(1)
45
46 def check_python_version(self):
47 if sys.version_info < (2, 6):
48 self.die("Need Python Version >= 2.6")
49
50 def run_command_with_code(self, cmd, redirect_output=True,
51 check_exit_code=True):
52 """Runs a command in an out-of-process shell.
53
54 Returns the output of that command. Working directory is ROOT.
55 """
56 if redirect_output:
57 stdout = subprocess.PIPE
58 else:
59 stdout = None
60
61 proc = subprocess.Popen(cmd, cwd=self.root, stdout=stdout)
62 output = proc.communicate()[0]
63 if check_exit_code and proc.returncode != 0:
64 self.die('Command "%s" failed.\n%s', ' '.join(cmd), output)
65 return (output, proc.returncode)
66
67 def run_command(self, cmd, redirect_output=True, check_exit_code=True):
68 return self.run_command_with_code(cmd, redirect_output,
69 check_exit_code)[0]
70
71 def get_distro(self):
72 if (os.path.exists('/etc/fedora-release') or
73 os.path.exists('/etc/redhat-release')):
74 return Fedora(self.root, self.venv, self.pip_requires,
75 self.test_requires, self.py_version, self.project)
76 else:
77 return Distro(self.root, self.venv, self.pip_requires,
78 self.test_requires, self.py_version, self.project)
79
80 def check_dependencies(self):
81 self.get_distro().install_virtualenv()
82
83 def create_virtualenv(self, no_site_packages=True):
84 """Creates the virtual environment and installs PIP.
85
86 Creates the virtual environment and installs PIP only into the
87 virtual environment.
88 """
89 if not os.path.isdir(self.venv):
90 print 'Creating venv...',
91 if no_site_packages:
92 self.run_command(['virtualenv', '-q', '--no-site-packages',
93 self.venv])
94 else:
95 self.run_command(['virtualenv', '-q', self.venv])
96 print 'done.'
97 print 'Installing pip in virtualenv...',
98 if not self.run_command(['tools/with_venv.sh', 'easy_install',
99 'pip>1.0']).strip():
100 self.die("Failed to install pip.")
101 print 'done.'
102 else:
103 print "venv already exists..."
104 pass
105
106 def pip_install(self, *args):
107 self.run_command(['tools/with_venv.sh',
108 'pip', 'install', '--upgrade'] + list(args),
109 redirect_output=False)
110
111 def install_dependencies(self):
112 print 'Installing dependencies with pip (this can take a while)...'
113
114 # First things first, make sure our venv has the latest pip and
115 # distribute.
116 # NOTE: we keep pip at version 1.1 since the most recent version causes
117 # the .venv creation to fail. See:
118 # https://bugs.launchpad.net/nova/+bug/1047120
119 self.pip_install('pip==1.1')
120 self.pip_install('distribute')
121
122 # Install greenlet by hand - just listing it in the requires file does
123 # not
124 # get it installed in the right order
125 self.pip_install('greenlet')
126
127 self.pip_install('-r', self.pip_requires)
128 self.pip_install('-r', self.test_requires)
129
130 def post_process(self):
131 self.get_distro().post_process()
132
133 def parse_args(self, argv):
134 """Parses command-line arguments."""
135 cli_opts = [
136 cfg.BoolOpt('no-site-packages',
137 default=False,
138 short='n',
139 help="Do not inherit packages from global Python"
140 "install"),
141 ]
142 CLI = cfg.ConfigOpts()
143 CLI.register_cli_opts(cli_opts)
144 CLI(argv[1:])
145 return CLI
146
147
148class Distro(InstallVenv):
149
150 def check_cmd(self, cmd):
151 return bool(self.run_command(['which', cmd],
152 check_exit_code=False).strip())
153
154 def install_virtualenv(self):
155 if self.check_cmd('virtualenv'):
156 return
157
158 if self.check_cmd('easy_install'):
159 print 'Installing virtualenv via easy_install...',
160 if self.run_command(['easy_install', 'virtualenv']):
161 print 'Succeeded'
162 return
163 else:
164 print 'Failed'
165
166 self.die('ERROR: virtualenv not found.\n\n%s development'
167 ' requires virtualenv, please install it using your'
168 ' favorite package management tool' % self.project)
169
170 def post_process(self):
171 """Any distribution-specific post-processing gets done here.
172
173 In particular, this is useful for applying patches to code inside
174 the venv.
175 """
176 pass
177
178
179class Fedora(Distro):
180 """This covers all Fedora-based distributions.
181
182 Includes: Fedora, RHEL, CentOS, Scientific Linux
183 """
184
185 def check_pkg(self, pkg):
186 return self.run_command_with_code(['rpm', '-q', pkg],
187 check_exit_code=False)[1] == 0
188
189 def yum_install(self, pkg, **kwargs):
190 print "Attempting to install '%s' via yum" % pkg
191 self.run_command(['sudo', 'yum', 'install', '-y', pkg], **kwargs)
192
193 def apply_patch(self, originalfile, patchfile):
194 self.run_command(['patch', originalfile, patchfile])
195
196 def install_virtualenv(self):
197 if self.check_cmd('virtualenv'):
198 return
199
200 if not self.check_pkg('python-virtualenv'):
201 self.yum_install('python-virtualenv', check_exit_code=False)
202
203 super(Fedora, self).install_virtualenv()
204
205 def post_process(self):
206 """Workaround for a bug in eventlet.
207
208 This currently affects RHEL6.1, but the fix can safely be
209 applied to all RHEL and Fedora distributions.
210
211 This can be removed when the fix is applied upstream.
212
213 Nova: https://bugs.launchpad.net/nova/+bug/884915
214 Upstream: https://bitbucket.org/which_linden/eventlet/issue/89
215 """
216
217 # Install "patch" program if it's not there
218 if not self.check_pkg('patch'):
219 self.yum_install('patch')
220
221 # Apply the eventlet patch
222 self.apply_patch(os.path.join(self.venv, 'lib', self.py_version,
223 'site-packages',
224 'eventlet/green/subprocess.py'),
225 'contrib/redhat-eventlet.patch')