blob: 0999e2c29e18b0b11c2527b4b275746841f05c2c [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
Monty Taylor7a3c3792013-07-05 22:15:06 -040037 def __init__(self, root, venv, requirements,
38 test_requirements, py_version,
Matthew Treinish51dfee72013-01-28 15:50:29 -050039 project):
40 self.root = root
41 self.venv = venv
Monty Taylor7a3c3792013-07-05 22:15:06 -040042 self.requirements = requirements
43 self.test_requirements = test_requirements
Matthew Treinish51dfee72013-01-28 15:50:29 -050044 self.py_version = py_version
45 self.project = project
46
47 def die(self, message, *args):
Dirk Mueller74af42c2013-06-23 20:50:22 +020048 print(message % args, file=sys.stderr)
Matthew Treinish51dfee72013-01-28 15:50:29 -050049 sys.exit(1)
50
51 def check_python_version(self):
52 if sys.version_info < (2, 6):
53 self.die("Need Python Version >= 2.6")
54
55 def run_command_with_code(self, cmd, redirect_output=True,
56 check_exit_code=True):
57 """Runs a command in an out-of-process shell.
58
Joe Gordon2b0591d2013-02-14 23:18:39 +000059 Returns the output of that command. Working directory is self.root.
Matthew Treinish51dfee72013-01-28 15:50:29 -050060 """
61 if redirect_output:
62 stdout = subprocess.PIPE
63 else:
64 stdout = None
65
66 proc = subprocess.Popen(cmd, cwd=self.root, stdout=stdout)
67 output = proc.communicate()[0]
68 if check_exit_code and proc.returncode != 0:
69 self.die('Command "%s" failed.\n%s', ' '.join(cmd), output)
70 return (output, proc.returncode)
71
72 def run_command(self, cmd, redirect_output=True, check_exit_code=True):
73 return self.run_command_with_code(cmd, redirect_output,
74 check_exit_code)[0]
75
76 def get_distro(self):
77 if (os.path.exists('/etc/fedora-release') or
78 os.path.exists('/etc/redhat-release')):
Monty Taylor7a3c3792013-07-05 22:15:06 -040079 return Fedora(
80 self.root, self.venv, self.requirements,
81 self.test_requirements, self.py_version, self.project)
Matthew Treinish51dfee72013-01-28 15:50:29 -050082 else:
Monty Taylor7a3c3792013-07-05 22:15:06 -040083 return Distro(
84 self.root, self.venv, self.requirements,
85 self.test_requirements, self.py_version, self.project)
Matthew Treinish51dfee72013-01-28 15:50:29 -050086
87 def check_dependencies(self):
88 self.get_distro().install_virtualenv()
89
90 def create_virtualenv(self, no_site_packages=True):
91 """Creates the virtual environment and installs PIP.
92
93 Creates the virtual environment and installs PIP only into the
94 virtual environment.
95 """
96 if not os.path.isdir(self.venv):
Dirk Mueller74af42c2013-06-23 20:50:22 +020097 print('Creating venv...', end=' ')
Matthew Treinish51dfee72013-01-28 15:50:29 -050098 if no_site_packages:
99 self.run_command(['virtualenv', '-q', '--no-site-packages',
100 self.venv])
101 else:
102 self.run_command(['virtualenv', '-q', self.venv])
Dirk Mueller74af42c2013-06-23 20:50:22 +0200103 print('done.')
Matthew Treinish51dfee72013-01-28 15:50:29 -0500104 else:
Dirk Mueller74af42c2013-06-23 20:50:22 +0200105 print("venv already exists...")
Matthew Treinish51dfee72013-01-28 15:50:29 -0500106 pass
107
108 def pip_install(self, *args):
109 self.run_command(['tools/with_venv.sh',
110 'pip', 'install', '--upgrade'] + list(args),
111 redirect_output=False)
112
113 def install_dependencies(self):
Dirk Mueller74af42c2013-06-23 20:50:22 +0200114 print('Installing dependencies with pip (this can take a while)...')
Matthew Treinish51dfee72013-01-28 15:50:29 -0500115
116 # First things first, make sure our venv has the latest pip and
Matthew Treinishffa94d62013-09-11 18:09:17 +0000117 # setuptools and pbr
118 self.pip_install('pip>=1.4')
Monty Taylor7a3c3792013-07-05 22:15:06 -0400119 self.pip_install('setuptools')
Matthew Treinishffa94d62013-09-11 18:09:17 +0000120 self.pip_install('pbr')
Matthew Treinish51dfee72013-01-28 15:50:29 -0500121
Monty Taylor7a3c3792013-07-05 22:15:06 -0400122 self.pip_install('-r', self.requirements)
123 self.pip_install('-r', self.test_requirements)
Matthew Treinish51dfee72013-01-28 15:50:29 -0500124
125 def post_process(self):
126 self.get_distro().post_process()
127
128 def parse_args(self, argv):
129 """Parses command-line arguments."""
Dirk Mueller74af42c2013-06-23 20:50:22 +0200130 parser = optparse.OptionParser()
131 parser.add_option('-n', '--no-site-packages',
132 action='store_true',
133 help="Do not inherit packages from global Python "
134 "install")
135 return parser.parse_args(argv[1:])[0]
Matthew Treinish51dfee72013-01-28 15:50:29 -0500136
137
138class Distro(InstallVenv):
139
140 def check_cmd(self, cmd):
141 return bool(self.run_command(['which', cmd],
142 check_exit_code=False).strip())
143
144 def install_virtualenv(self):
145 if self.check_cmd('virtualenv'):
146 return
147
148 if self.check_cmd('easy_install'):
Dirk Mueller74af42c2013-06-23 20:50:22 +0200149 print('Installing virtualenv via easy_install...', end=' ')
Matthew Treinish51dfee72013-01-28 15:50:29 -0500150 if self.run_command(['easy_install', 'virtualenv']):
Dirk Mueller74af42c2013-06-23 20:50:22 +0200151 print('Succeeded')
Matthew Treinish51dfee72013-01-28 15:50:29 -0500152 return
153 else:
Dirk Mueller74af42c2013-06-23 20:50:22 +0200154 print('Failed')
Matthew Treinish51dfee72013-01-28 15:50:29 -0500155
156 self.die('ERROR: virtualenv not found.\n\n%s development'
157 ' requires virtualenv, please install it using your'
158 ' favorite package management tool' % self.project)
159
160 def post_process(self):
161 """Any distribution-specific post-processing gets done here.
162
163 In particular, this is useful for applying patches to code inside
164 the venv.
165 """
166 pass
167
168
169class Fedora(Distro):
170 """This covers all Fedora-based distributions.
171
172 Includes: Fedora, RHEL, CentOS, Scientific Linux
173 """
174
175 def check_pkg(self, pkg):
176 return self.run_command_with_code(['rpm', '-q', pkg],
177 check_exit_code=False)[1] == 0
178
Matthew Treinish51dfee72013-01-28 15:50:29 -0500179 def apply_patch(self, originalfile, patchfile):
Dirk Mueller74af42c2013-06-23 20:50:22 +0200180 self.run_command(['patch', '-N', originalfile, patchfile],
181 check_exit_code=False)
Matthew Treinish51dfee72013-01-28 15:50:29 -0500182
183 def install_virtualenv(self):
184 if self.check_cmd('virtualenv'):
185 return
186
187 if not self.check_pkg('python-virtualenv'):
Dirk Mueller74af42c2013-06-23 20:50:22 +0200188 self.die("Please install 'python-virtualenv'.")
Matthew Treinish51dfee72013-01-28 15:50:29 -0500189
190 super(Fedora, self).install_virtualenv()
191
192 def post_process(self):
193 """Workaround for a bug in eventlet.
194
195 This currently affects RHEL6.1, but the fix can safely be
196 applied to all RHEL and Fedora distributions.
197
198 This can be removed when the fix is applied upstream.
199
200 Nova: https://bugs.launchpad.net/nova/+bug/884915
Dirk Mueller74af42c2013-06-23 20:50:22 +0200201 Upstream: https://bitbucket.org/eventlet/eventlet/issue/89
202 RHEL: https://bugzilla.redhat.com/958868
Matthew Treinish51dfee72013-01-28 15:50:29 -0500203 """
204
Matthew Treinishffa94d62013-09-11 18:09:17 +0000205 if os.path.exists('contrib/redhat-eventlet.patch'):
206 # Install "patch" program if it's not there
207 if not self.check_pkg('patch'):
208 self.die("Please install 'patch'.")
Matthew Treinish51dfee72013-01-28 15:50:29 -0500209
Matthew Treinishffa94d62013-09-11 18:09:17 +0000210 # Apply the eventlet patch
211 self.apply_patch(os.path.join(self.venv, 'lib', self.py_version,
212 'site-packages',
213 'eventlet/green/subprocess.py'),
214 'contrib/redhat-eventlet.patch')