/
solo.py
310 lines (280 loc) · 12.3 KB
/
solo.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
#Copyright 2010-2012 Miquel Torres <tobami@googlemail.com>
#
#Licensed under the Apache License, Version 2.0 (the "License");
#you may not use this file except in compliance with the License.
#You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
#Unless required by applicable law or agreed to in writing, software
#distributed under the License is distributed on an "AS IS" BASIS,
#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#See the License for the specific language governing permissions and
#limitations under the License.
#
"""Chef Solo deployment"""
import os
import re
from fabric.api import *
from fabric import colors
from fabric.contrib.files import append, exists, upload_template
from fabric.utils import abort
from littlechef.lib import credentials
from littlechef.settings import node_work_path, cookbook_paths
from littlechef import LOGFILE as logging_path
# Path to local patch
BASEDIR = os.path.abspath(os.path.dirname(__file__).replace('\\', '/'))
def install(distro_type, distro, gems, version, stop_client):
"""Calls the appropriate installation function for the given distro"""
with credentials():
if distro_type == "debian":
if gems == "yes":
_gem_apt_install()
else:
_apt_install(distro, version, stop_client)
elif distro_type == "rpm":
if gems == "yes":
_gem_rpm_install()
else:
_rpm_install()
elif distro_type == "gentoo":
_emerge_install()
elif distro_type == "pacman":
_gem_pacman_install()
else:
abort('wrong distro type: {0}'.format(distro_type))
def configure(current_node=None):
"""Deploy chef-solo specific files"""
current_node = current_node or {}
with credentials():
# Ensure that the /tmp/chef-solo/cache directory exist
cache_dir = "{0}/cache".format(node_work_path)
if not exists(cache_dir):
with settings(hide('running', 'stdout'), warn_only=True):
output = sudo('mkdir -p {0}'.format(cache_dir))
if output.failed:
abort(
"Could not create {0} dir. Do you have sudo rights?".format(
node_work_path))
# Change ownership of /tmp/chef-solo/ so that we can rsync
with hide('running', 'stdout'):
with settings(warn_only=True):
output = sudo(
'chown -R {0} {1}'.format(env.user, node_work_path))
if output.failed:
abort(
"Could not modify {0} dir. Do you have sudo rights?".format(
node_work_path))
# Set up chef solo configuration
if not exists(logging_path):
sudo('mkdir -p {0}'.format(logging_path))
if not exists('/etc/chef'):
sudo('mkdir -p /etc/chef')
# Set parameters and upload solo.rb template
reversed_cookbook_paths = cookbook_paths[:]
reversed_cookbook_paths.reverse()
cookbook_paths_list = '[{0}]'.format(', '.join(
['"{0}/{1}"'.format(node_work_path, x) \
for x in reversed_cookbook_paths]))
data = {
'node_work_path': node_work_path,
'cookbook_paths_list': cookbook_paths_list,
'environment': current_node.get('chef_environment', '_default'),
'verbose': "true" if env.verbose else "false"
}
with hide('running', 'stdout'):
upload_template(os.path.join(BASEDIR, 'solo.rb'), '/etc/chef/',
context=data, use_sudo=True, backup=False, mode=0400)
with hide('stdout'):
sudo('chown root:root {0}'.format('/etc/chef/solo.rb'))
def check_distro():
"""Check that the given distro is supported and return the distro type"""
debian_distros = ['wheezy', 'squeeze', 'lenny']
ubuntu_distros = ['natty', 'maverick', 'lucid', 'karmic']
rpm_distros = ['centos', 'rhel', 'sl']
with credentials(
hide('warnings', 'running', 'stdout', 'stderr'), warn_only=True):
output = sudo('cat /etc/issue')
if 'Debian GNU/Linux 5.0' in output:
distro = "lenny"
distro_type = "debian"
elif 'Debian GNU/Linux 6.0' in output:
distro = "squeeze"
distro_type = "debian"
elif 'Debian GNU/Linux wheezy' in output:
distro = "wheezy"
distro_type = "debian"
elif 'Ubuntu' in output:
distro = sudo('lsb_release -cs')
distro_type = "debian"
elif 'CentOS' in output:
distro = "CentOS"
distro_type = "rpm"
elif 'Red Hat Enterprise Linux' in output:
distro = "Red Hat"
distro_type = "rpm"
elif 'Scientific Linux SL' in output:
distro = "Scientific Linux"
distro_type = "rpm"
elif 'This is \\n.\\O (\\s \\m \\r) \\t' in output:
distro = "Gentoo"
distro_type = "gentoo"
elif 'Arch Linux \\r (\\n) (\\l)' in output:
distro = "Arch Linux"
distro_type = "pacman"
else:
print "Currently supported distros are:"
print " Debian: " + ", ".join(debian_distros)
print " Ubuntu: " + ", ".join(ubuntu_distros)
print " RHEL: " + ", ".join(rpm_distros)
print " Gentoo"
print " Arch Linux"
abort("Unsupported distro '{0}'".format(output))
return distro_type, distro
def _gem_install():
"""Install Chef from gems"""
# Install RubyGems from Source
rubygems_version = "1.7.2"
run('wget http://production.cf.rubygems.org/rubygems/rubygems-{0}.tgz'
.format(rubygems_version))
run('tar zxf rubygems-{0}.tgz'.format(rubygems_version))
with cd('rubygems-{0}'.format(rubygems_version)):
sudo('ruby setup.rb --no-format-executable'.format(rubygems_version))
sudo('rm -rf rubygems-{0} rubygems-{0}.tgz'.format(rubygems_version))
sudo('gem install --no-rdoc --no-ri chef')
def _gem_apt_install():
"""Install Chef from gems for apt based distros"""
with hide('stdout', 'running'):
sudo('apt-get update')
prefix = "DEBIAN_FRONTEND=noninteractive"
packages = "ruby ruby-dev libopenssl-ruby irb build-essential wget"
packages += " ssl-cert rsync"
sudo('{0} apt-get --yes install {1}'.format(prefix, packages))
_gem_install()
def _gem_rpm_install():
"""Install Chef from gems for rpm based distros"""
_add_rpm_repos()
with show('running'):
sudo('yum -y install ruby ruby-shadow gcc gcc-c++ ruby-devel wget rsync')
_gem_install()
def _gem_pacman_install():
"""Install Chef from gems for pacman based distros"""
with hide('stdout', 'running'):
sudo('pacman -Syu --noconfirm')
with show('running'):
sudo('pacman -S --noconfirm ruby base-devel wget rsync')
sudo('gem install --no-rdoc --no-ri chef')
def _apt_install(distro, version, stop_client='yes'):
"""Install Chef for debian based distros"""
with settings(hide('stdout', 'running')):
with settings(hide('warnings'), warn_only=True):
wget_is_installed = sudo('which wget')
if wget_is_installed.failed:
# Install wget
print "Installing wget..."
# we may not be able to install wget without updating first
sudo('apt-get update')
output = sudo('apt-get --yes install wget')
if output.failed:
print(colors.red("Error while installing wget:"))
abort(output.lstrip("\\n"))
rsync_is_installed = sudo('which rsync')
if rsync_is_installed.failed:
# Install rsync
print "Installing rsync..."
# we may not be able to install rsync without updating first
sudo('apt-get update')
output = sudo('apt-get --yes install rsync')
if output.failed:
print(colors.red("Error while installing rsync:"))
abort(output.lstrip("\\n"))
# Add Opscode Debian repo
print("Setting up Opscode repository...")
if version == "0.9":
version = ""
else:
version = "-" + version
append('opscode.list',
'deb http://apt.opscode.com/ {0}{1} main'.format(distro, version),
use_sudo=True)
sudo('mv opscode.list /etc/apt/sources.list.d/')
# Add repository GPG key
gpg_key = "http://apt.opscode.com/packages@opscode.com.gpg.key"
sudo('wget -qO - {0} | sudo apt-key add -'.format(gpg_key))
# Load package list from new repository
with settings(hide('warnings'), warn_only=True):
output = sudo('apt-get update')
if output.failed:
print(colors.red("Error while executing apt-get install chef:"))
abort(output)
# Install Chef Solo
print("Installing Chef Solo")
# Ensure we don't get asked for the Chef Server
command = "echo chef chef/chef_server_url select ''"
command += " | debconf-set-selections"
sudo(command)
# Install package
with settings(hide('warnings'), warn_only=True):
output = sudo('apt-get --yes install ucf chef')
if output.failed:
print(colors.red("Error while executing 'apt-get install chef':"))
abort(output)
if stop_client == 'yes':
# We only want chef-solo, stop chef-client and remove it from init
sudo('update-rc.d -f chef-client remove')
with settings(hide('warnings'), warn_only=True):
# The logrotate entry will force restart of chef-client
sudo('rm /etc/logrotate.d/chef')
with settings(hide('warnings'), warn_only=True):
output = sudo('service chef-client stop')
if output.failed:
# Probably an older distro without the newer "service" command
sudo('/etc/init.d/chef-client stop')
def _add_rpm_repos():
"""Add RPM repositories for Chef
Opscode doesn't officially support an ELFF resporitory any longer:
http://wiki.opscode.com/display/chef/Installation+on+RHEL+and+CentOS+5+with+RPMs
Using http://rbel.frameos.org/
"""
version_string = sudo('cat /etc/redhat-release')
try:
rhel_version = re.findall("\d[\d.]*", version_string)[0].split('.')[0]
except IndexError:
print "Warning: could not correctly detect the Red Hat version"
print "Defaulting to 5 packages"
rhel_version = "5"
epel_release = "epel-release-5-4.noarch"
if rhel_version == "6":
epel_release = "epel-release-6-5.noarch"
with show('running'):
# Install the EPEL Yum Repository.
with settings(hide('warnings', 'running'), warn_only=True):
repo_url = "http://download.fedora.redhat.com"
repo_path = "/pub/epel/{0}/i386/".format(rhel_version)
repo_path += "{0}.rpm".format(epel_release)
output = sudo('rpm -Uvh {0}{1}'.format(repo_url, repo_path))
installed = "package {0} is already installed".format(epel_release)
if output.failed and installed not in output:
abort(output)
# Install the FrameOS RBEL Yum Repository.
with settings(hide('warnings', 'running'), warn_only=True):
repo_url = "http://rbel.co"
repo_path = "/rbel{0}".format(rhel_version)
output = sudo('rpm -Uvh {0}{1}'.format(repo_url, repo_path))
installed = "package rbel{0}-release-1.0-2.el{0}".format(
rhel_version)
installed += ".noarch is already installed"
if output.failed and installed not in output:
abort(output)
def _rpm_install():
"""Install Chef for rpm based distros"""
_add_rpm_repos()
with show('running'):
# Ensure we have an up-to-date ruby, as we need >=1.8.7
sudo('yum -y upgrade ruby*')
# Install Chef
sudo('yum -y install rubygem-chef')
def _emerge_install():
"""Install Chef for Gentoo"""
with show('running'):
sudo("USE='-test' ACCEPT_KEYWORDS='~amd64' emerge -u chef")