blob: 42a44e8cd266b2b7b6a116c6c44362060c0f933c [file] [log] [blame]
Matthew Treinish51dfee72013-01-28 15:50:29 -05001# vim: tabstop=4 shiftwidth=4 softtabstop=4
2
Dirk Mueller74af42c2013-06-23 20:50:22 +02003# Copyright 2013 OpenStack Foundation
Matthew Treinish51dfee72013-01-28 15:50:29 -05004# 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
Dirk Mueller74af42c2013-06-23 20:50:22 +020021Since this script is used to bootstrap a virtualenv from the system's Python
22environment, it should be kept strictly compatible with Python 2.6.
23
Matthew Treinish51dfee72013-01-28 15:50:29 -050024Synced in from openstack-common
25"""
26
Dirk Mueller74af42c2013-06-23 20:50:22 +020027from __future__ import print_function
28
29import optparse
Matthew Treinish51dfee72013-01-28 15:50:29 -050030import os
31import subprocess
32import sys
33
Matthew Treinish7682cde2013-02-06 16:34:40 -050034
Matthew Treinish51dfee72013-01-28 15:50:29 -050035class InstallVenv(object):
36
37 def __init__(self, root, venv, pip_requires, test_requires, py_version,
38 project):
39 self.root = root
40 self.venv = venv
41 self.pip_requires = pip_requires
42 self.test_requires = test_requires
43 self.py_version = py_version
44 self.project = project
45
46 def die(self, message, *args):
Dirk Mueller74af42c2013-06-23 20:50:22 +020047 print(message % args, file=sys.stderr)
Matthew Treinish51dfee72013-01-28 15:50:29 -050048 sys.exit(1)
49
50 def check_python_version(self):
51 if sys.version_info < (2, 6):
52 self.die("Need Python Version >= 2.6")
53
54 def run_command_with_code(self, cmd, redirect_output=True,
55 check_exit_code=True):
56 """Runs a command in an out-of-process shell.
57
Joe Gordon2b0591d2013-02-14 23:18:39 +000058 Returns the output of that command. Working directory is self.root.
Matthew Treinish51dfee72013-01-28 15:50:29 -050059 """
60 if redirect_output:
61 stdout = subprocess.PIPE
62 else:
63 stdout = None
64
65 proc = subprocess.Popen(cmd, cwd=self.root, stdout=stdout)
66 output = proc.communicate()[0]
67 if check_exit_code and proc.returncode != 0:
68 self.die('Command "%s" failed.\n%s', ' '.join(cmd), output)
69 return (output, proc.returncode)
70
71 def run_command(self, cmd, redirect_output=True, check_exit_code=True):
72 return self.run_command_with_code(cmd, redirect_output,
73 check_exit_code)[0]
74
75 def get_distro(self):
76 if (os.path.exists('/etc/fedora-release') or
77 os.path.exists('/etc/redhat-release')):
78 return Fedora(self.root, self.venv, self.pip_requires,
79 self.test_requires, self.py_version, self.project)
80 else:
81 return Distro(self.root, self.venv, self.pip_requires,
82 self.test_requires, self.py_version, self.project)
83
84 def check_dependencies(self):
85 self.get_distro().install_virtualenv()
86
87 def create_virtualenv(self, no_site_packages=True):
88 """Creates the virtual environment and installs PIP.
89
90 Creates the virtual environment and installs PIP only into the
91 virtual environment.
92 """
93 if not os.path.isdir(self.venv):
Dirk Mueller74af42c2013-06-23 20:50:22 +020094 print('Creating venv...', end=' ')
Matthew Treinish51dfee72013-01-28 15:50:29 -050095 if no_site_packages:
96 self.run_command(['virtualenv', '-q', '--no-site-packages',
97 self.venv])
98 else:
99 self.run_command(['virtualenv', '-q', self.venv])
Dirk Mueller74af42c2013-06-23 20:50:22 +0200100 print('done.')
101 print('Installing pip in venv...', end=' ')
Matthew Treinish51dfee72013-01-28 15:50:29 -0500102 if not self.run_command(['tools/with_venv.sh', 'easy_install',
103 'pip>1.0']).strip():
104 self.die("Failed to install pip.")
Dirk Mueller74af42c2013-06-23 20:50:22 +0200105 print('done.')
Matthew Treinish51dfee72013-01-28 15:50:29 -0500106 else:
Dirk Mueller74af42c2013-06-23 20:50:22 +0200107 print("venv already exists...")
Matthew Treinish51dfee72013-01-28 15:50:29 -0500108 pass
109
110 def pip_install(self, *args):
111 self.run_command(['tools/with_venv.sh',
112 'pip', 'install', '--upgrade'] + list(args),
113 redirect_output=False)
114
115 def install_dependencies(self):
Dirk Mueller74af42c2013-06-23 20:50:22 +0200116 print('Installing dependencies with pip (this can take a while)...')
Matthew Treinish51dfee72013-01-28 15:50:29 -0500117
118 # First things first, make sure our venv has the latest pip and
119 # distribute.
120 # NOTE: we keep pip at version 1.1 since the most recent version causes
121 # the .venv creation to fail. See:
122 # https://bugs.launchpad.net/nova/+bug/1047120
123 self.pip_install('pip==1.1')
124 self.pip_install('distribute')
125
126 # Install greenlet by hand - just listing it in the requires file does
127 # not
128 # get it installed in the right order
129 self.pip_install('greenlet')
130
131 self.pip_install('-r', self.pip_requires)
132 self.pip_install('-r', self.test_requires)
133
134 def post_process(self):
135 self.get_distro().post_process()
136
137 def parse_args(self, argv):
138 """Parses command-line arguments."""
Dirk Mueller74af42c2013-06-23 20:50:22 +0200139 parser = optparse.OptionParser()
140 parser.add_option('-n', '--no-site-packages',
141 action='store_true',
142 help="Do not inherit packages from global Python "
143 "install")
144 return parser.parse_args(argv[1:])[0]
Matthew Treinish51dfee72013-01-28 15:50:29 -0500145
146
147class Distro(InstallVenv):
148
149 def check_cmd(self, cmd):
150 return bool(self.run_command(['which', cmd],
151 check_exit_code=False).strip())
152
153 def install_virtualenv(self):
154 if self.check_cmd('virtualenv'):
155 return
156
157 if self.check_cmd('easy_install'):
Dirk Mueller74af42c2013-06-23 20:50:22 +0200158 print('Installing virtualenv via easy_install...', end=' ')
Matthew Treinish51dfee72013-01-28 15:50:29 -0500159 if self.run_command(['easy_install', 'virtualenv']):
Dirk Mueller74af42c2013-06-23 20:50:22 +0200160 print('Succeeded')
Matthew Treinish51dfee72013-01-28 15:50:29 -0500161 return
162 else:
Dirk Mueller74af42c2013-06-23 20:50:22 +0200163 print('Failed')
Matthew Treinish51dfee72013-01-28 15:50:29 -0500164
165 self.die('ERROR: virtualenv not found.\n\n%s development'
166 ' requires virtualenv, please install it using your'
167 ' favorite package management tool' % self.project)
168
169 def post_process(self):
170 """Any distribution-specific post-processing gets done here.
171
172 In particular, this is useful for applying patches to code inside
173 the venv.
174 """
175 pass
176
177
178class Fedora(Distro):
179 """This covers all Fedora-based distributions.
180
181 Includes: Fedora, RHEL, CentOS, Scientific Linux
182 """
183
184 def check_pkg(self, pkg):
185 return self.run_command_with_code(['rpm', '-q', pkg],
186 check_exit_code=False)[1] == 0
187
Matthew Treinish51dfee72013-01-28 15:50:29 -0500188 def apply_patch(self, originalfile, patchfile):
Dirk Mueller74af42c2013-06-23 20:50:22 +0200189 self.run_command(['patch', '-N', originalfile, patchfile],
190 check_exit_code=False)
Matthew Treinish51dfee72013-01-28 15:50:29 -0500191
192 def install_virtualenv(self):
193 if self.check_cmd('virtualenv'):
194 return
195
196 if not self.check_pkg('python-virtualenv'):
Dirk Mueller74af42c2013-06-23 20:50:22 +0200197 self.die("Please install 'python-virtualenv'.")
Matthew Treinish51dfee72013-01-28 15:50:29 -0500198
199 super(Fedora, self).install_virtualenv()
200
201 def post_process(self):
202 """Workaround for a bug in eventlet.
203
204 This currently affects RHEL6.1, but the fix can safely be
205 applied to all RHEL and Fedora distributions.
206
207 This can be removed when the fix is applied upstream.
208
209 Nova: https://bugs.launchpad.net/nova/+bug/884915
Dirk Mueller74af42c2013-06-23 20:50:22 +0200210 Upstream: https://bitbucket.org/eventlet/eventlet/issue/89
211 RHEL: https://bugzilla.redhat.com/958868
Matthew Treinish51dfee72013-01-28 15:50:29 -0500212 """
213
214 # Install "patch" program if it's not there
215 if not self.check_pkg('patch'):
Dirk Mueller74af42c2013-06-23 20:50:22 +0200216 self.die("Please install 'patch'.")
Matthew Treinish51dfee72013-01-28 15:50:29 -0500217
218 # Apply the eventlet patch
219 self.apply_patch(os.path.join(self.venv, 'lib', self.py_version,
220 'site-packages',
221 'eventlet/green/subprocess.py'),
222 'contrib/redhat-eventlet.patch')